@@ -42,8 +42,7 @@ export class ApexLogParser {
4242 cpuUsed = 0 ;
4343 lastTimestamp = 0 ;
4444 discontinuity = false ;
45- namespaces : string [ ] = [ ] ;
46- namespacesUniq = new Set < string > ( ) ;
45+ namespaces = new Set < string > ( ) ;
4746
4847 /**
4948 * Takes string input of a log and returns the ApexLog class, which represents a log tree
@@ -58,7 +57,7 @@ export class ApexLogParser {
5857 apexLog . logIssues = this . logIssues ;
5958 apexLog . parsingErrors = this . parsingErrors ;
6059 apexLog . cpuTime = this . cpuUsed ;
61- apexLog . namespaces = this . namespaces ;
60+ apexLog . namespaces = Array . from ( this . namespaces ) ;
6261
6362 return apexLog ;
6463 }
@@ -67,19 +66,19 @@ export class ApexLogParser {
6766 const parts = line . split ( '|' ) ;
6867
6968 const type = parts [ 1 ] ?? '' ;
69+
7070 const metaCtor = getLogEventClass ( type as LogEventType ) ;
7171 if ( metaCtor ) {
7272 const entry = new metaCtor ( this , parts ) ;
7373 entry . logLine = line ;
7474 lastEntry ?. onAfter ?.( this , entry ) ;
75- if ( entry . namespace && ! this . namespacesUniq . has ( entry . namespace ) ) {
76- this . namespaces . push ( entry . namespace ) ;
77- this . namespacesUniq . add ( entry . namespace ) ;
75+ if ( entry . namespace ) {
76+ this . namespaces . add ( entry . namespace ) ;
7877 }
7978 return entry ;
8079 }
8180
82- const hasType = type && typePattern . test ( type ) ;
81+ const hasType = ! ! ( type && typePattern . test ( type ) ) ;
8382 if ( ! hasType && lastEntry ?. acceptsText ) {
8483 // wrapped text from the previous entry?
8584 lastEntry . text += '\n' + line ;
@@ -169,11 +168,10 @@ export class ApexLogParser {
169168 line . parent = rootMethod ;
170169 rootMethod . addChild ( line ) ;
171170 }
172- rootMethod . setTimes ( ) ;
173171
172+ rootMethod . setTimes ( ) ;
174173 this . insertPackageWrappers ( rootMethod ) ;
175174 this . aggregateTotals ( [ rootMethod ] ) ;
176-
177175 return rootMethod ;
178176 }
179177
@@ -220,40 +218,39 @@ export class ApexLogParser {
220218 break ;
221219 }
222220
223- nextLine . namespace ||= currentLine . namespace || 'default' ;
224221 lineIter . fetch ( ) ; // it's a child - consume the line
225222 this . lastTimestamp = nextLine . timestamp ;
223+ nextLine . namespace ||= currentLine . namespace || 'default' ;
224+ nextLine . parent = currentLine ;
225+ currentLine . children . push ( nextLine ) ;
226226
227227 if ( nextLine instanceof Method ) {
228228 this . parseTree ( nextLine , lineIter , stack ) ;
229229 }
230-
231- nextLine . parent = currentLine ;
232- currentLine . children . push ( nextLine ) ;
233230 }
234231
235232 // End of line error handling. We have finished processing this log line and either got to the end
236233 // of the log without finding an exit line or the current line was truncated)
237234 if ( ! nextLine || currentLine . isTruncated ) {
238235 // truncated method - terminate at the end of the log
239- currentLine . exitStamp = this . lastTimestamp ;
236+ currentLine . exitStamp = this . lastTimestamp ?? currentLine . timestamp ;
240237
241238 // 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
242239 this . addLogIssue (
243- this . lastTimestamp ,
240+ currentLine . exitStamp ,
244241 'Unexpected-End' ,
245242 'An entry event was found without a corresponding exit event e.g a `METHOD_ENTRY` event without a `METHOD_EXIT`' ,
246243 'unexpected' ,
247244 ) ;
248245
249246 if ( currentLine . isTruncated ) {
250247 this . updateLogIssue (
251- this . lastTimestamp ,
248+ currentLine . exitStamp ,
252249 'Max-Size-reached' ,
253250 'The maximum log size has been reached. Part of the log has been truncated.' ,
254251 'skip' ,
255252 ) ;
256- this . maxSizeTimestamp = this . lastTimestamp ;
253+ this . maxSizeTimestamp = currentLine . exitStamp ;
257254 }
258255 currentLine . isTruncated = true ;
259256 }
@@ -264,7 +261,7 @@ export class ApexLogParser {
264261 }
265262
266263 private isMatchingEnd ( startMethod : Method , endLine : LogLine ) {
267- return (
264+ return ! ! (
268265 endLine . type &&
269266 startMethod . exitTypes . includes ( endLine . type ) &&
270267 ( endLine . lineNumber === startMethod . lineNumber ||
@@ -305,26 +302,26 @@ export class ApexLogParser {
305302
306303 private flattenByDepth ( nodes : LogLine [ ] ) {
307304 const result = new Map < number , LogLine [ ] > ( ) ;
308- result . set ( 0 , nodes ) ;
309-
310- let currentDepth = 1 ;
311305
306+ let currentDepth = 0 ;
312307 let currentNodes = nodes ;
313308 let len = currentNodes . length ;
314309 while ( len ) {
315- result . set ( currentDepth , [ ] ) ;
310+ result . set ( currentDepth , currentNodes ) ;
311+
312+ const children : LogLine [ ] = [ ] ;
316313 while ( len -- ) {
317314 const node = currentNodes [ len ] ;
318315 if ( node ?. children ) {
319- const children = result . get ( currentDepth ) ! ;
320316 node . children . forEach ( ( c ) => {
321317 if ( c . children . length ) {
322318 children . push ( c ) ;
323319 }
324320 } ) ;
325321 }
326322 }
327- currentNodes = result . get ( currentDepth ++ ) ?? [ ] ;
323+ currentDepth ++ ;
324+ currentNodes = children ;
328325 len = currentNodes . length ;
329326 }
330327
@@ -677,7 +674,7 @@ export abstract class LogLine {
677674 if ( parts ) {
678675 const [ timeData , type ] = parts ;
679676 this . text = this . type = type as LogEventType ;
680- this . timestamp = this . parseTimestamp ( timeData || '' ) ;
677+ this . timestamp = timeData ? this . parseTimestamp ( timeData ) : 0 ;
681678 } else {
682679 this . timestamp = 0 ;
683680 this . text = '' ;
@@ -965,14 +962,10 @@ class ConstructorEntryLine extends Method {
965962
966963 _parseConstructorNamespace ( className : string ) : string {
967964 let possibleNs = className . slice ( 0 , className . indexOf ( '.' ) ) ;
968- possibleNs =
969- this . logParser . namespaces . find ( ( ns ) => {
970- return ns === possibleNs ;
971- } ) || '' ;
972-
973- if ( possibleNs ) {
965+ if ( this . logParser . namespaces . has ( possibleNs ) ) {
974966 return possibleNs ;
975967 }
968+
976969 const constructorParts = ( className ?? '' ) . split ( '.' ) ;
977970 possibleNs = constructorParts [ 0 ] || '' ;
978971 // inmner class with a namespace
@@ -1007,12 +1000,12 @@ export class MethodEntryLine extends Method {
10071000 constructor ( parser : ApexLogParser , parts : string [ ] ) {
10081001 super ( parser , parts , [ 'METHOD_EXIT' ] , 'Method' , 'method' ) ;
10091002 this . lineNumber = this . parseLineNumber ( parts [ 2 ] ) ;
1010- this . text = parts [ 4 ] || this . type || '' ;
1003+ this . text = parts [ 4 ] || this . type || this . text ;
10111004 if ( this . text . indexOf ( 'System.Type.forName(' ) !== - 1 ) {
10121005 // assume we are not charged for class loading (or at least not lengthy remote-loading / compiling)
10131006 this . cpuType = 'loading' ;
10141007 } else {
1015- const possibleNs = this . _parseMethodNamespace ( parts [ 4 ] || '' ) ;
1008+ const possibleNs = this . _parseMethodNamespace ( parts [ 4 ] ) ;
10161009 if ( possibleNs ) {
10171010 this . namespace = possibleNs ;
10181011 }
@@ -1025,23 +1018,27 @@ export class MethodEntryLine extends Method {
10251018 }
10261019 }
10271020
1028- _parseMethodNamespace ( methodName : string ) : string {
1021+ _parseMethodNamespace ( methodName : string | undefined ) : string {
1022+ if ( ! methodName ) {
1023+ return '' ;
1024+ }
1025+
10291026 const methodBracketIndex = methodName . indexOf ( '(' ) ;
10301027 if ( methodBracketIndex === - 1 ) {
10311028 return '' ;
10321029 }
10331030
1034- let possibleNs = methodName . slice ( 0 , methodName . indexOf ( '.' ) ) ;
1035- possibleNs =
1036- this . logParser . namespaces . find ( ( ns ) => {
1037- return ns === possibleNs ;
1038- } ) || '' ;
1031+ const nsSeparator = methodName . indexOf ( '.' ) ;
1032+ if ( nsSeparator === - 1 ) {
1033+ return '' ;
1034+ }
10391035
1040- if ( possibleNs ) {
1036+ const possibleNs = methodName . slice ( 0 , nsSeparator ) ;
1037+ if ( this . logParser . namespaces . has ( possibleNs ) ) {
10411038 return possibleNs ;
10421039 }
10431040
1044- const methodNameParts = methodName ? methodName . slice ( 0 , methodBracketIndex ) ?. split ( '.' ) : '' ;
1041+ const methodNameParts = methodName . slice ( 0 , methodBracketIndex ) ?. split ( '.' ) ;
10451042 if ( methodNameParts . length === 4 ) {
10461043 return methodNameParts [ 0 ] ?? '' ;
10471044 } else if ( methodNameParts . length === 2 ) {
@@ -1057,9 +1054,12 @@ class MethodExitLine extends LogLine {
10571054 constructor ( parser : ApexLogParser , parts : string [ ] ) {
10581055 super ( parser , parts ) ;
10591056 this . lineNumber = this . parseLineNumber ( parts [ 2 ] ) ;
1060- this . text = parts [ 4 ] ?? parts [ 3 ] ?? '' ;
1057+ this . text = parts [ 4 ] ?? parts [ 3 ] ?? this . text ;
10611058
1059+ /*A method will end with ')'. Without that this it represents the first reference to a class, outer or inner. One of the few reliable ways to determine valid namespaces. The first reference to a class (outer or inner) will always have an METHOD_EXIT containing the Outer class name with namespace if present. Other events will follow, CONSTRUCTOR_ENTRY etc. But this case will only ever have 2 parts ns.Outer even if the first reference was actually an inner class e.g new ns.Outer.Inner();*/
1060+ // If does not end in ) then we have a reference to the class, either via outer or inner.
10621061 if ( ! this . text . endsWith ( ')' ) ) {
1062+ // if there is a . the we have a namespace e.g ns.Outer
10631063 const index = this . text . indexOf ( '.' ) ;
10641064 if ( index !== - 1 ) {
10651065 this . namespace = this . text . slice ( 0 , index ) ;
0 commit comments