@@ -3,7 +3,7 @@ import { addContextToFrame } from '@sentry/utils';
33import { readFile } from 'fs' ;
44import { LRUMap } from 'lru_map' ;
55
6- const FILE_CONTENT_CACHE = new LRUMap < string , string | null > ( 100 ) ;
6+ const FILE_CONTENT_CACHE = new LRUMap < string , string [ ] | null > ( 100 ) ;
77const DEFAULT_LINES_OF_CONTEXT = 7 ;
88
99// TODO: Replace with promisify when minimum supported node >= v8
@@ -102,8 +102,8 @@ export class ContextLines implements Integration {
102102 // and attempt to add source context to frames.
103103 if ( this . _contextLines > 0 && event . exception ?. values ) {
104104 for ( const exception of event . exception . values ) {
105- if ( exception . stacktrace ? .frames ) {
106- this . addSourceContextToFrames ( exception . stacktrace . frames ) ;
105+ if ( exception . stacktrace && exception . stacktrace . frames ) {
106+ await this . addSourceContextToFrames ( exception . stacktrace . frames ) ;
107107 }
108108 }
109109 }
@@ -116,12 +116,11 @@ export class ContextLines implements Integration {
116116 for ( const frame of frames ) {
117117 // Only add context if we have a filename and it hasn't already been added
118118 if ( frame . filename && frame . context_line === undefined ) {
119- const sourceFile = FILE_CONTENT_CACHE . get ( frame . filename ) ;
119+ const sourceFileLines = FILE_CONTENT_CACHE . get ( frame . filename ) ;
120120
121- if ( sourceFile ) {
121+ if ( sourceFileLines ) {
122122 try {
123- const lines = sourceFile . split ( '\n' ) ;
124- addContextToFrame ( lines , frame , this . _contextLines ) ;
123+ addContextToFrame ( sourceFileLines , frame , this . _contextLines ) ;
125124 } catch ( e ) {
126125 // anomaly, being defensive in case
127126 // unlikely to ever happen in practice but can definitely happen in theory
@@ -134,25 +133,34 @@ export class ContextLines implements Integration {
134133
135134/**
136135 * Reads file contents and caches them in a global LRU cache.
136+ * If reading fails, mark the file as null in the cache so we don't try again.
137137 *
138138 * @param filename filepath to read content from.
139139 */
140- async function _readSourceFile ( filename : string ) : Promise < string | null > {
140+ async function _readSourceFile ( filename : string ) : Promise < string [ ] | null > {
141141 const cachedFile = FILE_CONTENT_CACHE . get ( filename ) ;
142- // We have a cache hit
142+
143+ // We have already attempted to read this file and failed, do not try again
144+ if ( cachedFile === null ) {
145+ return null ;
146+ }
147+
148+ // We have a cache hit, return it
143149 if ( cachedFile !== undefined ) {
144150 return cachedFile ;
145151 }
146152
147- let content : string | null = null ;
148-
149153 // Guard from throwing if readFile fails, this enables us to use Promise.all and
150154 // not have it short circuiting if one of the promises rejects + since context lines are added
151155 // on a best effort basis, we want to throw here anyways.
156+
157+ // If we made it to here, it means that our file is not cache nor marked as failed, so attempt to read it
158+ let content : string [ ] | null = null ;
152159 try {
153- content = await readTextFileAsync ( filename ) ;
160+ const rawFileContents = await readTextFileAsync ( filename ) ;
161+ content = rawFileContents . split ( '\n' ) ;
154162 } catch ( _ ) {
155- //
163+ // if we fail, we will mark the file as null in the cache and short circuit next time we try to read it
156164 }
157165
158166 FILE_CONTENT_CACHE . set ( filename , content ) ;
0 commit comments