@@ -2,9 +2,13 @@ import { SentryEvent, SentryException, StackFrame } from '@sentry/types';
22import { readFileAsync } from '@sentry/utils/fs' ;
33import { basename , dirname } from '@sentry/utils/path' ;
44import { snipLine } from '@sentry/utils/string' ;
5+ import * as Limiter from 'async-limiter' ;
56import * as stacktrace from 'stack-trace' ;
7+ import { NodeOptions } from './backend' ;
68
7- const LINES_OF_CONTEXT : number = 7 ;
9+ // tslint:disable-next-line:no-unsafe-any
10+ const fsLimiter = new Limiter ( { concurrency : 25 } ) ;
11+ const DEFAULT_LINES_OF_CONTEXT : number = 7 ;
812
913/**
1014 * Just an Error object with arbitrary attributes attached to it.
@@ -75,7 +79,7 @@ async function readSourceFiles(
7579 filenames . map ( async filename => {
7680 let content ;
7781 try {
78- content = await readFileAsync ( filename ) ;
82+ content = await readFileAsync ( filename , fsLimiter ) ;
7983 } catch ( _ ) {
8084 // unsure what to add here as the file is unreadable
8185 content = null ;
@@ -99,8 +103,12 @@ export async function extractStackFromError(error: Error): Promise<stacktrace.St
99103}
100104
101105/** JSDoc */
102- export async function parseStack ( stack : stacktrace . StackFrame [ ] ) : Promise < StackFrame [ ] > {
106+ export async function parseStack ( stack : stacktrace . StackFrame [ ] , options ?: NodeOptions ) : Promise < StackFrame [ ] > {
103107 const filesToRead : string [ ] = [ ] ;
108+
109+ const linesOfContext =
110+ options && options . frameContextLines !== undefined ? options . frameContextLines : DEFAULT_LINES_OF_CONTEXT ;
111+
104112 const frames : StackFrame [ ] = stack . map ( frame => {
105113 const parsedFrame : StackFrame = {
106114 colno : frame . getColumnNumber ( ) ,
@@ -126,16 +134,21 @@ export async function parseStack(stack: stacktrace.StackFrame[]): Promise<StackF
126134 if ( parsedFrame . filename ) {
127135 parsedFrame . module = getModule ( parsedFrame . filename ) ;
128136
129- if ( ! isInternal ) {
137+ if ( ! isInternal && linesOfContext > 0 ) {
130138 filesToRead . push ( parsedFrame . filename ) ;
131139 }
132140 }
133141
134142 return parsedFrame ;
135143 } ) ;
136144
145+ // We do an early return if we do not want to fetch context liens
146+ if ( linesOfContext <= 0 ) {
147+ return frames ;
148+ }
149+
137150 try {
138- return await addPrePostContext ( filesToRead , frames ) ;
151+ return await addPrePostContext ( filesToRead , frames , linesOfContext ) ;
139152 } catch ( _ ) {
140153 // This happens in electron for example where we are not able to read files from asar.
141154 // So it's fine, we recover be just returning all frames without pre/post context.
@@ -149,21 +162,25 @@ export async function parseStack(stack: stacktrace.StackFrame[]): Promise<StackF
149162 * @param filesToRead string[] of filepaths
150163 * @param frames StackFrame[] containg all frames
151164 */
152- async function addPrePostContext ( filesToRead : string [ ] , frames : StackFrame [ ] ) : Promise < StackFrame [ ] > {
165+ async function addPrePostContext (
166+ filesToRead : string [ ] ,
167+ frames : StackFrame [ ] ,
168+ linesOfContext : number ,
169+ ) : Promise < StackFrame [ ] > {
153170 const sourceFiles = await readSourceFiles ( filesToRead ) ;
154171 return frames . map ( frame => {
155172 if ( frame . filename && sourceFiles [ frame . filename ] ) {
156173 try {
157174 const lines = sourceFiles [ frame . filename ] . split ( '\n' ) ;
158175
159176 frame . pre_context = lines
160- . slice ( Math . max ( 0 , ( frame . lineno || 0 ) - ( LINES_OF_CONTEXT + 1 ) ) , ( frame . lineno || 0 ) - 1 )
177+ . slice ( Math . max ( 0 , ( frame . lineno || 0 ) - ( linesOfContext + 1 ) ) , ( frame . lineno || 0 ) - 1 )
161178 . map ( ( line : string ) => snipLine ( line , 0 ) ) ;
162179
163180 frame . context_line = snipLine ( lines [ ( frame . lineno || 0 ) - 1 ] , frame . colno || 0 ) ;
164181
165182 frame . post_context = lines
166- . slice ( frame . lineno || 0 , ( frame . lineno || 0 ) + LINES_OF_CONTEXT )
183+ . slice ( frame . lineno || 0 , ( frame . lineno || 0 ) + linesOfContext )
167184 . map ( ( line : string ) => snipLine ( line , 0 ) ) ;
168185 } catch ( e ) {
169186 // anomaly, being defensive in case
@@ -175,10 +192,10 @@ async function addPrePostContext(filesToRead: string[], frames: StackFrame[]): P
175192}
176193
177194/** JSDoc */
178- export async function getExceptionFromError ( error : Error ) : Promise < SentryException > {
195+ export async function getExceptionFromError ( error : Error , options ?: NodeOptions ) : Promise < SentryException > {
179196 const name = error . name || error . constructor . name ;
180197 const stack = await extractStackFromError ( error ) ;
181- const frames = await parseStack ( stack ) ;
198+ const frames = await parseStack ( stack , options ) ;
182199
183200 return {
184201 stacktrace : {
@@ -190,9 +207,9 @@ export async function getExceptionFromError(error: Error): Promise<SentryExcepti
190207}
191208
192209/** JSDoc */
193- export async function parseError ( error : ExtendedError ) : Promise < SentryEvent > {
210+ export async function parseError ( error : ExtendedError , options ?: NodeOptions ) : Promise < SentryEvent > {
194211 const name = error . name || error . constructor . name ;
195- const exception = await getExceptionFromError ( error ) ;
212+ const exception = await getExceptionFromError ( error , options ) ;
196213 return {
197214 exception : {
198215 values : [ exception ] ,
0 commit comments