@@ -3,7 +3,7 @@ import { addContextToFrame } from '@sentry/utils';
3
3
import { readFile } from 'fs' ;
4
4
import { LRUMap } from 'lru_map' ;
5
5
6
- const FILE_CONTENT_CACHE = new LRUMap < string , string | null > ( 100 ) ;
6
+ const FILE_CONTENT_CACHE = new LRUMap < string , string [ ] | null > ( 100 ) ;
7
7
const DEFAULT_LINES_OF_CONTEXT = 7 ;
8
8
9
9
// TODO: Replace with promisify when minimum supported node >= v8
@@ -64,7 +64,7 @@ export class ContextLines implements Integration {
64
64
public async addSourceContext ( event : Event ) : Promise < Event > {
65
65
if ( this . _contextLines > 0 && event . exception ?. values ) {
66
66
for ( const exception of event . exception . values ) {
67
- if ( exception . stacktrace ? .frames ) {
67
+ if ( exception . stacktrace && exception . stacktrace . frames ) {
68
68
await this . addSourceContextToFrames ( exception . stacktrace . frames ) ;
69
69
}
70
70
}
@@ -80,12 +80,11 @@ export class ContextLines implements Integration {
80
80
for ( const frame of frames ) {
81
81
// Only add context if we have a filename and it hasn't already been added
82
82
if ( frame . filename && frame . context_line === undefined ) {
83
- const sourceFile = await _readSourceFile ( frame . filename ) ;
83
+ const sourceFileLines = await _readSourceFile ( frame . filename ) ;
84
84
85
- if ( sourceFile ) {
85
+ if ( sourceFileLines ) {
86
86
try {
87
- const lines = sourceFile . split ( '\n' ) ;
88
- addContextToFrame ( lines , frame , contextLines ) ;
87
+ addContextToFrame ( sourceFileLines , frame , contextLines ) ;
89
88
} catch ( e ) {
90
89
// anomaly, being defensive in case
91
90
// unlikely to ever happen in practice but can definitely happen in theory
@@ -98,21 +97,30 @@ export class ContextLines implements Integration {
98
97
99
98
/**
100
99
* Reads file contents and caches them in a global LRU cache.
100
+ * If reading fails, mark the file as null in the cache so we don't try again.
101
101
*
102
102
* @param filename filepath to read content from.
103
103
*/
104
- async function _readSourceFile ( filename : string ) : Promise < string | null > {
104
+ async function _readSourceFile ( filename : string ) : Promise < string [ ] | null > {
105
105
const cachedFile = FILE_CONTENT_CACHE . get ( filename ) ;
106
- // We have a cache hit
106
+
107
+ // We have already attempted to read this file and failed, do not try again
108
+ if ( cachedFile === null ) {
109
+ return null ;
110
+ }
111
+
112
+ // We have a cache hit, return it
107
113
if ( cachedFile !== undefined ) {
108
114
return cachedFile ;
109
115
}
110
116
111
- let content : string | null = null ;
117
+ // File is neither in cache nor marked as failed, attempt to read it
118
+ let content : string [ ] | null = null ;
112
119
try {
113
- content = await readTextFileAsync ( filename ) ;
120
+ const rawFileContents = await readTextFileAsync ( filename ) ;
121
+ content = rawFileContents . split ( '\n' ) ;
114
122
} catch ( _ ) {
115
- //
123
+ // if we fail, we will mark the file as null in the cache and short circuit next time we try to read it
116
124
}
117
125
118
126
FILE_CONTENT_CACHE . set ( filename , content ) ;
0 commit comments