@@ -43,8 +43,10 @@ class ApexLogParser {
4343 maxSizeTimestamp : number | null = null ;
4444 reasons : Set < string > = new Set < string > ( ) ;
4545 cpuUsed = 0 ;
46+ lastTimestamp = 0 ;
4647 discontinuity = false ;
4748 namespaces : string [ ] = [ ] ;
49+ namespacesUniq = new Set < string > ( ) ;
4850
4951 /**
5052 * Takes string input of a log and returns the ApexLog class, which represents a log tree
@@ -65,69 +67,71 @@ class ApexLogParser {
6567 }
6668
6769 private parseLine ( line : string , lastEntry : LogLine | null ) : LogLine | null {
68- const parts = line . split ( '|' ) ,
69- type = parts [ 1 ] || '' ;
70+ const parts = line . split ( '|' ) ;
7071
72+ const type = parts [ 1 ] ?? '' ;
7173 const metaCtor = getLogEventClass ( type as LogEventType ) ;
7274 if ( metaCtor ) {
7375 const entry = new metaCtor ( parts ) ;
7476 entry . logLine = line ;
7577 lastEntry ?. onAfter ?.( this , entry ) ;
76- entry . namespace &&
77- ! this . namespaces . includes ( entry . namespace ) &&
78+ if ( entry . namespace && ! this . namespacesUniq . has ( entry . namespace ) ) {
7879 this . namespaces . push ( entry . namespace ) ;
80+ this . namespacesUniq . add ( entry . namespace ) ;
81+ }
7982 return entry ;
8083 }
8184
82- if ( ( ! type || ! typePattern . test ( type ) ) && lastEntry && lastEntry . acceptsText ) {
85+ const hasType = type && typePattern . test ( type ) ;
86+ if ( ! hasType && lastEntry ?. acceptsText ) {
8387 // wrapped text from the previous entry?
84- lastEntry . text += `\n ${ line } ` ;
85- } else if ( type && typePattern . test ( type ) ) {
88+ lastEntry . text += '\n' + line ;
89+ } else if ( hasType ) {
8690 const message = `Unsupported log event name: ${ type } ` ;
8791 ! this . parsingErrors . includes ( message ) && this . parsingErrors . push ( message ) ;
92+ } else if ( lastEntry && line . startsWith ( '*** Skipped' ) ) {
93+ this . addLogIssue (
94+ lastEntry . timestamp ,
95+ 'Skipped-Lines' ,
96+ `${ line } . A section of the log has been skipped and the log has been truncated. Full details of this section of log can not be provided.` ,
97+ 'skip' ,
98+ ) ;
99+ } else if ( lastEntry && line . indexOf ( 'MAXIMUM DEBUG LOG SIZE REACHED' ) !== - 1 ) {
100+ this . addLogIssue (
101+ lastEntry . timestamp ,
102+ 'Max-Size-reached' ,
103+ 'The maximum log size has been reached. Part of the log has been truncated.' ,
104+ 'skip' ,
105+ ) ;
106+ this . maxSizeTimestamp = lastEntry . timestamp ;
107+ } else if ( ! hasType && settingsPattern . test ( line ) ) {
108+ // skip an unexpected settings line
88109 } else {
89- if ( lastEntry && line . startsWith ( '*** Skipped' ) ) {
90- this . addLogIssue (
91- lastEntry . timestamp ,
92- 'Skipped-Lines' ,
93- `${ line } . A section of the log has been skipped and the log has been truncated. Full details of this section of log can not be provided.` ,
94- 'skip' ,
95- ) ;
96- } else if ( lastEntry && line . indexOf ( 'MAXIMUM DEBUG LOG SIZE REACHED' ) >= 0 ) {
97- this . addLogIssue (
98- lastEntry . timestamp ,
99- 'Max-Size-reached' ,
100- 'The maximum log size has been reached. Part of the log has been truncated.' ,
101- 'skip' ,
102- ) ;
103- this . maxSizeTimestamp = lastEntry . timestamp ;
104- } else if ( settingsPattern . test ( line ) ) {
105- // skip an unexpected settings line
106- } else {
107- this . parsingErrors . push ( `Invalid log line: ${ line } ` ) ;
108- }
110+ this . parsingErrors . push ( `Invalid log line: ${ line } ` ) ;
109111 }
110112
111113 return null ;
112114 }
113115
114116 private * generateLogLines ( log : string ) : Generator < LogLine > {
115- const start = log . match ( / ^ .* E X E C U T I O N _ S T A R T E D .* $ / m) ?. index || 0 ;
117+ const start = log . match ( / ^ .* E X E C U T I O N _ S T A R T E D .* $ / m) ?. index ?? 0 ;
116118 if ( start > 0 ) {
117119 log = log . slice ( start ) ;
118120 }
119121
120122 const hascrlf = log . indexOf ( '\r\n' ) > - 1 ;
121123 let lastEntry = null ;
122- let eolIndex = log . indexOf ( '\n' ) ;
124+ let lfIndex = null ;
125+ let eolIndex = ( lfIndex = log . indexOf ( '\n' ) ) ;
123126 let startIndex = 0 ;
124127 let crlfIndex = - 1 ;
125128
126129 while ( eolIndex !== - 1 ) {
127130 if ( hascrlf && eolIndex > crlfIndex ) {
128131 crlfIndex = log . indexOf ( '\r' , eolIndex - 1 ) ;
132+ eolIndex = crlfIndex + 1 === eolIndex ? crlfIndex : lfIndex ;
129133 }
130- const line = log . slice ( startIndex , crlfIndex + 1 === eolIndex ? crlfIndex : eolIndex ) ;
134+ const line = log . slice ( startIndex , eolIndex ) ;
131135 if ( line ) {
132136 // ignore blank lines
133137 const entry = this . parseLine ( line , lastEntry ) ;
@@ -136,8 +140,8 @@ class ApexLogParser {
136140 yield entry ;
137141 }
138142 }
139- startIndex = eolIndex + 1 ;
140- eolIndex = log . indexOf ( '\n' , startIndex ) ;
143+ startIndex = lfIndex + 1 ;
144+ lfIndex = eolIndex = log . indexOf ( '\n' , startIndex ) ;
141145 }
142146
143147 // Parse the last line
@@ -175,44 +179,41 @@ class ApexLogParser {
175179 }
176180
177181 private parseTree ( currentLine : Method , lineIter : LineIterator , stack : Method [ ] ) {
178- let lastTimestamp = currentLine . timestamp ;
182+ this . lastTimestamp = currentLine . timestamp ;
179183 currentLine . namespace ||= 'default' ;
180184
181- const isEntry = currentLine . exitTypes . length > 0 ;
185+ const isEntry = currentLine . exitTypes . length ;
182186 if ( isEntry ) {
183187 const exitOnNextLine = currentLine . nextLineIsExit ;
184188 let nextLine ;
185189
186190 stack . push ( currentLine ) ;
187191
188192 while ( ( nextLine = lineIter . peek ( ) ) ) {
189- lastTimestamp = nextLine . timestamp ;
190193 // discontinuities are stack unwinding (caused by Exceptions)
191194 this . discontinuity ||= nextLine . discontinuity ; // start unwinding stack
192195
193- if (
194- exitOnNextLine &&
195- ( nextLine . nextLineIsExit || nextLine . isExit || nextLine . exitTypes . length > 0 )
196- ) {
197- currentLine . exitStamp = nextLine . timestamp ;
198- currentLine . onEnd ?.( nextLine , stack ) ;
199- break ;
200- }
201-
202196 // Exit Line has been found no more work needed
203197 if (
198+ ! exitOnNextLine &&
204199 ! nextLine . nextLineIsExit &&
205200 nextLine . isExit &&
201+ ! nextLine . exitTypes . length &&
206202 this . endMethod ( currentLine , nextLine , lineIter , stack )
207203 ) {
208204 // the method wants to see the exit line
209205 currentLine . onEnd ?.( nextLine , stack ) ;
210206 break ;
211- }
212-
213- if (
214- this . maxSizeTimestamp &&
207+ } else if (
208+ exitOnNextLine &&
209+ ( nextLine . nextLineIsExit || nextLine . isExit || nextLine . exitTypes . length > 0 )
210+ ) {
211+ currentLine . exitStamp = nextLine . timestamp ;
212+ currentLine . onEnd ?.( nextLine , stack ) ;
213+ break ;
214+ } else if (
215215 this . discontinuity &&
216+ this . maxSizeTimestamp &&
216217 nextLine . timestamp > this . maxSizeTimestamp
217218 ) {
218219 // The current line was truncated (we did not find the exit line before the end of log) and there was a discontinuity
@@ -222,6 +223,7 @@ class ApexLogParser {
222223
223224 nextLine . namespace ||= currentLine . namespace || 'default' ;
224225 lineIter . fetch ( ) ; // it's a child - consume the line
226+ this . lastTimestamp = nextLine . timestamp ;
225227
226228 if ( nextLine instanceof Method ) {
227229 this . parseTree ( nextLine , lineIter , stack ) ;
@@ -234,24 +236,24 @@ class ApexLogParser {
234236 // of the log without finding an exit line or the current line was truncated)
235237 if ( ! nextLine || currentLine . isTruncated ) {
236238 // truncated method - terminate at the end of the log
237- currentLine . exitStamp = lastTimestamp ;
239+ currentLine . exitStamp = this . lastTimestamp ;
238240
239241 // we found an entry event on its own e.g a `METHOD_ENTRY` without a `METHOD_EXIT` and got to the end of the log
240242 this . addLogIssue (
241- lastTimestamp ,
243+ this . lastTimestamp ,
242244 'Unexpected-End' ,
243245 'An entry event was found without a corresponding exit event e.g a `METHOD_ENTRY` event without a `METHOD_EXIT`' ,
244246 'unexpected' ,
245247 ) ;
246248
247249 if ( currentLine . isTruncated ) {
248250 this . updateLogIssue (
249- lastTimestamp ,
251+ this . lastTimestamp ,
250252 'Max-Size-reached' ,
251253 'The maximum log size has been reached. Part of the log has been truncated.' ,
252254 'skip' ,
253255 ) ;
254- this . maxSizeTimestamp = lastTimestamp ;
256+ this . maxSizeTimestamp = this . lastTimestamp ;
255257 }
256258 currentLine . isTruncated = true ;
257259 }
@@ -322,7 +324,7 @@ class ApexLogParser {
322324 } ) ;
323325 }
324326 }
325- currentNodes = result . get ( currentDepth ++ ) || [ ] ;
327+ currentNodes = result . get ( currentDepth ++ ) ?? [ ] ;
326328 len = currentNodes . length ;
327329 }
328330
@@ -341,7 +343,7 @@ class ApexLogParser {
341343 const nodesByDepth = this . flattenByDepth ( nodes ) ;
342344 let depth = nodesByDepth . size ;
343345 while ( depth -- ) {
344- const nds = nodesByDepth . get ( depth ) || [ ] ;
346+ const nds = nodesByDepth . get ( depth ) ?? [ ] ;
345347 let i = nds . length ;
346348 while ( i -- ) {
347349 const parent = nds [ i ] ;
@@ -353,6 +355,7 @@ class ApexLogParser {
353355 parent . duration . self -= child . duration . total ;
354356 } ) ;
355357 }
358+ nodesByDepth . delete ( depth ) ;
356359 }
357360 }
358361
@@ -365,9 +368,7 @@ class ApexLogParser {
365368 for ( let i = 0 ; i < len ; i ++ ) {
366369 const child = children [ i ] ;
367370 if ( child ) {
368- const childType = child . type ,
369- isPkgType = childType === 'ENTERING_MANAGED_PKG' ;
370-
371+ const isPkgType = child . type === 'ENTERING_MANAGED_PKG' ;
371372 if ( lastPkg && child instanceof TimedNode ) {
372373 if ( isPkgType && child . namespace === lastPkg . namespace ) {
373374 // combine adjacent (like) packages
@@ -456,7 +457,7 @@ export class DebugLevel {
456457}
457458
458459export class LineIterator {
459- next : LogLine | null = null ;
460+ next : LogLine | null ;
460461 lineGenerator : Generator < LogLine > ;
461462
462463 constructor ( lineGenerator : Generator < LogLine > ) {
@@ -506,7 +507,7 @@ export abstract class LogLine {
506507 /**
507508 * A parsed version of the log line text useful for display in UIs
508509 */
509- text = '' ;
510+ text ;
510511
511512 // optional metadata
512513 /**
@@ -563,7 +564,7 @@ export abstract class LogLine {
563564 /**
564565 * The timestamp of this log line, in nanoseconds
565566 */
566- timestamp = 0 ;
567+ timestamp ;
567568
568569 /**
569570 * The time spent.
@@ -628,9 +629,11 @@ export abstract class LogLine {
628629 constructor ( parts : string [ ] | null ) {
629630 if ( parts ) {
630631 const [ timeData , type ] = parts ;
631- this . type = type as LogEventType ;
632- this . text = this . type ;
633- this . timestamp = this . parseTimestamp ( timeData as string ) ;
632+ this . text = this . type = type as LogEventType ;
633+ this . timestamp = this . parseTimestamp ( timeData || '' ) ;
634+ } else {
635+ this . timestamp = 0 ;
636+ this . text = '' ;
634637 }
635638 }
636639
@@ -2468,23 +2471,23 @@ class MatchEngineBegin extends Method {
24682471}
24692472
24702473function getLogEventClass ( eventName : LogEventType ) : LogLineConstructor | null | undefined {
2474+ if ( ! eventName ) {
2475+ return null ;
2476+ }
2477+
24712478 // Fast path for the most commonly occuring types
24722479 switch ( eventName ) {
24732480 case 'METHOD_ENTRY' :
24742481 return MethodEntryLine ;
2475- break ;
24762482
24772483 case 'METHOD_EXIT' :
24782484 return MethodExitLine ;
2479- break ;
24802485
24812486 case 'CONSTRUCTOR_ENTRY' :
24822487 return ConstructorEntryLine ;
2483- break ;
24842488
24852489 case 'CONSTRUCTOR_EXIT' :
24862490 return ConstructorExitLine ;
2487- break ;
24882491
24892492 default :
24902493 break ;
@@ -2504,7 +2507,10 @@ function getLogEventClass(eventName: LogEventType): LogLineConstructor | null |
25042507}
25052508
25062509type LogLineConstructor < T extends LogLine = LogLine > = new ( parts : string [ ] ) => T ;
2507- export const lineTypeMap = new Map < LogEventType , LogLineConstructor > ( [
2510+ export const lineTypeMap : ReadonlyMap < LogEventType , LogLineConstructor > = new Map <
2511+ LogEventType ,
2512+ LogLineConstructor
2513+ > ( [
25082514 [ 'BULK_DML_RETRY' , BulkDMLEntry ] ,
25092515 [ 'BULK_HEAP_ALLOCATE' , BulkHeapAllocateLine ] ,
25102516 [ 'CALLOUT_REQUEST' , CalloutRequestLine ] ,
0 commit comments