@@ -62,10 +62,46 @@ export class ContextLines implements Integration {
62
62
63
63
/** Processes an event and adds context lines */
64
64
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.
65
101
if ( this . _contextLines > 0 && event . exception ?. values ) {
66
102
for ( const exception of event . exception . values ) {
67
103
if ( exception . stacktrace ?. frames ) {
68
- await this . addSourceContextToFrames ( exception . stacktrace . frames ) ;
104
+ this . addSourceContextToFrames ( exception . stacktrace . frames ) ;
69
105
}
70
106
}
71
107
}
@@ -74,18 +110,16 @@ export class ContextLines implements Integration {
74
110
}
75
111
76
112
/** 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 {
80
114
for ( const frame of frames ) {
81
115
// Only add context if we have a filename and it hasn't already been added
82
116
if ( frame . filename && frame . context_line === undefined ) {
83
- const sourceFile = await _readSourceFile ( frame . filename ) ;
117
+ const sourceFile = FILE_CONTENT_CACHE . get ( frame . filename ) ;
84
118
85
119
if ( sourceFile ) {
86
120
try {
87
121
const lines = sourceFile . split ( '\n' ) ;
88
- addContextToFrame ( lines , frame , contextLines ) ;
122
+ addContextToFrame ( lines , frame , this . _contextLines ) ;
89
123
} catch ( e ) {
90
124
// anomaly, being defensive in case
91
125
// unlikely to ever happen in practice but can definitely happen in theory
0 commit comments