@@ -62,10 +62,46 @@ export class ContextLines implements Integration {
6262
6363 /** Processes an event and adds context lines */
6464 public async addSourceContext ( event : Event ) : Promise < Event > {
65+ // keep a lookup map of which files we've already enqueued to read,
66+ // so we don't enqueue the same file multiple times which would cause multiple i/o reads
67+ const enqueuedReadSourceFileTasks : Record < string , number > = { } ;
68+ const readSourceFileTasks : Promise < string | null > [ ] = [ ] ;
69+
70+ if ( this . _contextLines > 0 && event . exception ?. values ) {
71+ for ( const exception of event . exception . values ) {
72+ if ( ! exception . stacktrace ?. frames ) {
73+ continue ;
74+ }
75+
76+ // We want to iterate in reverse order as calling cache.get will bump the file in our LRU cache.
77+ // This ends up prioritizes source context for frames at the top of the stack instead of the bottom.
78+ for ( let i = exception . stacktrace . frames . length - 1 ; i >= 0 ; i -- ) {
79+ const frame = exception . stacktrace . frames [ i ] ;
80+ // Call cache.get to bump the file to the top of the cache and ensure we have not already
81+ // enqueued a read operation for this filename
82+ if (
83+ frame . filename &&
84+ ! enqueuedReadSourceFileTasks [ frame . filename ] &&
85+ ! FILE_CONTENT_CACHE . get ( frame . filename )
86+ ) {
87+ readSourceFileTasks . push ( _readSourceFile ( frame . filename ) ) ;
88+ enqueuedReadSourceFileTasks [ frame . filename ] = 1 ;
89+ }
90+ }
91+ }
92+ }
93+
94+ // check if files to read > 0, if so, await all of them to be read before adding source contexts
95+ if ( readSourceFileTasks . length > 0 ) {
96+ await Promise . all ( readSourceFileTasks ) ;
97+ }
98+
99+ // Perform the same loop as above, but this time we can assume all files are in the cache
100+ // and attempt to add source context to frames.
65101 if ( this . _contextLines > 0 && event . exception ?. values ) {
66102 for ( const exception of event . exception . values ) {
67103 if ( exception . stacktrace ?. frames ) {
68- await this . addSourceContextToFrames ( exception . stacktrace . frames ) ;
104+ this . addSourceContextToFrames ( exception . stacktrace . frames ) ;
69105 }
70106 }
71107 }
@@ -74,18 +110,16 @@ export class ContextLines implements Integration {
74110 }
75111
76112 /** Adds context lines to frames */
77- public async addSourceContextToFrames ( frames : StackFrame [ ] ) : Promise < void > {
78- const contextLines = this . _contextLines ;
79-
113+ public addSourceContextToFrames ( frames : StackFrame [ ] ) : void {
80114 for ( const frame of frames ) {
81115 // Only add context if we have a filename and it hasn't already been added
82116 if ( frame . filename && frame . context_line === undefined ) {
83- const sourceFile = await _readSourceFile ( frame . filename ) ;
117+ const sourceFile = FILE_CONTENT_CACHE . get ( frame . filename ) ;
84118
85119 if ( sourceFile ) {
86120 try {
87121 const lines = sourceFile . split ( '\n' ) ;
88- addContextToFrame ( lines , frame , contextLines ) ;
122+ addContextToFrame ( lines , frame , this . _contextLines ) ;
89123 } catch ( e ) {
90124 // anomaly, being defensive in case
91125 // unlikely to ever happen in practice but can definitely happen in theory
0 commit comments