diff --git a/log-viewer/modules/Database.ts b/log-viewer/modules/Database.ts index 2e0c1118..ff52fa5d 100644 --- a/log-viewer/modules/Database.ts +++ b/log-viewer/modules/Database.ts @@ -1,9 +1,14 @@ /* * Copyright (c) 2020 Certinia Inc. All rights reserved. */ -import { ApexLog, DMLBeginLine, Method, SOQLExecuteBeginLine } from './parsers/ApexLogParser.js'; +import { + type ApexLog, + DMLBeginLine, + type LogEvent, + SOQLExecuteBeginLine, +} from './parsers/LogEvents.js'; -export type Stack = Method[]; +export type Stack = LogEvent[]; export class DatabaseAccess { private static _instance: DatabaseAccess | null = null; @@ -23,13 +28,13 @@ export class DatabaseAccess { public getStack( timestamp: number, stack: Stack = [], - line: Method = DatabaseAccess._treeRoot, + line: LogEvent = DatabaseAccess._treeRoot, ): Stack { const children = line.children; const len = children.length; for (let i = 0; i < len; ++i) { const child = children[i]; - if (child instanceof Method) { + if (child?.exitTypes.length) { stack.push(child); if (child.timestamp === timestamp) { return stack; @@ -45,7 +50,7 @@ export class DatabaseAccess { return []; } - public getSOQLLines(line: Method = DatabaseAccess._treeRoot): SOQLExecuteBeginLine[] { + public getSOQLLines(line: LogEvent = DatabaseAccess._treeRoot): SOQLExecuteBeginLine[] { const results: SOQLExecuteBeginLine[] = []; const children = line.children; @@ -56,7 +61,7 @@ export class DatabaseAccess { results.push(child); } - if (child instanceof Method) { + if (child?.exitTypes.length) { Array.prototype.push.apply(results, this.getSOQLLines(child)); } } @@ -64,7 +69,7 @@ export class DatabaseAccess { return results; } - public getDMLLines(line: Method = DatabaseAccess._treeRoot): DMLBeginLine[] { + public getDMLLines(line: LogEvent = DatabaseAccess._treeRoot): DMLBeginLine[] { const results: DMLBeginLine[] = []; const children = line.children; @@ -75,7 +80,7 @@ export class DatabaseAccess { results.push(child); } - if (child instanceof Method) { + if (child?.exitTypes.length) { // results = results.concat(this.getDMLLines(child)); Array.prototype.push.apply(results, this.getDMLLines(child)); } diff --git a/log-viewer/modules/__tests__/ApexLogParser.test.ts b/log-viewer/modules/__tests__/ApexLogParser.test.ts index 2b711ca7..f22b4781 100644 --- a/log-viewer/modules/__tests__/ApexLogParser.test.ts +++ b/log-viewer/modules/__tests__/ApexLogParser.test.ts @@ -2,21 +2,25 @@ * Copyright (c) 2020 Certinia Inc. All rights reserved. */ import { - ApexLogParser, CodeUnitStartedLine, ExecutionStartedLine, - LogLine, - Method, + LogEvent, MethodEntryLine, SOQLExecuteBeginLine, SOQLExecuteExplainLine, - TimedNode, - lineTypeMap, - parse, parseObjectNamespace, parseRows, parseVfNamespace, -} from '../parsers/ApexLogParser.js'; +} from '../parsers/LogEvents.js'; +import { lineTypeMap } from '../parsers/LogLineMapping.js'; + +import { ApexLogParser, parse } from '../parsers/ApexLogParser.js'; + +class DummyLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + } +} describe('parseObjectNamespace tests', () => { it('Should consider no separator to be unmanaged', () => { @@ -79,14 +83,14 @@ describe('Pseudo EXIT events', () => { expect(log1.children.length).toEqual(4); expect(log1.duration).toEqual({ self: 0, total: 3 }); - const approval1 = log1.children[0] as Method; + const approval1 = log1.children[0]; expect(approval1).toMatchObject({ type: 'WF_APPROVAL_SUBMIT', timestamp: 1, duration: { self: 1, total: 1 }, }); - const processFound1 = log1.children[1] as Method; + const processFound1 = log1.children[1]; expect(processFound1).toMatchObject({ parent: log1, type: 'WF_PROCESS_FOUND', @@ -94,7 +98,7 @@ describe('Pseudo EXIT events', () => { duration: { self: 1, total: 1 }, }); - const approval2 = log1.children[2] as Method; + const approval2 = log1.children[2]; expect(approval2).toMatchObject({ parent: log1, type: 'WF_APPROVAL_SUBMIT', @@ -102,7 +106,7 @@ describe('Pseudo EXIT events', () => { duration: { self: 1, total: 1 }, }); - const processFound2 = log1.children[3] as Method; + const processFound2 = log1.children[3]; expect(processFound2).toMatchObject({ parent: log1, type: 'WF_PROCESS_FOUND', @@ -125,22 +129,22 @@ describe('Pseudo EXIT events', () => { expect(log1.children.length).toEqual(1); expect(log1.duration).toEqual({ self: 0, total: 6 }); - const children = (log1.children[0] as Method).children; + const children = log1.children[0]?.children ?? []; expect(children.length).toEqual(4); - const child1 = children[0] as Method; + const child1 = children[0]!; expect(child1.timestamp).toEqual(2); expect(child1.exitStamp).toEqual(3); - const child2 = children[1] as Method; + const child2 = children[1]!; expect(child2.timestamp).toEqual(3); expect(child2.exitStamp).toEqual(4); - const child3 = children[2] as Method; + const child3 = children[2]!; expect(child3.timestamp).toEqual(4); expect(child3.exitStamp).toEqual(5); - const child4 = children[3] as Method; + const child4 = children[3]!; expect(child4.timestamp).toEqual(5); expect(child4.exitStamp).toEqual(6); }); @@ -157,18 +161,18 @@ describe('Pseudo EXIT events', () => { expect(log1.children.length).toEqual(1); expect(log1.duration).toEqual({ self: 0, total: 4 }); - const children = (log1.children[0] as Method).children; + const children = log1.children[0]?.children ?? []; expect(children.length).toEqual(3); - const child1 = children[0] as Method; + const child1 = children[0]!; expect(child1.timestamp).toEqual(2); expect(child1.exitStamp).toEqual(3); - const child2 = children[1] as Method; + const child2 = children[1]!; expect(child2.timestamp).toEqual(3); expect(child2.exitStamp).toEqual(4); - const child3 = children[2] as Method; + const child3 = children[2]!; expect(child3.timestamp).toEqual(4); expect(child3.exitStamp).toEqual(5); }); @@ -204,7 +208,7 @@ describe('parseLog tests', () => { expect(logLines.length).toEqual(1); expect(logLines[0]).toBeInstanceOf(ExecutionStartedLine); - const firstChildren = (logLines[0] as Method).children; + const firstChildren = logLines[0]?.children ?? []; expect(firstChildren.length).toEqual(1); expect(firstChildren[0]).toBeInstanceOf(CodeUnitStartedLine); }); @@ -222,7 +226,7 @@ describe('parseLog tests', () => { expect(apexLog.children.length).toEqual(1); expect(apexLog.children[0]).toBeInstanceOf(ExecutionStartedLine); - const firstChildren = (apexLog.children[0] as Method).children; + const firstChildren = apexLog.children[0]?.children ?? []; expect(firstChildren.length).toEqual(1); expect(firstChildren[0]).toBeInstanceOf(CodeUnitStartedLine); }); @@ -237,7 +241,7 @@ describe('parseLog tests', () => { expect(apexLog.children.length).toBe(1); expect(apexLog.children[0]).toBeInstanceOf(ExecutionStartedLine); - const firstChildren = (apexLog.children[0] as Method).children; + const firstChildren = apexLog.children[0]?.children ?? []; expect(firstChildren[0]).toBeInstanceOf(CodeUnitStartedLine); }); @@ -406,7 +410,7 @@ describe('parseLog tests', () => { const apexLog = parse(log); - const methods = apexLog.children as Method[]; + const methods = apexLog.children; expect(methods.length).toBe(24); methods.forEach((line) => { expect(line.exitTypes.length).toBe(0); @@ -464,7 +468,7 @@ describe('getRootMethod tests', () => { const apexLog = parse(log); - const timedLogLines = apexLog.children as TimedNode[]; + const timedLogLines = apexLog.children; expect(timedLogLines.length).toBe(1); const startLine = timedLogLines[0]; expect(startLine?.type).toBe('EXECUTION_STARTED'); @@ -478,7 +482,7 @@ describe('getRootMethod tests', () => { }); expect(unitStart.children.length).toBe(1); - const interViewsBegin = unitStart.children[0] as TimedNode; + const interViewsBegin = unitStart.children[0]!; expect(interViewsBegin).toMatchObject({ parent: unitStart, type: 'FLOW_START_INTERVIEWS_BEGIN', @@ -508,7 +512,7 @@ describe('getRootMethod tests', () => { const apexLog = parse(log); - const timedLogLines = apexLog.children as TimedNode[]; + const timedLogLines = apexLog.children; expect(timedLogLines.length).toBe(1); const startLine = timedLogLines[0]; expect(startLine?.type).toBe('EXECUTION_STARTED'); @@ -519,7 +523,7 @@ describe('getRootMethod tests', () => { expect(unitStart.codeUnitType).toBe('Flow'); expect(unitStart.children.length).toBe(1); - const interViewsBegin = unitStart.children[0] as TimedNode; + const interViewsBegin = unitStart.children[0]!; expect(interViewsBegin.type).toBe('FLOW_START_INTERVIEWS_BEGIN'); expect(interViewsBegin.text).toBe('FLOW_START_INTERVIEWS : Example Flow'); expect(interViewsBegin.suffix).toBe(' (Flow)'); @@ -547,7 +551,7 @@ describe('getRootMethod tests', () => { const apexLog = parse(log); - const timedLogLines = apexLog.children as TimedNode[]; + const timedLogLines = apexLog.children; expect(timedLogLines.length).toBe(1); const startLine = timedLogLines[0]; expect(startLine?.type).toBe('EXECUTION_STARTED'); @@ -558,17 +562,17 @@ describe('getRootMethod tests', () => { expect(unitStart.codeUnitType).toBe('Workflow'); expect(unitStart.children.length).toBe(1); - const pbBegin = unitStart.children[0] as TimedNode; + const pbBegin = unitStart.children[0]!; expect(pbBegin.type).toBe('FLOW_START_INTERVIEWS_BEGIN'); expect(pbBegin.text).toBe('FLOW_START_INTERVIEWS : Example Process Builder'); expect(pbBegin.suffix).toBe(' (Process Builder)'); expect(pbBegin.children.length).toBe(1); - const pbDetail = pbBegin.children[0] as TimedNode; + const pbDetail = pbBegin.children[0]!; expect(pbDetail.type).toBe('FLOW_START_INTERVIEW_BEGIN'); expect(pbDetail.text).toBe('Example Process Builder'); - const interViewsBegin = pbDetail.children[0] as TimedNode; + const interViewsBegin = pbDetail.children[0]!; expect(interViewsBegin.type).toBe('FLOW_START_INTERVIEWS_BEGIN'); expect(interViewsBegin.text).toBe('FLOW_START_INTERVIEWS : Example Flow'); expect(interViewsBegin.suffix).toBe(' (Flow)'); @@ -643,9 +647,9 @@ describe('getRootMethod tests', () => { expect(apexLog.exitStamp).toBe(1100); expect(apexLog.executionEndTime).toBe(1100); - const rootChildren = apexLog.children as Method[]; + const rootChildren = apexLog.children; const executionStarted = rootChildren[0]; - const executionChildren = executionStarted?.children as Method[]; + const executionChildren = executionStarted?.children ?? []; expect(executionChildren.length).toBe(5); expect(executionChildren[0]).toMatchObject({ @@ -1118,7 +1122,8 @@ describe('namespace tests', () => { describe('Recalculate durations tests', () => { it('Recalculates parent node', () => { const parser = new ApexLogParser(); - const node = new Method(parser, ['14:32:07.563 (1)', 'DUMMY'], [], 'Method', ''); + const node = new DummyLine(parser, ['14:32:07.563 (1)', 'DUMMY']); + node.subCategory = 'Method'; node.exitStamp = 3; node.recalculateDurations(); @@ -1130,7 +1135,7 @@ describe('Recalculate durations tests', () => { describe('Line Type Tests', () => { it('Lines referenced by exitTypes should be exits', () => { const parser = new ApexLogParser(); - for (const [key, lineType] of lineTypeMap) { + for (const lineType of Object.values(lineTypeMap)) { const line = new lineType(parser, [ '14:32:07.563 (17358806534)', 'DUMMY', @@ -1138,12 +1143,9 @@ describe('Line Type Tests', () => { 'Rows:3', '', 'Rows:5', - ]) as LogLine; - if (line instanceof Method) { - expect(line.exitTypes).not.toBe(null); - if (line.isExit) { - expect(line.exitTypes).toEqual([key]); - } + ]) as LogEvent; + + if (line.exitTypes.length) { line.exitTypes.forEach((exitType) => { const exitCls = lineTypeMap.get(exitType); expect(exitCls).not.toBe(null); @@ -1155,7 +1157,7 @@ describe('Line Type Tests', () => { 'Rows:3', '', 'Rows:5', - ]) as LogLine; + ]) as LogEvent; expect(exitLine.isExit).toBe(true); } }); diff --git a/log-viewer/modules/__tests__/soql/SOQLLinter.test.ts b/log-viewer/modules/__tests__/soql/SOQLLinter.test.ts index 906c845d..a34da170 100644 --- a/log-viewer/modules/__tests__/soql/SOQLLinter.test.ts +++ b/log-viewer/modules/__tests__/soql/SOQLLinter.test.ts @@ -1,9 +1,19 @@ /* * Copyright (c) 2021 Certinia Inc. All rights reserved. */ -import { ApexLogParser, Method } from '../../parsers/ApexLogParser.js'; +import { ApexLogParser } from '../../parsers/ApexLogParser.js'; +import { LogEvent } from '../../parsers/LogEvents.js'; import { SOQLLinter } from '../../soql/SOQLLinter.js'; +class DummySOQLLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[] | null) { + super(parser, parts); + this.subCategory = 'Code Unit'; + this.cpuType = 'method'; + this.exitTypes = ['CODE_UNIT_FINISHED']; + } +} + describe('SOQL Linter rule tests', () => { it('No where clause should return rule', async () => { const soql = 'SELECT Id FROM ANOBJECT__c'; @@ -155,20 +165,14 @@ describe('SOQL in Trigger Rule tests', () => { it('soql in trigger should return rule', async () => { const parser = new ApexLogParser(); const soql = 'SELECT Id FROM AnObject__c WHERE value__c > 0'; - const mockTriggerLine = new Method( - parser, - [ - '04:16:39.166 (1166781977)', - 'CODE_UNIT_STARTED', - '[EXTERNAL]', - 'a0000000000aaaa', - 'Account on Account trigger event AfterInsert', - '__sfdc_trigger/Account', - ], - ['CODE_UNIT_FINISHED'], - 'Code Unit', - 'method', - ); + const mockTriggerLine = new DummySOQLLine(parser, [ + '04:16:39.166 (1166781977)', + 'CODE_UNIT_STARTED', + '[EXTERNAL]', + 'a0000000000aaaa', + 'Account on Account trigger event AfterInsert', + '__sfdc_trigger/Account', + ]); mockTriggerLine.text = 'Account on Account trigger event AfterInsert'; const results = await new SOQLLinter().lint(soql, [mockTriggerLine]); diff --git a/log-viewer/modules/components/AppHeader.ts b/log-viewer/modules/components/AppHeader.ts index ff52c226..3de0f8f0 100644 --- a/log-viewer/modules/components/AppHeader.ts +++ b/log-viewer/modules/components/AppHeader.ts @@ -10,9 +10,10 @@ import { import { LitElement, css, html, unsafeCSS } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; +import type { ApexLog } from '../parsers/LogEvents.js'; import codiconStyles from '../styles/codicon.css'; import { globalStyles } from '../styles/global.styles.js'; -import { ApexLog, type TimelineGroup } from '../timeline/Timeline.js'; +import type { TimelineGroup } from '../timeline/Timeline.js'; import '../timeline/TimelineView.js'; import './analysis-view/AnalysisView.js'; import './calltree-view/CalltreeView'; diff --git a/log-viewer/modules/components/CallStack.ts b/log-viewer/modules/components/CallStack.ts index f792deb7..8aac118e 100644 --- a/log-viewer/modules/components/CallStack.ts +++ b/log-viewer/modules/components/CallStack.ts @@ -5,7 +5,7 @@ import { LitElement, css, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { DatabaseAccess } from '../Database.js'; -import { LogLine } from '../parsers/ApexLogParser.js'; +import type { LogEvent } from '../parsers/LogEvents.js'; import { globalStyles } from '../styles/global.styles.js'; import { goToRow } from './calltree-view/CalltreeView.js'; @@ -78,7 +78,7 @@ export class CallStack extends LitElement { } } - private lineLink(line: LogLine) { + private lineLink(line: LogEvent) { return html` = new Map(); for (const child of root.children) { @@ -455,7 +455,7 @@ function groupMetrics(root: LogLine) { return Array.from(methodMap.values()); } -function addNodeToMap(map: Map, node: LogLine) { +function addNodeToMap(map: Map, node: LogEvent) { if (node.duration.total) { const key = node.namespace + node.text; let metric = map.get(key); diff --git a/log-viewer/modules/components/analysis-view/column-calcs/CallStackSum.ts b/log-viewer/modules/components/analysis-view/column-calcs/CallStackSum.ts index 436b3c0d..f62b5a52 100644 --- a/log-viewer/modules/components/analysis-view/column-calcs/CallStackSum.ts +++ b/log-viewer/modules/components/analysis-view/column-calcs/CallStackSum.ts @@ -1,12 +1,12 @@ -import type { LogLine } from '../../../parsers/ApexLogParser'; -import { type Metric } from '../AnalysisView.js'; +import type { LogEvent } from '../../../parsers/LogEvents.js'; +import type { Metric } from '../AnalysisView.js'; export function callStackSum(_values: number[], data: Metric[], _calcParams: unknown) { - const nodes: LogLine[] = []; + const nodes: LogEvent[] = []; for (const row of data) { Array.prototype.push.apply(nodes, row.nodes); } - const allNodes = new Set(nodes); + const allNodes = new Set(nodes); let total = 0; for (const node of nodes) { @@ -18,7 +18,7 @@ export function callStackSum(_values: number[], data: Metric[], _calcParams: unk return total; } -function _isChildOfOther(node: LogLine, filteredNodes: Set) { +function _isChildOfOther(node: LogEvent, filteredNodes: Set) { let parent = node.parent; while (parent) { if (filteredNodes.has(parent)) { diff --git a/log-viewer/modules/components/calltree-view/CalltreeView.ts b/log-viewer/modules/components/calltree-view/CalltreeView.ts index e552e062..51562432 100644 --- a/log-viewer/modules/components/calltree-view/CalltreeView.ts +++ b/log-viewer/modules/components/calltree-view/CalltreeView.ts @@ -27,7 +27,8 @@ import { progressFormatter } from '../../datagrid/format/Progress.js'; import { RowKeyboardNavigation } from '../../datagrid/module/RowKeyboardNavigation.js'; import { RowNavigation } from '../../datagrid/module/RowNavigation.js'; import dataGridStyles from '../../datagrid/style/DataGrid.scss'; -import { ApexLog, LogLine, TimedNode, type LogEventType } from '../../parsers/ApexLogParser.js'; +import type { ApexLog, LogEvent } from '../../parsers/LogEvents.js'; +import type { LogEventType } from '../../parsers/types.js'; import { vscodeMessenger } from '../../services/VSCodeExtensionMessenger.js'; import { globalStyles } from '../../styles/global.styles.js'; import { isVisible } from '../../Util.js'; @@ -220,7 +221,7 @@ export class CalltreeView extends LitElement { _findEvt = ((event: FindEvt) => this._find(event)) as EventListener; - _getAllTypes(data: LogLine[]): string[] { + _getAllTypes(data: LogEvent[]): string[] { const flattened = this._flatten(data); const types = new Set(); for (const line of flattened) { @@ -229,7 +230,7 @@ export class CalltreeView extends LitElement { return Array.from(types).sort(); } - _flat(arr: LogLine[], target: LogLine[]) { + _flat(arr: LogEvent[], target: LogEvent[]) { arr.forEach((el) => { target.push(el); if (el.children.length > 0) { @@ -238,8 +239,8 @@ export class CalltreeView extends LitElement { }); } - _flatten(arr: LogLine[]) { - const flattened: LogLine[] = []; + _flatten(arr: LogEvent[]) { + const flattened: LogEvent[] = []; this._flat(arr, flattened); return flattened; } @@ -826,7 +827,7 @@ export class CalltreeView extends LitElement { } } - private _toCallTree(nodes: LogLine[]): CalltreeRow[] | undefined { + private _toCallTree(nodes: LogEvent[]): CalltreeRow[] | undefined { const len = nodes.length; if (!len) { return undefined; @@ -873,7 +874,7 @@ export class CalltreeView extends LitElement { if (!row) { break; } - const node = (row.getData() as CalltreeRow).originalData as TimedNode; + const node = (row.getData() as CalltreeRow).originalData as LogEvent; // Return True if the element is present in the middle. const endTime = node.exitStamp ?? node.timestamp; @@ -899,7 +900,7 @@ export class CalltreeView extends LitElement { interface CalltreeRow { id: string; - originalData: LogLine; + originalData: LogEvent; _children: CalltreeRow[] | undefined | null; text: string; duration: CountTotals; diff --git a/log-viewer/modules/components/calltree-view/module/MiddleRowFocus.ts b/log-viewer/modules/components/calltree-view/module/MiddleRowFocus.ts index d9966150..64a91630 100644 --- a/log-viewer/modules/components/calltree-view/module/MiddleRowFocus.ts +++ b/log-viewer/modules/components/calltree-view/module/MiddleRowFocus.ts @@ -2,9 +2,9 @@ * Copyright (c) 2024 Certinia Inc. All rights reserved. */ import { Module, type RowComponent, type Tabulator } from 'tabulator-tables'; -import type { TimedNode } from '../../../parsers/ApexLogParser'; +import type { LogEvent } from '../../../parsers/LogEvents.js'; -type TimedNodeProp = { originalData: TimedNode }; +type TimedNodeProp = { originalData: LogEvent }; const middleRowFocusOption = 'middleRowFocus' as const; /** diff --git a/log-viewer/modules/components/database-view/DMLView.ts b/log-viewer/modules/components/database-view/DMLView.ts index 7ea6d345..1c58c3f1 100644 --- a/log-viewer/modules/components/database-view/DMLView.ts +++ b/log-viewer/modules/components/database-view/DMLView.ts @@ -25,7 +25,7 @@ import dataGridStyles from '../../datagrid/style/DataGrid.scss'; // others import { DatabaseAccess } from '../../Database.js'; -import { ApexLog, DMLBeginLine } from '../../parsers/ApexLogParser.js'; +import type { ApexLog, DMLBeginLine } from '../../parsers/LogEvents.js'; import { vscodeMessenger } from '../../services/VSCodeExtensionMessenger.js'; import codiconStyles from '../../styles/codicon.css'; import { globalStyles } from '../../styles/global.styles.js'; diff --git a/log-viewer/modules/components/database-view/DatabaseSection.ts b/log-viewer/modules/components/database-view/DatabaseSection.ts index 9764cfbb..a2dde7a5 100644 --- a/log-viewer/modules/components/database-view/DatabaseSection.ts +++ b/log-viewer/modules/components/database-view/DatabaseSection.ts @@ -4,7 +4,7 @@ import { LitElement, css, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; -import { Method } from '../../parsers/ApexLogParser.js'; +import type { LogEvent } from '../../parsers/LogEvents.js'; import { globalStyles } from '../../styles/global.styles.js'; import '../BadgeBase.js'; @@ -13,7 +13,7 @@ export class DatabaseSection extends LitElement { @property({ type: String }) title = ''; @property({ type: Object, attribute: false }) - dbLines: Method[] = []; + dbLines: LogEvent[] = []; static styles = [ globalStyles, diff --git a/log-viewer/modules/components/database-view/DatabaseView.ts b/log-viewer/modules/components/database-view/DatabaseView.ts index 83fe0620..434e647d 100644 --- a/log-viewer/modules/components/database-view/DatabaseView.ts +++ b/log-viewer/modules/components/database-view/DatabaseView.ts @@ -5,7 +5,7 @@ import { LitElement, css, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; -import { ApexLog } from '../../parsers/ApexLogParser.js'; +import type { ApexLog } from '../../parsers/LogEvents.js'; import { globalStyles } from '../../styles/global.styles.js'; import '../CallStack.js'; import './DMLView.js'; diff --git a/log-viewer/modules/components/database-view/SOQLView.ts b/log-viewer/modules/components/database-view/SOQLView.ts index 2915fe9b..367efba4 100644 --- a/log-viewer/modules/components/database-view/SOQLView.ts +++ b/log-viewer/modules/components/database-view/SOQLView.ts @@ -32,11 +32,11 @@ import dataGridStyles from '../../datagrid/style/DataGrid.scss'; // others import { DatabaseAccess } from '../../Database.js'; import { isVisible } from '../../Util.js'; -import { +import type { ApexLog, SOQLExecuteBeginLine, SOQLExecuteExplainLine, -} from '../../parsers/ApexLogParser.js'; +} from '../../parsers/LogEvents.js'; import { vscodeMessenger } from '../../services/VSCodeExtensionMessenger.js'; import codiconStyles from '../../styles/codicon.css'; import { globalStyles } from '../../styles/global.styles.js'; diff --git a/log-viewer/modules/parsers/ApexLogParser.ts b/log-viewer/modules/parsers/ApexLogParser.ts index 0381839c..6eb2c262 100644 --- a/log-viewer/modules/parsers/ApexLogParser.ts +++ b/log-viewer/modules/parsers/ApexLogParser.ts @@ -2,21 +2,13 @@ * Copyright (c) 2020 Certinia Inc. All rights reserved. */ +import { ApexLog, type LogEvent } from './LogEvents.js'; +import { getLogEventClass } from './LogLineMapping.js'; +import type { IssueType, LogEventType, LogIssue } from './types.js'; + const typePattern = /^[A-Z_]*$/, settingsPattern = /^\d+\.\d+\sAPEX_CODE,\w+;APEX_PROFILING,.+$/m; -type LineNumber = number | string | null; // an actual line-number or 'EXTERNAL' -type IssueType = 'unexpected' | 'error' | 'skip'; - -export type LogSubCategory = - | 'Method' - | 'System Method' - | 'Code Unit' - | 'DML' - | 'SOQL' - | 'Flow' - | 'Workflow'; - /** * Takes string input of a log and returns the ApexLog class, which represents a log tree * @param {string} logData @@ -62,7 +54,7 @@ export class ApexLogParser { return apexLog; } - private parseLine(line: string, lastEntry: LogLine | null): LogLine | null { + private parseLine(line: string, lastEntry: LogEvent | null): LogEvent | null { const parts = line.split('|'); const type = parts[1] ?? ''; @@ -111,7 +103,7 @@ export class ApexLogParser { return null; } - private *generateLogLines(log: string): Generator { + private *generateLogLines(log: string): Generator { const start = log.search(/^\d{2}:\d{2}:\d{2}.\d{1} \(\d+\)\|EXECUTION_STARTED$/m); if (start > -1) { log = log.slice(start); @@ -154,19 +146,19 @@ export class ApexLogParser { } } - private toLogTree(lineGenerator: Generator) { + private toLogTree(lineGenerator: Generator) { const rootMethod = new ApexLog(this), - stack: Method[] = []; - let line: LogLine | null; + stack: LogEvent[] = []; + let line: LogEvent | null; const lineIter = new LineIterator(lineGenerator); while ((line = lineIter.fetch())) { - if (line instanceof Method) { + if (line.exitTypes.length) { this.parseTree(line, lineIter, stack); } line.parent = rootMethod; - rootMethod.addChild(line); + rootMethod.children.push(line); } rootMethod.setTimes(); @@ -175,7 +167,7 @@ export class ApexLogParser { return rootMethod; } - private parseTree(currentLine: Method, lineIter: LineIterator, stack: Method[]) { + private parseTree(currentLine: LogEvent, lineIter: LineIterator, stack: LogEvent[]) { this.lastTimestamp = currentLine.timestamp; currentLine.namespace ||= 'default'; @@ -224,7 +216,7 @@ export class ApexLogParser { nextLine.parent = currentLine; currentLine.children.push(nextLine); - if (nextLine instanceof Method) { + if (nextLine.exitTypes.length) { this.parseTree(nextLine, lineIter, stack); } } @@ -260,7 +252,7 @@ export class ApexLogParser { } } - private isMatchingEnd(startMethod: Method, endLine: LogLine) { + private isMatchingEnd(startMethod: LogEvent, endLine: LogEvent) { return !!( endLine.type && startMethod.exitTypes.includes(endLine.type) && @@ -271,10 +263,10 @@ export class ApexLogParser { } private endMethod( - startMethod: Method, - endLine: LogLine, + startMethod: LogEvent, + endLine: LogEvent, lineIter: LineIterator, - stack: Method[], + stack: LogEvent[], ) { startMethod.exitStamp = endLine.timestamp; @@ -300,16 +292,17 @@ export class ApexLogParser { } } - private flattenByDepth(nodes: LogLine[]) { - const result = new Map(); + private flattenByDepth(nodes: LogEvent[]) { + const result = new Map(); let currentDepth = 0; + let currentNodes = nodes; let len = currentNodes.length; while (len) { result.set(currentDepth, currentNodes); - const children: LogLine[] = []; + const children: LogEvent[] = []; while (len--) { const node = currentNodes[len]; if (node?.children) { @@ -328,7 +321,7 @@ export class ApexLogParser { return result; } - private aggregateTotals(nodes: LogLine[]) { + private aggregateTotals(nodes: LogEvent[]) { const len = nodes.length; if (!len) { return; @@ -359,40 +352,41 @@ export class ApexLogParser { } } - private insertPackageWrappers(node: Method) { + private insertPackageWrappers(node: LogEvent) { const children = node.children; - let lastPkg: TimedNode | null = null; + let lastPkg: LogEvent | null = null; - const newChildren: LogLine[] = []; + const newChildren: LogEvent[] = []; const len = children.length; for (let i = 0; i < len; i++) { const child = children[i]; - if (child) { - const isPkgType = child.type === 'ENTERING_MANAGED_PKG'; - if (lastPkg && child instanceof TimedNode) { - if (isPkgType && child.namespace === lastPkg.namespace) { - // combine adjacent (like) packages - lastPkg.exitStamp = child.exitStamp || child.timestamp; - continue; // skip any more child processing (it's gone) - } else if (!isPkgType) { - // we are done merging adjacent `ENTERING_MANAGED_PKG` of the same namesapce - lastPkg.recalculateDurations(); - lastPkg = null; - } + if (!child) { + continue; + } + const isPkgType = child.type === 'ENTERING_MANAGED_PKG'; + if (lastPkg) { + if (isPkgType && child.namespace === lastPkg.namespace) { + // combine adjacent (like) packages + lastPkg.exitStamp = child.exitStamp || child.timestamp; + continue; // skip any more child processing (it's gone) + } else if (!isPkgType && child.exitStamp) { + // we are done merging adjacent `ENTERING_MANAGED_PKG` of the same namesapce + lastPkg.recalculateDurations(); + lastPkg = null; } + } - if (child instanceof Method) { - this.insertPackageWrappers(child); - } + if (child.exitTypes.length) { + this.insertPackageWrappers(child); + } - // It is a ENTERING_MANAGED_PKG line that does not match the last one - // or we have not come across a ENTERING_MANAGED_PKG line yet. - if (isPkgType) { - lastPkg?.recalculateDurations(); - lastPkg = child as TimedNode; - } - newChildren.push(child); + // It is a ENTERING_MANAGED_PKG line that does not match the last one + // or we have not come across a ENTERING_MANAGED_PKG line yet. + if (isPkgType) { + lastPkg?.recalculateDurations(); + lastPkg = child; } + newChildren.push(child); } lastPkg?.recalculateDurations(); @@ -441,11 +435,6 @@ export class ApexLogParser { } } -interface SelfTotal { - self: number; - total: number; -} - export class DebugLevel { logCategory: string; logLevel: string; @@ -457,2634 +446,21 @@ export class DebugLevel { } export class LineIterator { - next: LogLine | null; - lineGenerator: Generator; + next: LogEvent | null; + lineGenerator: Generator; - constructor(lineGenerator: Generator) { + constructor(lineGenerator: Generator) { this.lineGenerator = lineGenerator; this.next = this.lineGenerator.next().value; } - peek(): LogLine | null { + peek(): LogEvent | null { return this.next; } - fetch(): LogLine | null { + fetch(): LogEvent | null { const result = this.next; this.next = this.lineGenerator.next().value; return result; } } - -export interface LogIssue { - startTime?: number; - summary: string; - description: string; - type: IssueType; -} - -/** - * All log lines extend this base class. - */ -export abstract class LogLine { - logParser: ApexLogParser; - - // common metadata (available for all lines) - - parent: LogLine | null = null; - - /** - * All child nodes of the current node - */ - children: LogLine[] = []; - - /** - * The type of this log line from the log file e.g METHOD_ENTRY - */ - type: LogEventType | null = null; - - /** - * The full raw text of this log line - */ - logLine = ''; // the raw text of this log line - - /** - * A parsed version of the log line text useful for display in UIs - */ - text; - - // optional metadata - /** - * Should this log entry pull in following text lines (as the log entry can contain newlines)? - */ - acceptsText = false; - - /** - * Is this log entry generated by a declarative process? - */ - declarative = false; - /** - * Is a method exit line? - */ - isExit = false; - - /** - * Should the exitstamp be the timestamp of the next line? - * These kind of lines can not be used as exit lines for anything othe than other pseudo exits. - */ - nextLineIsExit = false; - - /** - * The line number within the containing class - */ - lineNumber: LineNumber = null; - - /** - * The package namespace associated with this log line - * @default default - */ - namespace: string | 'default' = ''; - - /** - * The variable value - */ - value: string | null = null; - - /** - * Could match to a corresponding symbol in a file in the workspace? - */ - hasValidSymbols = false; - - /** - * Extra description context - */ - suffix: string | null = null; - - /** - * Does this line cause a discontinuity in the call stack? e.g an exception causing stack unwinding - */ - discontinuity = false; - - /** - * The timestamp of this log line, in nanoseconds - */ - timestamp; - - /** - * The time spent. - */ - duration: SelfTotal = { - /** - * The net (wall) time spent in the node (when not inside children) - */ - self: 0, - /** - * The total (wall) time spent in the node - */ - total: 0, - }; - - /** - * Total + self row counts for DML - */ - dmlRowCount: SelfTotal = { - /** - * The net number of DML rows for this node, excluding child nodes - */ - self: 0, - /** - * The total number of DML rows for this node and child nodes - */ - total: 0, - }; - - /** - * Total + self row counts for SOQL - */ - soqlRowCount: SelfTotal = { - /** - * The net number of SOQL rows for this node, excluding child nodes - */ - self: 0, - /** - * The total number of SOQL rows for this node and child nodes - */ - total: 0, - }; - - /** - * Total + self row counts for SOSL - */ - soslRowCount: SelfTotal = { - /** - * The net number of SOSL rows for this node, excluding child nodes - */ - self: 0, - /** - * The total number of SOSL rows for this node and child nodes - */ - total: 0, - }; - - dmlCount: SelfTotal = { - /** - * The net number of DML operations (DML_BEGIN) in this node. - */ - self: 0, - /** - * The total number of DML operations (DML_BEGIN) in this node and child nodes - */ - total: 0, - }; - - soqlCount: SelfTotal = { - /** - * The net number of SOQL operations (SOQL_EXECUTE_BEGIN) in this node. - */ - self: 0, - /** - * The total number of SOQL operations (SOQL_EXECUTE_BEGIN) in this node and child nodes - */ - total: 0, - }; - - soslCount: SelfTotal = { - /** - * The net number of SOSL operations (SOSL_EXECUTE_BEGIN) in this node. - */ - self: 0, - /** - * The total number of SOSL operations (SOSL_EXECUTE_BEGIN) in this node and child nodes - */ - total: 0, - }; - - /** - * The total number of exceptions thrown (EXCEPTION_THROWN) in this node and child nodes - */ - totalThrownCount = 0; - - /** - * The line types which would legitimately end this method - */ - exitTypes: LogEventType[] = []; - - constructor(parser: ApexLogParser, parts: string[] | null) { - this.logParser = parser; - if (parts) { - const [timeData, type] = parts; - this.text = this.type = type as LogEventType; - this.timestamp = timeData ? this.parseTimestamp(timeData) : 0; - } else { - this.timestamp = 0; - this.text = ''; - } - } - - /** Called if a corresponding end event is found during tree parsing*/ - onEnd?(end: LogLine, stack: LogLine[]): void; - - /** Called when the Log event after this one is created in the line parser*/ - onAfter?(parser: ApexLogParser, next?: LogLine): void; - - private parseTimestamp(text: string): number { - const start = text.indexOf('('); - if (start !== -1) { - return Number(text.slice(start + 1, -1)); - } - throw new Error(`Unable to parse timestamp: '${text}'`); - } - - protected parseLineNumber(text: string | null | undefined): string | number { - switch (true) { - case text === '[EXTERNAL]': - return 'EXTERNAL'; - case !!text: { - const lineNumberStr = text.slice(1, -1); - if (lineNumberStr) { - return Number(lineNumberStr); - } - throw new Error(`Unable to parse line number: '${text}'`); - } - default: - return 0; - } - } -} - -class BasicLogLine extends LogLine {} -class BasicExitLine extends LogLine { - isExit = true; -} - -type CPUType = 'loading' | 'custom' | 'method' | 'free' | 'system' | 'pkg' | ''; - -/** - * Log lines extend this class if they have a duration (and hence can be shown on the timeline). - * There are no real children (as there is no exit line), but children can get reparented here... - */ -export class TimedNode extends LogLine { - /** - * The timestamp when the node finished, in nanoseconds - */ - exitStamp: number | null = null; - - /** - * The log sub category this event belongs to - */ - subCategory: LogSubCategory; - - /** - * The CPU type, e.g loading, method, custom - */ - cpuType: CPUType; // the category key to collect our cpu usage - - constructor( - parser: ApexLogParser, - parts: string[] | null, - timelineKey: LogSubCategory, - cpuType: CPUType, - ) { - super(parser, parts); - this.subCategory = timelineKey; - this.cpuType = cpuType; - } - - addChild(line: LogLine) { - this.children.push(line); - } - - recalculateDurations() { - if (this.exitStamp) { - this.duration.total = this.duration.self = this.exitStamp - this.timestamp; - } - } -} - -/** - * Log lines extend this class if they have a start-line and an end-line (and hence can have children in-between). - * - The start-line should extend "Method" and collect any children. - * - The end-line should extend "Detail" and terminate the method (also providing the "exitStamp"). - * The method will be rendered as "expandable" in the tree-view, if it has children. - */ -export class Method extends TimedNode { - /** - * Whether the log event was truncated when the log ended, e,g no matching end event - */ - isTruncated = false; - - constructor( - parser: ApexLogParser, - parts: string[] | null, - exitTypes: string[], - timelineKey: LogSubCategory, - cpuType: CPUType, - ) { - super(parser, parts, timelineKey, cpuType); - this.exitTypes = exitTypes as LogEventType[]; - } -} - -/** - * This class represents the single root node for the node tree. - * It is a "pseudo" node and not present in the log. - * Since it has children it extends "Method". - */ -export class ApexLog extends Method { - type = null; - text = 'LOG_ROOT'; - timestamp = 0; - exitStamp = 0; - /** - * The size of the log, in bytes - */ - public size = 0; - - /** - * The total CPU time consumed, in ms - */ - public cpuTime: number = 0; - - /** - * The Apex Debug Logging Levels for the current log - */ - public debugLevels: DebugLevel[] = []; - - /** - * All the namespaces that appear in this log. - */ - public namespaces: string[] = []; - - /** - * Any issues within the log, such as cpu time exceeded or max log size reached. - */ - public logIssues: LogIssue[] = []; - - /** - * Any issues that occurred during the parsing of the log, such as an unrecognized log event type. - */ - public parsingErrors: string[] = []; - - /** - * The endtime with nodes of 0 duration excluded - */ - executionEndTime = 0; - - constructor(parser: ApexLogParser) { - super(parser, null, [], 'Code Unit', ''); - } - - setTimes() { - this.timestamp = - this.children.find((child) => { - return child.timestamp; - })?.timestamp || 0; - // We do not just want to use the very last exitStamp because it could be CUMULATIVE_USAGE which is not really part of the code execution time but does have a later time. - let endTime; - const reverseLen = this.children.length - 1; - for (let i = reverseLen; i >= 0; i--) { - const child = this.children[i]; - // If there is no duration on a node then it is not going to be shown on the timeline anyway - if (child instanceof TimedNode && child.exitStamp) { - endTime ??= child.exitStamp; - if (child.duration) { - this.executionEndTime = child.exitStamp; - break; - } - } - endTime ??= child?.timestamp; - } - this.exitStamp = endTime || 0; - this.recalculateDurations(); - } -} - -export function parseObjectNamespace(text: string | null | undefined): string { - if (!text) { - return ''; - } - - const sep = text.indexOf('__'); - if (sep === -1) { - return 'default'; - } - return text.slice(0, sep); -} - -export function parseVfNamespace(text: string): string { - const sep = text.indexOf('__'); - if (sep === -1) { - return 'default'; - } - const firstSlash = text.indexOf('/'); - if (firstSlash === -1) { - return 'default'; - } - const secondSlash = text.indexOf('/', firstSlash + 1); - if (secondSlash < 0) { - return 'default'; - } - return text.substring(secondSlash + 1, sep); -} - -export function parseRows(text: string | null | undefined): number { - if (!text) { - return 0; - } - - const rowCount = text.slice(text.indexOf('Rows:') + 5); - if (rowCount) { - return Number(rowCount); - } - throw new Error(`Unable to parse row count: '${text}'`); -} - -/* Log line entry Parsers */ - -class BulkHeapAllocateLine extends LogLine { - logCategory: 'Apex Code'; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - this.logCategory = 'Apex Code'; - } -} - -class CalloutRequestLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[3]} : ${parts[2]}`; - } -} - -class CalloutResponseLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[3]} : ${parts[2]}`; - } -} -class NamedCredentialRequestLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[3]} : ${parts[4]} : ${parts[5]} : ${parts[6]}`; - } -} - -class NamedCredentialResponseLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]}`; - } -} - -class NamedCredentialResponseDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[3]} : ${parts[4]} ${parts[5]} : ${parts[6]} ${parts[7]}`; - } -} - -class ConstructorEntryLine extends Method { - hasValidSymbols = true; - suffix = ' (constructor)'; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['CONSTRUCTOR_EXIT'], 'Method', 'method'); - this.lineNumber = this.parseLineNumber(parts[2]); - const [, , , , args, className] = parts; - - this.text = className + (args ? args.substring(args.lastIndexOf('(')) : ''); - const possibleNS = this._parseConstructorNamespace(className || ''); - if (possibleNS) { - this.namespace = possibleNS; - } - } - - _parseConstructorNamespace(className: string): string { - let possibleNs = className.slice(0, className.indexOf('.')); - if (this.logParser.namespaces.has(possibleNs)) { - return possibleNs; - } - - const constructorParts = (className ?? '').split('.'); - possibleNs = constructorParts[0] || ''; - // inmner class with a namespace - if (constructorParts.length === 3) { - return possibleNs; - } - - return ''; - } -} - -class ConstructorExitLine extends LogLine { - isExit = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - } -} - -class EmailQueueLine extends LogLine { - acceptsText = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - } -} - -export class MethodEntryLine extends Method { - hasValidSymbols = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['METHOD_EXIT'], 'Method', 'method'); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = parts[4] || this.type || this.text; - if (this.text.indexOf('System.Type.forName(') !== -1) { - // assume we are not charged for class loading (or at least not lengthy remote-loading / compiling) - this.cpuType = 'loading'; - } else { - const possibleNs = this._parseMethodNamespace(parts[4]); - if (possibleNs) { - this.namespace = possibleNs; - } - } - } - - onEnd(end: MethodExitLine, _stack: LogLine[]): void { - if (end.namespace && !end.text.endsWith(')')) { - this.namespace = end.namespace; - } - } - - _parseMethodNamespace(methodName: string | undefined): string { - if (!methodName) { - return ''; - } - - const methodBracketIndex = methodName.indexOf('('); - if (methodBracketIndex === -1) { - return ''; - } - - const nsSeparator = methodName.indexOf('.'); - if (nsSeparator === -1) { - return ''; - } - - const possibleNs = methodName.slice(0, nsSeparator); - if (this.logParser.namespaces.has(possibleNs)) { - return possibleNs; - } - - const methodNameParts = methodName.slice(0, methodBracketIndex)?.split('.'); - if (methodNameParts.length === 4) { - return methodNameParts[0] ?? ''; - } else if (methodNameParts.length === 2) { - return 'default'; - } - - return ''; - } -} -class MethodExitLine extends LogLine { - isExit = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = parts[4] ?? parts[3] ?? this.text; - - /*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();*/ - // If does not end in ) then we have a reference to the class, either via outer or inner. - if (!this.text.endsWith(')')) { - // if there is a . the we have a namespace e.g ns.Outer - const index = this.text.indexOf('.'); - if (index !== -1) { - this.namespace = this.text.slice(0, index); - } - } - } -} - -class SystemConstructorEntryLine extends Method { - suffix = '(system constructor)'; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['SYSTEM_CONSTRUCTOR_EXIT'], 'System Method', 'method'); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = parts[3] || ''; - } -} - -class SystemConstructorExitLine extends LogLine { - isExit = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - } -} -class SystemMethodEntryLine extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['SYSTEM_METHOD_EXIT'], 'System Method', 'method'); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = parts[3] || ''; - } -} - -class SystemMethodExitLine extends LogLine { - isExit = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - } -} - -export class CodeUnitStartedLine extends Method { - suffix = ' (entrypoint)'; - codeUnitType = ''; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['CODE_UNIT_FINISHED'], 'Code Unit', 'custom'); - - const typeString = parts[5] || parts[4] || parts[3] || ''; - let sepIndex = typeString.indexOf(':'); - if (sepIndex === -1) { - sepIndex = typeString.indexOf('/'); - } - this.codeUnitType = sepIndex !== -1 ? typeString.slice(0, sepIndex) : ''; - - const name = parts[4] || parts[3] || this.codeUnitType || ''; - switch (this.codeUnitType) { - case 'EventService': - this.cpuType = 'method'; - this.namespace = parseObjectNamespace(typeString.slice(sepIndex + 1)); - this.text = name; - break; - case 'Validation': - this.cpuType = 'custom'; - this.declarative = true; - - this.text = name; - break; - case 'Workflow': - this.cpuType = 'custom'; - this.declarative = true; - this.text = name; - break; - case 'Flow': - this.cpuType = 'custom'; - this.declarative = true; - this.text = name; - break; - case 'VF': - this.cpuType = 'method'; - this.namespace = parseVfNamespace(name); - this.text = name; - break; - case 'apex': { - this.cpuType = 'method'; - const namespaceIndex = name.indexOf('.'); - this.namespace = - namespaceIndex !== -1 - ? name.slice(name.indexOf('apex://') + 7, namespaceIndex) - : 'default'; - this.text = name; - break; - } - case '__sfdc_trigger': { - this.cpuType = 'method'; - this.text = name || parts[4] || ''; - const triggerParts = parts[5]?.split('/') || ''; - this.namespace = triggerParts.length === 3 ? triggerParts[1] || 'default' : 'default'; - break; - } - default: { - this.cpuType = 'method'; - this.text = name; - const openBracket = name.lastIndexOf('('); - const methodName = - openBracket !== -1 ? name.slice(0, openBracket + 1).split('.') : name.split('.'); - if (methodName.length === 3 || (methodName.length === 2 && !methodName[1]?.endsWith('('))) { - this.namespace = methodName[0] || 'default'; - } - break; - } - } - - this.namespace ||= 'default'; - } -} -export class CodeUnitFinishedLine extends LogLine { - isExit = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class VFApexCallStartLine extends Method { - suffix = ' (VF APEX)'; - invalidClasses = [ - 'pagemessagescomponentcontroller', - 'pagemessagecomponentcontroller', - 'severitymessages', - ]; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['VF_APEX_CALL_END'], 'Method', 'method'); - this.lineNumber = this.parseLineNumber(parts[2]); - - const classText = parts[5] || parts[3] || ''; - let methodtext = parts[4] || ''; - if ( - !methodtext && - (!classText.includes(' ') || - this.invalidClasses.some((invalidCls: string) => - classText.toLowerCase().includes(invalidCls), - )) - ) { - // we have a system entry and they do not have exits - // e.g |VF_APEX_CALL_START|[EXTERNAL]|/apexpage/pagemessagescomponentcontroller.apex - // and they really mess with the logs so skip handling them. - this.exitTypes = []; - } else if (methodtext) { - this.hasValidSymbols = true; - // method call - const methodIndex = methodtext.indexOf('('); - const constructorIndex = methodtext.indexOf(''); - if (methodIndex > -1) { - // Method - methodtext = '.' + methodtext.substring(methodIndex).slice(1, -1) + '()'; - } else if (constructorIndex > -1) { - // Constructor - methodtext = methodtext.substring(constructorIndex + 6) + '()'; - } else { - // Property - methodtext = '.' + methodtext; - } - } else { - this.hasValidSymbols = true; - } - this.text = classText + methodtext; - } -} - -class VFApexCallEndLine extends LogLine { - isExit = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class VFDeserializeViewstateBeginLine extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['VF_DESERIALIZE_VIEWSTATE_END'], 'System Method', 'method'); - } -} - -class VFFormulaStartLine extends Method { - suffix = ' (VF FORMULA)'; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['VF_EVALUATE_FORMULA_END'], 'System Method', 'custom'); - this.text = parts[3] || ''; - } -} - -class VFFormulaEndLine extends LogLine { - isExit = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class VFSeralizeViewStateStartLine extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['VF_SERIALIZE_VIEWSTATE_END'], 'System Method', 'method'); - } -} - -class VFPageMessageLine extends LogLine { - acceptsText = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class DMLBeginLine extends Method { - dmlCount = { - self: 1, - total: 1, - }; - - namespace = 'default'; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['DML_END'], 'DML', 'free'); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = 'DML ' + parts[3] + ' ' + parts[4]; - const rowCountString = parts[5]; - this.dmlRowCount.total = this.dmlRowCount.self = rowCountString ? parseRows(rowCountString) : 0; - } -} - -class DMLEndLine extends LogLine { - isExit = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - } -} - -class IdeasQueryExecuteLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - } -} - -class SOQLExecuteBeginLine extends Method { - aggregations = 0; - soqlCount = { - self: 1, - total: 1, - }; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['SOQL_EXECUTE_END'], 'SOQL', 'free'); - this.lineNumber = this.parseLineNumber(parts[2]); - - const [, , , aggregations, soqlString] = parts; - - const aggregationText = aggregations || ''; - if (aggregationText) { - const aggregationIndex = aggregationText.indexOf('Aggregations:'); - this.aggregations = Number(aggregationText.slice(aggregationIndex + 13)); - } - this.text = soqlString || ''; - } - - onEnd(end: SOQLExecuteEndLine, _stack: LogLine[]): void { - this.soqlRowCount.total = this.soqlRowCount.self = end.soqlRowCount.total; - } -} - -class SOQLExecuteEndLine extends LogLine { - isExit = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.soqlRowCount.total = this.soqlRowCount.self = parseRows(parts[3] || ''); - } -} - -class SOQLExecuteExplainLine extends LogLine { - cardinality: number | null = null; // The estimated number of records that the leading operation type would return - fields: string[] | null = null; //The indexed field(s) used by the Query Optimizer. If the leading operation type is Index, the fields value is Index. Otherwise, the fields value is null. - leadingOperationType: string | null = null; // The primary operation type that Salesforce will use to optimize the query. - relativeCost: number | null = null; // The cost of the query compared to the Force.com Query Optimizer’s selectivity threshold. Values above 1 mean that the query won’t be selective. - sObjectCardinality: number | null = null; // The approximate record count for the queried object. - sObjectType: string | null = null; //T he name of the queried SObject - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - - const queryPlanDetails = parts[3] || ''; - this.text = queryPlanDetails; - - const queryplanParts = queryPlanDetails.split('],'); - if (queryplanParts.length > 1) { - const planExplain = queryplanParts[0] || ''; - const [cardinalityText, sobjCardinalityText, costText] = (queryplanParts[1] || '').split(','); - - const onIndex = planExplain.indexOf(' on'); - this.leadingOperationType = planExplain.slice(0, onIndex); - - const colonIndex = planExplain.indexOf(' :'); - this.sObjectType = planExplain.slice(onIndex + 4, colonIndex); - - // remove whitespace if there is any. we could have [ field1__c, field2__c ] - // I am not 100% sure of format when we have multiple fields so this is safer - const fieldsAsString = planExplain.slice(planExplain.indexOf('[') + 1).replace(/\s+/g, ''); - this.fields = fieldsAsString === '' ? [] : fieldsAsString.split(','); - - this.cardinality = cardinalityText - ? Number(cardinalityText.slice(cardinalityText.indexOf('cardinality: ') + 13)) - : null; - this.sObjectCardinality = sobjCardinalityText - ? Number( - sobjCardinalityText.slice(sobjCardinalityText.indexOf('sobjectCardinality: ') + 20), - ) - : null; - this.relativeCost = costText - ? Number(costText.slice(costText.indexOf('relativeCost ') + 13)) - : null; - } - } -} - -class SOSLExecuteBeginLine extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['SOSL_EXECUTE_END'], 'SOQL', 'free'); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = `SOSL: ${parts[3]}`; - - this.soslCount = { - self: 1, - total: 1, - }; - } - - onEnd(end: SOSLExecuteEndLine, _stack: LogLine[]): void { - this.soslRowCount.total = this.soslRowCount.self = end.soslRowCount.total; - } -} - -class SOSLExecuteEndLine extends LogLine { - isExit = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.soslRowCount.total = this.soslRowCount.self = parseRows(parts[3] || ''); - } -} - -class HeapAllocateLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = parts[3] || ''; - } -} - -class HeapDeallocateLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - } -} - -class StatementExecuteLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - } -} - -class VariableScopeBeginLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = parts.slice(3).join(' | '); - } -} - -class VariableAssignmentLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = parts.slice(3).join(' | '); - } -} -class UserInfoLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = parts[3] + ' ' + parts[4]; - } -} - -class UserDebugLine extends LogLine { - acceptsText = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = parts.slice(3).join(' | '); - } -} - -class CumulativeLimitUsageLine extends Method { - namespace = 'default'; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['CUMULATIVE_LIMIT_USAGE_END'], 'System Method', 'system'); - } -} - -class CumulativeProfilingLine extends LogLine { - acceptsText = true; - namespace = 'default'; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] + ' ' + (parts[3] ?? ''); - } -} - -class CumulativeProfilingBeginLine extends Method { - namespace = 'default'; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['CUMULATIVE_PROFILING_END'], 'System Method', 'custom'); - } -} - -class LimitUsageLine extends LogLine { - namespace = 'default'; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = parts[3] + ' ' + parts[4] + ' out of ' + parts[5]; - } -} - -class LimitUsageForNSLine extends LogLine { - acceptsText = true; - namespace = 'default'; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } - - onAfter(parser: ApexLogParser, _next?: LogLine): void { - const matched = this.text.match(/Maximum CPU time: (\d+)/), - cpuText = matched?.[1] || '0', - cpuTime = parseInt(cpuText, 10) * 1000000; // convert from milli-seconds to nano-seconds - - if (!parser.cpuUsed || cpuTime > parser.cpuUsed) { - parser.cpuUsed = cpuTime; - } - } -} - -class NBANodeBegin extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['NBA_NODE_END'], 'System Method', 'method'); - this.text = parts.slice(2).join(' | '); - } -} - -class NBANodeDetail extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts.slice(2).join(' | '); - } -} -class NBANodeEnd extends LogLine { - isExit = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts.slice(2).join(' | '); - } -} -class NBANodeError extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts.slice(2).join(' | '); - } -} -class NBAOfferInvalid extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts.slice(2).join(' | '); - } -} -class NBAStrategyBegin extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['NBA_STRATEGY_END'], 'System Method', 'method'); - this.text = parts.slice(2).join(' | '); - } -} -class NBAStrategyEnd extends LogLine { - isExit = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts.slice(2).join(' | '); - } -} -class NBAStrategyError extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts.slice(2).join(' | '); - } -} - -class PushTraceFlagsLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = parts[4] + ', line:' + this.lineNumber + ' - ' + parts[5]; - } -} - -class PopTraceFlagsLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = parts[4] + ', line:' + this.lineNumber + ' - ' + parts[5]; - } -} - -class QueryMoreBeginLine extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['QUERY_MORE_END'], 'SOQL', 'custom'); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = `line: ${this.lineNumber}`; - } -} - -class QueryMoreEndLine extends LogLine { - isExit = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = `line: ${this.lineNumber}`; - } -} -class QueryMoreIterationsLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = `line: ${this.lineNumber}, iterations:${parts[3]}`; - } -} - -class SavepointRollbackLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = `${parts[3]}, line: ${this.lineNumber}`; - } -} - -class SavePointSetLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = `${parts[3]}, line: ${this.lineNumber}`; - } -} - -class TotalEmailRecipientsQueuedLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class StackFrameVariableListLine extends LogLine { - acceptsText = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - } -} - -class StaticVariableListLine extends LogLine { - acceptsText = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - } -} - -// This looks like a method, but the exit line is often missing... -class SystemModeEnterLine extends LogLine { - // namespace = "system"; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class SystemModeExitLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -export class ExecutionStartedLine extends Method { - namespace = 'default'; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['EXECUTION_FINISHED'], 'Method', 'method'); - } -} - -class EnteringManagedPackageLine extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, [], 'Method', 'pkg'); - const rawNs = parts[2] || '', - lastDot = rawNs.lastIndexOf('.'); - - this.text = this.namespace = lastDot < 0 ? rawNs : rawNs.substring(lastDot + 1); - } - - onAfter(parser: ApexLogParser, end?: LogLine): void { - if (end) { - this.exitStamp = end.timestamp; - } - } -} - -class EventSericePubBeginLine extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['EVENT_SERVICE_PUB_END'], 'Flow', 'custom'); - this.text = parts[2] || ''; - } -} - -class EventSericePubEndLine extends LogLine { - isExit = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class EventSericePubDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] + ' ' + parts[3] + ' ' + parts[4]; - } -} - -class EventSericeSubBeginLine extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['EVENT_SERVICE_SUB_END'], 'Flow', 'custom'); - this.text = `${parts[2]} ${parts[3]}`; - } -} - -class EventSericeSubEndLine extends LogLine { - isExit = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} ${parts[3]}`; - } -} - -class EventSericeSubDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} ${parts[3]} ${parts[4]} ${parts[6]} ${parts[6]}`; - } -} - -export class FlowStartInterviewsBeginLine extends Method { - declarative = true; - text = 'FLOW_START_INTERVIEWS : '; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['FLOW_START_INTERVIEWS_END'], 'Flow', 'custom'); - } - - onEnd(end: LogLine, stack: LogLine[]) { - const flowType = this.getFlowType(stack); - this.suffix = ` (${flowType})`; - this.text += this.getFlowName(); - } - - getFlowType(stack: LogLine[]) { - let flowType; - // ignore the last one on stack is it will be this FlowStartInterviewsBeginLine - const len = stack.length - 2; - for (let i = len; i >= 0; i--) { - const elem = stack[i]; - // type = "CODE_UNIT_STARTED" a flow or Processbuilder was started directly - // type = "FLOW_START_INTERVIEWS_BEGIN" a flow was started from a process builder - if (elem instanceof CodeUnitStartedLine) { - flowType = elem.codeUnitType === 'Flow' ? 'Flow' : 'Process Builder'; - break; - } else if (elem && elem.type === 'FLOW_START_INTERVIEWS_BEGIN') { - flowType = 'Flow'; - break; - } - } - return flowType || ''; - } - - getFlowName() { - if (this.children.length) { - return this.children[0]?.text || ''; - } - return ''; - } -} - -class FlowStartInterviewsErrorLine extends LogLine { - acceptsText = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} - ${parts[4]}`; - } -} - -class FlowStartInterviewBeginLine extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['FLOW_START_INTERVIEW_END'], 'Flow', 'custom'); - this.text = parts[3] || ''; - } -} - -class FlowStartInterviewLimitUsageLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class FlowStartScheduledRecordsLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]}`; - } -} - -class FlowCreateInterviewErrorLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]}`; - } -} - -class FlowElementBeginLine extends Method { - declarative = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['FLOW_ELEMENT_END'], 'Flow', 'custom'); - this.text = parts[3] + ' ' + parts[4]; - } -} - -class FlowElementDeferredLine extends LogLine { - declarative = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] + ' ' + parts[3]; - } -} - -class FlowElementAssignmentLine extends LogLine { - declarative = true; - acceptsText = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[3] + ' ' + parts[4]; - } -} - -class FlowWaitEventResumingDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]}`; - } -} - -class FlowWaitEventWaitingDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]} : ${parts[6]}`; - } -} - -class FlowWaitResumingDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; - } -} - -class FlowWaitWaitingDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]}`; - } -} - -class FlowInterviewFinishedLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[3] || ''; - } -} - -class FlowInterviewResumedLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]}`; - } -} - -class FlowInterviewPausedLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; - } -} - -class FlowElementErrorLine extends LogLine { - acceptsText = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[1] || '' + parts[2] + ' ' + parts[3] + ' ' + parts[4]; - } -} - -class FlowElementFaultLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; - } -} - -class FlowElementLimitUsageLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]}`; - } -} - -class FlowInterviewFinishedLimitUsageLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]}`; - } -} - -class FlowSubflowDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]}`; - } -} - -class FlowActionCallDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[3] + ' : ' + parts[4] + ' : ' + parts[5] + ' : ' + parts[6]; - } -} - -class FlowAssignmentDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[3] + ' : ' + parts[4] + ' : ' + parts[5]; - } -} - -class FlowLoopDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[3] + ' : ' + parts[4]; - } -} - -class FlowRuleDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[3] + ' : ' + parts[4]; - } -} - -class FlowBulkElementBeginLine extends Method { - declarative = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['FLOW_BULK_ELEMENT_END'], 'Flow', 'custom'); - this.text = `${parts[2]} - ${parts[3]}`; - } -} - -class FlowBulkElementDetailLine extends LogLine { - declarative = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] + ' : ' + parts[3] + ' : ' + parts[4]; - } -} - -class FlowBulkElementNotSupportedLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; - } -} - -class FlowBulkElementLimitUsageLine extends LogLine { - declarative = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class PNInvalidAppLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]}.${parts[3]}`; - } -} - -class PNInvalidCertificateLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]}.${parts[3]}`; - } -} -class PNInvalidNotificationLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]}.${parts[3]} : ${parts[4]} : ${parts[5]} : ${parts[6]} : ${parts[7]} : ${parts[8]}`; - } -} -class PNNoDevicesLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]}.${parts[3]}`; - } -} - -class PNSentLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]}.${parts[3]} : ${parts[4]} : ${parts[5]} : ${parts[6]} : ${parts[7]}`; - } -} - -class SLAEndLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]} : ${parts[6]}`; - } -} - -class SLAEvalMilestoneLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]}`; - } -} - -class SLAProcessCaseLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]}`; - } -} - -class TestingLimitsLine extends LogLine { - acceptsText = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - } -} - -class ValidationRuleLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[3] || ''; - } -} - -class ValidationErrorLine extends LogLine { - acceptsText = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class ValidationFormulaLine extends LogLine { - acceptsText = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - const extra = parts.length > 3 ? ' ' + parts[3] : ''; - - this.text = parts[2] + extra; - } -} - -class ValidationPassLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[3] || ''; - } -} - -class WFFlowActionErrorLine extends LogLine { - acceptsText = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[1] + ' ' + parts[4]; - } -} - -class WFFlowActionErrorDetailLine extends LogLine { - acceptsText = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[1] + ' ' + parts[2]; - } -} - -class WFFieldUpdateLine extends Method { - isExit = true; - nextLineIsExit = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['WF_FIELD_UPDATE'], 'Workflow', 'custom'); - this.text = ' ' + parts[2] + ' ' + parts[3] + ' ' + parts[4] + ' ' + parts[5] + ' ' + parts[6]; - } -} - -class WFRuleEvalBeginLine extends Method { - declarative = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['WF_RULE_EVAL_END'], 'Workflow', 'custom'); - this.text = parts[2] || ''; - } -} - -class WFRuleEvalValueLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class WFRuleFilterLine extends LogLine { - acceptsText = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class WFCriteriaBeginLine extends Method { - declarative = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['WF_CRITERIA_END', 'WF_RULE_NOT_EVALUATED'], 'Workflow', 'custom'); - this.text = 'WF_CRITERIA : ' + parts[5] + ' : ' + parts[3]; - } -} - -class WFFormulaLine extends Method { - acceptsText = true; - isExit = true; - nextLineIsExit = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['WF_FORMULA'], 'Workflow', 'custom'); - this.text = parts[2] + ' : ' + parts[3]; - } -} - -class WFActionLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class WFActionsEndLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class WFActionTaskLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]} : ${parts[6]} : ${parts[7]}`; - } -} - -class WFApprovalLine extends Method { - isExit = true; - nextLineIsExit = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['WF_APPROVAL'], 'Workflow', 'custom'); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; - } -} - -class WFApprovalRemoveLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]}`; - } -} - -class WFApprovalSubmitLine extends Method { - isExit = true; - nextLineIsExit = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['WF_APPROVAL_SUBMIT'], 'Workflow', 'custom'); - this.text = `${parts[2]}`; - } -} - -class WFApprovalSubmitterLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; - } -} - -class WFAssignLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]}`; - } -} - -class WFEmailAlertLine extends Method { - isExit = true; - nextLineIsExit = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['WF_EMAIL_ALERT'], 'Workflow', 'custom'); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; - } -} - -class WFEmailSentLine extends Method { - isExit = true; - nextLineIsExit = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['WF_EMAIL_SENT'], 'Workflow', 'custom'); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; - } -} - -class WFEnqueueActionsLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class WFEscalationActionLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]}`; - } -} - -class WFEvalEntryCriteriaLine extends Method { - isExit = true; - nextLineIsExit = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['WF_EVAL_ENTRY_CRITERIA'], 'Workflow', 'custom'); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; - } -} - -class WFFlowActionDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - const optional = parts[4] ? ` : ${parts[4]} :${parts[5]}` : ''; - this.text = `${parts[2]} : ${parts[3]}` + optional; - } -} - -class WFNextApproverLine extends Method { - isExit = true; - nextLineIsExit = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['WF_NEXT_APPROVER'], 'Workflow', 'custom'); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; - } -} - -class WFOutboundMsgLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]}`; - } -} - -class WFProcessFoundLine extends Method { - isExit = true; - nextLineIsExit = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['WF_PROCESS_FOUND'], 'Workflow', 'custom'); - this.text = `${parts[2]} : ${parts[3]}`; - } -} - -class WFProcessNode extends Method { - isExit = true; - nextLineIsExit = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['WF_PROCESS_NODE'], 'Workflow', 'custom'); - this.text = parts[2] || ''; - } -} - -class WFReassignRecordLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]}`; - } -} - -class WFResponseNotifyLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]}`; - } -} - -class WFRuleEntryOrderLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class WFRuleInvocationLine extends Method { - isExit = true; - nextLineIsExit = true; - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['WF_RULE_INVOCATION'], 'Workflow', 'custom'); - this.text = parts[2] || ''; - } -} - -class WFSoftRejectLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class WFTimeTriggerLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]}`; - } -} - -class WFSpoolActionBeginLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class ExceptionThrownLine extends LogLine { - discontinuity = true; - acceptsText = true; - totalThrownCount = 1; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.lineNumber = this.parseLineNumber(parts[2]); - this.text = parts[3] || ''; - } - - onAfter(parser: ApexLogParser, _next?: LogLine): void { - if (this.text.indexOf('System.LimitException') >= 0) { - const isMultiLine = this.text.indexOf('\n'); - const len = isMultiLine < 0 ? 99 : isMultiLine; - const truncateText = this.text.length > len; - const summary = this.text.slice(0, len + 1) + (truncateText ? '…' : ''); - const message = truncateText ? this.text : ''; - parser.addLogIssue(this.timestamp, summary, message, 'error'); - } - } -} - -class FatalErrorLine extends LogLine { - acceptsText = true; - hideable = false; - discontinuity = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } - - onAfter(parser: ApexLogParser, _next?: LogLine): void { - const newLineIndex = this.text.indexOf('\n'); - const summary = newLineIndex > -1 ? this.text.slice(0, newLineIndex + 1) : this.text; - const detailText = summary.length !== this.text.length ? this.text : ''; - parser.addLogIssue(this.timestamp, 'FATAL ERROR! cause=' + summary, detailText, 'error'); - } -} - -class XDSDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class XDSResponseLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]} : ${parts[6]}`; - } -} -class XDSResponseDetailLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -class XDSResponseErrorLine extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -// e.g. "09:45:31.888 (38889007737)|DUPLICATE_DETECTION_BEGIN" -class DuplicateDetectionBegin extends Method { - declarative = true; - - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['DUPLICATE_DETECTION_END'], 'Workflow', 'custom'); - } -} - -// e.g. "09:45:31.888 (38889067408)|DUPLICATE_DETECTION_RULE_INVOCATION|DuplicateRuleId:0Bm20000000CaSP|DuplicateRuleName:Duplicate Account|DmlType:UPDATE" -class DuplicateDetectionRule extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = `${parts[3]} - ${parts[4]}`; - } -} - -/** - * NOTE: These can be found in the org on the create new debug level page but are not found in the docs here - * https://help.salesforce.com/s/articleView?id=sf.code_setting_debug_log_levels.htm - */ -class BulkDMLEntry extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts[2] || ''; - } -} - -/** - * DUPLICATE_DETECTION_MATCH_INVOCATION_DETAILS|EntityType:Account|ActionTaken:Allow_[Alert,Report]|DuplicateRecordIds: - */ -class DuplicateDetectionDetails extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts.slice(2).join(' | '); - } -} - -/** - * DUPLICATE_DETECTION_MATCH_INVOCATION_SUMMARY|EntityType:Account|NumRecordsToBeSaved:200|NumRecordsToBeSavedWithDuplicates:0|NumDuplicateRecordsFound:0 - */ -class DuplicateDetectionSummary extends LogLine { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts); - this.text = parts.slice(2).join(' | '); - } -} - -class SessionCachePutBegin extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['SESSION_CACHE_PUT_END'], 'Method', 'method'); - } -} -class SessionCacheGetBegin extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['SESSION_CACHE_GET_END'], 'Method', 'method'); - } -} - -class SessionCacheRemoveBegin extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['SESSION_CACHE_REMOVE_END'], 'Method', 'method'); - } -} - -class OrgCachePutBegin extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['ORG_CACHE_PUT_END'], 'Method', 'method'); - } -} - -class OrgCacheGetBegin extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['ORG_CACHE_GET_END'], 'Method', 'method'); - } -} - -class OrgCacheRemoveBegin extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['ORG_CACHE_REMOVE_END'], 'Method', 'method'); - } -} - -class VFSerializeContinuationStateBegin extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['VF_SERIALIZE_CONTINUATION_STATE_END'], 'Method', 'method'); - } -} - -class VFDeserializeContinuationStateBegin extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['VF_SERIALIZE_CONTINUATION_STATE_END'], 'Method', 'method'); - } -} - -class MatchEngineBegin extends Method { - constructor(parser: ApexLogParser, parts: string[]) { - super(parser, parts, ['MATCH_ENGINE_END'], 'Method', 'method'); - } -} - -function getLogEventClass(eventName: LogEventType): LogLineConstructor | null | undefined { - if (!eventName) { - return null; - } - - // Fast path for the most commonly occuring types - switch (eventName) { - case 'METHOD_ENTRY': - return MethodEntryLine; - - case 'METHOD_EXIT': - return MethodExitLine; - - case 'CONSTRUCTOR_ENTRY': - return ConstructorEntryLine; - - case 'CONSTRUCTOR_EXIT': - return ConstructorExitLine; - - default: - break; - } - - // Handle all other types - const logType = lineTypeMap.get(eventName); - if (logType) { - return logType; - } else if (basicLogEvents.indexOf(eventName) !== -1) { - return BasicLogLine; - } else if (basicExitLogEvents.indexOf(eventName) !== -1) { - return BasicExitLine; - } - - return null; -} - -type LogLineConstructor = new ( - parser: ApexLogParser, - parts: string[], -) => T; -export const lineTypeMap: ReadonlyMap = new Map< - LogEventType, - LogLineConstructor ->([ - ['BULK_DML_RETRY', BulkDMLEntry], - ['BULK_HEAP_ALLOCATE', BulkHeapAllocateLine], - ['CALLOUT_REQUEST', CalloutRequestLine], - ['CALLOUT_RESPONSE', CalloutResponseLine], - ['NAMED_CREDENTIAL_REQUEST', NamedCredentialRequestLine], - ['NAMED_CREDENTIAL_RESPONSE', NamedCredentialResponseLine], - ['NAMED_CREDENTIAL_RESPONSE_DETAIL', NamedCredentialResponseDetailLine], - ['CONSTRUCTOR_ENTRY', ConstructorEntryLine], - ['CONSTRUCTOR_EXIT', ConstructorExitLine], - ['EMAIL_QUEUE', EmailQueueLine], - ['METHOD_ENTRY', MethodEntryLine], - ['METHOD_EXIT', MethodExitLine], - ['SYSTEM_CONSTRUCTOR_ENTRY', SystemConstructorEntryLine], - ['SYSTEM_CONSTRUCTOR_EXIT', SystemConstructorExitLine], - ['SYSTEM_METHOD_ENTRY', SystemMethodEntryLine], - ['SYSTEM_METHOD_EXIT', SystemMethodExitLine], - ['CODE_UNIT_STARTED', CodeUnitStartedLine], - ['CODE_UNIT_FINISHED', CodeUnitFinishedLine], - ['VF_APEX_CALL_START', VFApexCallStartLine], - ['VF_APEX_CALL_END', VFApexCallEndLine], - ['VF_DESERIALIZE_VIEWSTATE_BEGIN', VFDeserializeViewstateBeginLine], - ['VF_EVALUATE_FORMULA_BEGIN', VFFormulaStartLine], - ['VF_EVALUATE_FORMULA_END', VFFormulaEndLine], - ['VF_SERIALIZE_CONTINUATION_STATE_BEGIN', VFSerializeContinuationStateBegin], - ['VF_DESERIALIZE_CONTINUATION_STATE_BEGIN', VFDeserializeContinuationStateBegin], - ['VF_SERIALIZE_VIEWSTATE_BEGIN', VFSeralizeViewStateStartLine], - ['VF_PAGE_MESSAGE', VFPageMessageLine], - ['DML_BEGIN', DMLBeginLine], - ['DML_END', DMLEndLine], - ['IDEAS_QUERY_EXECUTE', IdeasQueryExecuteLine], - ['SOQL_EXECUTE_BEGIN', SOQLExecuteBeginLine], - ['SOQL_EXECUTE_END', SOQLExecuteEndLine], - ['SOQL_EXECUTE_EXPLAIN', SOQLExecuteExplainLine], - ['SOSL_EXECUTE_BEGIN', SOSLExecuteBeginLine], - ['SOSL_EXECUTE_END', SOSLExecuteEndLine], - ['HEAP_ALLOCATE', HeapAllocateLine], - ['HEAP_DEALLOCATE', HeapDeallocateLine], - ['STATEMENT_EXECUTE', StatementExecuteLine], - ['VARIABLE_SCOPE_BEGIN', VariableScopeBeginLine], - ['VARIABLE_ASSIGNMENT', VariableAssignmentLine], - ['USER_INFO', UserInfoLine], - ['USER_DEBUG', UserDebugLine], - ['CUMULATIVE_LIMIT_USAGE', CumulativeLimitUsageLine], - ['CUMULATIVE_PROFILING', CumulativeProfilingLine], - ['CUMULATIVE_PROFILING_BEGIN', CumulativeProfilingBeginLine], - ['LIMIT_USAGE', LimitUsageLine], - ['LIMIT_USAGE_FOR_NS', LimitUsageForNSLine], - ['NBA_NODE_BEGIN', NBANodeBegin], - ['NBA_NODE_DETAIL', NBANodeDetail], - ['NBA_NODE_END', NBANodeEnd], - ['NBA_NODE_ERROR', NBANodeError], - ['NBA_OFFER_INVALID', NBAOfferInvalid], - ['NBA_STRATEGY_BEGIN', NBAStrategyBegin], - ['NBA_STRATEGY_END', NBAStrategyEnd], - ['NBA_STRATEGY_ERROR', NBAStrategyError], - ['POP_TRACE_FLAGS', PopTraceFlagsLine], - ['PUSH_TRACE_FLAGS', PushTraceFlagsLine], - ['QUERY_MORE_BEGIN', QueryMoreBeginLine], - ['QUERY_MORE_END', QueryMoreEndLine], - ['QUERY_MORE_ITERATIONS', QueryMoreIterationsLine], - ['TOTAL_EMAIL_RECIPIENTS_QUEUED', TotalEmailRecipientsQueuedLine], - ['SAVEPOINT_ROLLBACK', SavepointRollbackLine], - ['SAVEPOINT_SET', SavePointSetLine], - ['STACK_FRAME_VARIABLE_LIST', StackFrameVariableListLine], - ['STATIC_VARIABLE_LIST', StaticVariableListLine], - ['SYSTEM_MODE_ENTER', SystemModeEnterLine], - ['SYSTEM_MODE_EXIT', SystemModeExitLine], - ['EXECUTION_STARTED', ExecutionStartedLine], - ['ENTERING_MANAGED_PKG', EnteringManagedPackageLine], - ['EVENT_SERVICE_PUB_BEGIN', EventSericePubBeginLine], - ['EVENT_SERVICE_PUB_END', EventSericePubEndLine], - ['EVENT_SERVICE_PUB_DETAIL', EventSericePubDetailLine], - ['EVENT_SERVICE_SUB_BEGIN', EventSericeSubBeginLine], - ['EVENT_SERVICE_SUB_DETAIL', EventSericeSubDetailLine], - ['EVENT_SERVICE_SUB_END', EventSericeSubEndLine], - ['FLOW_START_INTERVIEWS_BEGIN', FlowStartInterviewsBeginLine], - ['FLOW_START_INTERVIEWS_ERROR', FlowStartInterviewsErrorLine], - ['FLOW_START_INTERVIEW_BEGIN', FlowStartInterviewBeginLine], - ['FLOW_START_INTERVIEW_LIMIT_USAGE', FlowStartInterviewLimitUsageLine], - ['FLOW_START_SCHEDULED_RECORDS', FlowStartScheduledRecordsLine], - ['FLOW_CREATE_INTERVIEW_ERROR', FlowCreateInterviewErrorLine], - ['FLOW_ELEMENT_BEGIN', FlowElementBeginLine], - ['FLOW_ELEMENT_DEFERRED', FlowElementDeferredLine], - ['FLOW_ELEMENT_ERROR', FlowElementErrorLine], - ['FLOW_ELEMENT_FAULT', FlowElementFaultLine], - ['FLOW_ELEMENT_LIMIT_USAGE', FlowElementLimitUsageLine], - ['FLOW_INTERVIEW_FINISHED_LIMIT_USAGE', FlowInterviewFinishedLimitUsageLine], - ['FLOW_SUBFLOW_DETAIL', FlowSubflowDetailLine], - ['FLOW_VALUE_ASSIGNMENT', FlowElementAssignmentLine], - ['FLOW_WAIT_EVENT_RESUMING_DETAIL', FlowWaitEventResumingDetailLine], - ['FLOW_WAIT_EVENT_WAITING_DETAIL', FlowWaitEventWaitingDetailLine], - ['FLOW_WAIT_RESUMING_DETAIL', FlowWaitResumingDetailLine], - ['FLOW_WAIT_WAITING_DETAIL', FlowWaitWaitingDetailLine], - ['FLOW_INTERVIEW_FINISHED', FlowInterviewFinishedLine], - ['FLOW_INTERVIEW_PAUSED', FlowInterviewPausedLine], - ['FLOW_INTERVIEW_RESUMED', FlowInterviewResumedLine], - ['FLOW_ACTIONCALL_DETAIL', FlowActionCallDetailLine], - ['FLOW_ASSIGNMENT_DETAIL', FlowAssignmentDetailLine], - ['FLOW_LOOP_DETAIL', FlowLoopDetailLine], - ['FLOW_RULE_DETAIL', FlowRuleDetailLine], - ['FLOW_BULK_ELEMENT_BEGIN', FlowBulkElementBeginLine], - ['FLOW_BULK_ELEMENT_DETAIL', FlowBulkElementDetailLine], - ['FLOW_BULK_ELEMENT_LIMIT_USAGE', FlowBulkElementLimitUsageLine], - ['FLOW_BULK_ELEMENT_NOT_SUPPORTED', FlowBulkElementNotSupportedLine], - ['MATCH_ENGINE_BEGIN', MatchEngineBegin], - ['ORG_CACHE_PUT_BEGIN', OrgCachePutBegin], - ['ORG_CACHE_GET_BEGIN', OrgCacheGetBegin], - ['ORG_CACHE_REMOVE_BEGIN', OrgCacheRemoveBegin], - ['PUSH_NOTIFICATION_INVALID_APP', PNInvalidAppLine], - ['PUSH_NOTIFICATION_INVALID_CERTIFICATE', PNInvalidCertificateLine], - ['PUSH_NOTIFICATION_INVALID_NOTIFICATION', PNInvalidNotificationLine], - ['PUSH_NOTIFICATION_NO_DEVICES', PNNoDevicesLine], - ['PUSH_NOTIFICATION_SENT', PNSentLine], - ['SESSION_CACHE_PUT_BEGIN', SessionCachePutBegin], - ['SESSION_CACHE_GET_BEGIN', SessionCacheGetBegin], - ['SESSION_CACHE_REMOVE_BEGIN', SessionCacheRemoveBegin], - ['SLA_END', SLAEndLine], - ['SLA_EVAL_MILESTONE', SLAEvalMilestoneLine], - ['SLA_PROCESS_CASE', SLAProcessCaseLine], - ['TESTING_LIMITS', TestingLimitsLine], - ['VALIDATION_ERROR', ValidationErrorLine], - ['VALIDATION_FORMULA', ValidationFormulaLine], - ['VALIDATION_PASS', ValidationPassLine], - ['VALIDATION_RULE', ValidationRuleLine], - ['WF_FLOW_ACTION_ERROR', WFFlowActionErrorLine], - ['WF_FLOW_ACTION_ERROR_DETAIL', WFFlowActionErrorDetailLine], - ['WF_FIELD_UPDATE', WFFieldUpdateLine], - ['WF_RULE_EVAL_BEGIN', WFRuleEvalBeginLine], - ['WF_RULE_EVAL_VALUE', WFRuleEvalValueLine], - ['WF_RULE_FILTER', WFRuleFilterLine], - ['WF_CRITERIA_BEGIN', WFCriteriaBeginLine], - ['WF_FORMULA', WFFormulaLine], - ['WF_ACTION', WFActionLine], - ['WF_ACTIONS_END', WFActionsEndLine], - ['WF_ACTION_TASK', WFActionTaskLine], - ['WF_APPROVAL', WFApprovalLine], - ['WF_APPROVAL_REMOVE', WFApprovalRemoveLine], - ['WF_APPROVAL_SUBMIT', WFApprovalSubmitLine], - ['WF_APPROVAL_SUBMITTER', WFApprovalSubmitterLine], - ['WF_ASSIGN', WFAssignLine], - ['WF_EMAIL_ALERT', WFEmailAlertLine], - ['WF_EMAIL_SENT', WFEmailSentLine], - ['WF_ENQUEUE_ACTIONS', WFEnqueueActionsLine], - ['WF_ESCALATION_ACTION', WFEscalationActionLine], - ['WF_EVAL_ENTRY_CRITERIA', WFEvalEntryCriteriaLine], - ['WF_FLOW_ACTION_DETAIL', WFFlowActionDetailLine], - ['WF_NEXT_APPROVER', WFNextApproverLine], - ['WF_OUTBOUND_MSG', WFOutboundMsgLine], - ['WF_PROCESS_FOUND', WFProcessFoundLine], - ['WF_PROCESS_NODE', WFProcessNode], - ['WF_REASSIGN_RECORD', WFReassignRecordLine], - ['WF_RESPONSE_NOTIFY', WFResponseNotifyLine], - ['WF_RULE_ENTRY_ORDER', WFRuleEntryOrderLine], - ['WF_RULE_INVOCATION', WFRuleInvocationLine], - ['WF_SOFT_REJECT', WFSoftRejectLine], - ['WF_SPOOL_ACTION_BEGIN', WFSpoolActionBeginLine], - ['WF_TIME_TRIGGER', WFTimeTriggerLine], - ['EXCEPTION_THROWN', ExceptionThrownLine], - ['FATAL_ERROR', FatalErrorLine], - ['XDS_DETAIL', XDSDetailLine], - ['XDS_RESPONSE', XDSResponseLine], - ['XDS_RESPONSE_DETAIL', XDSResponseDetailLine], - ['XDS_RESPONSE_ERROR', XDSResponseErrorLine], - ['DUPLICATE_DETECTION_BEGIN', DuplicateDetectionBegin], - ['DUPLICATE_DETECTION_RULE_INVOCATION', DuplicateDetectionRule], - ['DUPLICATE_DETECTION_MATCH_INVOCATION_DETAILS', DuplicateDetectionDetails], - ['DUPLICATE_DETECTION_MATCH_INVOCATION_SUMMARY', DuplicateDetectionSummary], -]); - -const basicLogEvents: LogEventType[] = [ - 'BULK_COUNTABLE_STATEMENT_EXECUTE', - 'TEMPLATE_PROCESSING_ERROR', - 'EXTERNAL_SERVICE_REQUEST', - 'FLOW_CREATE_INTERVIEW_BEGIN', - 'FLOW_CREATE_INTERVIEW_END', - 'VARIABLE_SCOPE_END', - 'PUSH_NOTIFICATION_NOT_ENABLED', - 'SLA_NULL_START_DATE', - 'TEMPLATE_PROCESSING_ERROR', - 'VALIDATION_FAIL', - `WF_FLOW_ACTION_BEGIN`, - 'WF_FLOW_ACTION_END', - 'WF_ESCALATION_RULE', - 'WF_HARD_REJECT', - 'WF_NO_PROCESS_FOUND', - 'WF_TIME_TRIGGERS_BEGIN', - 'WF_KNOWLEDGE_ACTION', - 'WF_SEND_ACTION', - 'WAVE_APP_LIFECYCLE', - 'WF_QUICK_CREATE', - 'WF_APEX_ACTION', - 'INVOCABLE_ACTION_DETAIL', - 'INVOCABLE_ACTION_ERROR', - 'FLOW_COLLECTION_PROCESSOR_DETAIL', - 'FLOW_SCHEDULED_PATH_QUEUED', - 'ROUTE_WORK_ACTION', - 'ADD_SKILL_REQUIREMENT_ACTION', - 'ADD_SCREEN_POP_ACTION', - 'CALLOUT_REQUEST_PREPARE', - 'CALLOUT_REQUEST_FINALIZE', - 'FUNCTION_INVOCATION_REQUEST', - 'APP_CONTAINER_INITIATED', - 'FUNCTION_INVOCATION_RESPONSE', - 'XDS_REQUEST_DETAIL', - 'EXTERNAL_SERVICE_RESPONSE', - 'DATAWEAVE_USER_DEBUG', - 'USER_DEBUG_FINER', - 'USER_DEBUG_FINEST', - 'USER_DEBUG_FINE', - 'USER_DEBUG_DEBUG', - 'USER_DEBUG_INFO', - 'USER_DEBUG_WARN', - 'USER_DEBUG_ERROR', - 'VF_APEX_CALL', - 'HEAP_DUMP', - 'SCRIPT_EXECUTION', - 'SESSION_CACHE_MEMORY_USAGE', - 'ORG_CACHE_MEMORY_USAGE', - 'AE_PERSIST_VALIDATION', - 'REFERENCED_OBJECT_LIST', - 'DUPLICATE_RULE_FILTER', - 'DUPLICATE_RULE_FILTER_RESULT', - 'DUPLICATE_RULE_FILTER_VALUE', - 'TEMPLATED_ASSET', - 'TRANSFORMATION_SUMMARY', - 'RULES_EXECUTION_SUMMARY', - 'ASSET_DIFF_SUMMARY', - 'ASSET_DIFF_DETAIL', - 'RULES_EXECUTION_DETAIL', - 'JSON_DIFF_SUMMARY', - 'JSON_DIFF_DETAIL', - 'MATCH_ENGINE_INVOCATION', -]; - -const basicExitLogEvents: LogEventType[] = [ - 'FLOW_START_INTERVIEW_END', - 'VF_DESERIALIZE_VIEWSTATE_END', - 'VF_SERIALIZE_VIEWSTATE_END', - 'CUMULATIVE_LIMIT_USAGE_END', - 'CUMULATIVE_PROFILING_END', - 'EXECUTION_FINISHED', - 'FLOW_START_INTERVIEWS_END', - 'FLOW_ELEMENT_END', - 'FLOW_BULK_ELEMENT_END', - 'WF_RULE_EVAL_END', - 'WF_RULE_NOT_EVALUATED', - 'WF_CRITERIA_END', - 'DUPLICATE_DETECTION_END', - 'VF_SERIALIZE_CONTINUATION_STATE_END', - 'VF_DESERIALIZE_CONTINUATION_STATE_END', - 'MATCH_ENGINE_END', - 'ORG_CACHE_PUT_END', - 'ORG_CACHE_GET_END', - 'ORG_CACHE_REMOVE_END', - 'SESSION_CACHE_PUT_END', - 'SESSION_CACHE_GET_END', - 'SESSION_CACHE_REMOVE_END', -]; - -const _logEventNames = [ - 'BULK_DML_RETRY', - 'BULK_HEAP_ALLOCATE', - 'CALLOUT_REQUEST', - 'CALLOUT_RESPONSE', - 'NAMED_CREDENTIAL_REQUEST', - 'NAMED_CREDENTIAL_RESPONSE', - 'NAMED_CREDENTIAL_RESPONSE_DETAIL', - 'CONSTRUCTOR_ENTRY', - 'CONSTRUCTOR_EXIT', - 'EMAIL_QUEUE', - 'METHOD_ENTRY', - 'METHOD_EXIT', - 'SYSTEM_CONSTRUCTOR_ENTRY', - 'SYSTEM_CONSTRUCTOR_EXIT', - 'SYSTEM_METHOD_ENTRY', - 'SYSTEM_METHOD_EXIT', - 'CODE_UNIT_STARTED', - 'CODE_UNIT_FINISHED', - 'VF_APEX_CALL_START', - 'VF_APEX_CALL_END', - 'VF_DESERIALIZE_VIEWSTATE_BEGIN', - 'VF_EVALUATE_FORMULA_BEGIN', - 'VF_EVALUATE_FORMULA_END', - 'VF_SERIALIZE_CONTINUATION_STATE_BEGIN', - 'VF_DESERIALIZE_CONTINUATION_STATE_BEGIN', - 'VF_SERIALIZE_VIEWSTATE_BEGIN', - 'VF_PAGE_MESSAGE', - 'DML_BEGIN', - 'DML_END', - 'IDEAS_QUERY_EXECUTE', - 'SOQL_EXECUTE_BEGIN', - 'SOQL_EXECUTE_END', - 'SOQL_EXECUTE_EXPLAIN', - 'SOSL_EXECUTE_BEGIN', - 'SOSL_EXECUTE_END', - 'HEAP_ALLOCATE', - 'HEAP_DEALLOCATE', - 'STATEMENT_EXECUTE', - 'VARIABLE_SCOPE_BEGIN', - 'VARIABLE_ASSIGNMENT', - 'USER_INFO', - 'USER_DEBUG', - 'CUMULATIVE_LIMIT_USAGE', - 'CUMULATIVE_PROFILING', - 'CUMULATIVE_PROFILING_BEGIN', - 'LIMIT_USAGE', - 'LIMIT_USAGE_FOR_NS', - 'NBA_NODE_BEGIN', - 'NBA_NODE_DETAIL', - 'NBA_NODE_END', - 'NBA_NODE_ERROR', - 'NBA_OFFER_INVALID', - 'NBA_STRATEGY_BEGIN', - 'NBA_STRATEGY_END', - 'NBA_STRATEGY_ERROR', - 'POP_TRACE_FLAGS', - 'PUSH_TRACE_FLAGS', - 'QUERY_MORE_BEGIN', - 'QUERY_MORE_END', - 'QUERY_MORE_ITERATIONS', - 'TOTAL_EMAIL_RECIPIENTS_QUEUED', - 'SAVEPOINT_ROLLBACK', - 'SAVEPOINT_SET', - 'STACK_FRAME_VARIABLE_LIST', - 'STATIC_VARIABLE_LIST', - 'SYSTEM_MODE_ENTER', - 'SYSTEM_MODE_EXIT', - 'EXECUTION_STARTED', - 'ENTERING_MANAGED_PKG', - 'EVENT_SERVICE_PUB_BEGIN', - 'EVENT_SERVICE_PUB_END', - 'EVENT_SERVICE_PUB_DETAIL', - 'EVENT_SERVICE_SUB_BEGIN', - 'EVENT_SERVICE_SUB_DETAIL', - 'EVENT_SERVICE_SUB_END', - 'FLOW_START_INTERVIEWS_BEGIN', - 'FLOW_START_INTERVIEWS_ERROR', - 'FLOW_START_INTERVIEW_BEGIN', - 'FLOW_START_INTERVIEW_LIMIT_USAGE', - 'FLOW_START_SCHEDULED_RECORDS', - 'FLOW_CREATE_INTERVIEW_ERROR', - 'FLOW_ELEMENT_BEGIN', - 'FLOW_ELEMENT_DEFERRED', - 'FLOW_ELEMENT_ERROR', - 'FLOW_ELEMENT_FAULT', - 'FLOW_ELEMENT_LIMIT_USAGE', - 'FLOW_INTERVIEW_FINISHED_LIMIT_USAGE', - 'FLOW_SUBFLOW_DETAIL', - 'FLOW_VALUE_ASSIGNMENT', - 'FLOW_WAIT_EVENT_RESUMING_DETAIL', - 'FLOW_WAIT_EVENT_WAITING_DETAIL', - 'FLOW_WAIT_RESUMING_DETAIL', - 'FLOW_WAIT_WAITING_DETAIL', - 'FLOW_INTERVIEW_FINISHED', - 'FLOW_INTERVIEW_PAUSED', - 'FLOW_INTERVIEW_RESUMED', - 'FLOW_ACTIONCALL_DETAIL', - 'FLOW_ASSIGNMENT_DETAIL', - 'FLOW_LOOP_DETAIL', - 'FLOW_RULE_DETAIL', - 'FLOW_BULK_ELEMENT_BEGIN', - 'FLOW_BULK_ELEMENT_DETAIL', - 'FLOW_BULK_ELEMENT_LIMIT_USAGE', - 'FLOW_BULK_ELEMENT_NOT_SUPPORTED', - 'MATCH_ENGINE_BEGIN', - 'ORG_CACHE_PUT_BEGIN', - 'ORG_CACHE_GET_BEGIN', - 'ORG_CACHE_REMOVE_BEGIN', - 'PUSH_NOTIFICATION_INVALID_APP', - 'PUSH_NOTIFICATION_INVALID_CERTIFICATE', - 'PUSH_NOTIFICATION_INVALID_NOTIFICATION', - 'PUSH_NOTIFICATION_NO_DEVICES', - 'PUSH_NOTIFICATION_SENT', - 'SESSION_CACHE_PUT_BEGIN', - 'SESSION_CACHE_GET_BEGIN', - 'SESSION_CACHE_REMOVE_BEGIN', - 'SLA_END', - 'SLA_EVAL_MILESTONE', - 'SLA_PROCESS_CASE', - 'TESTING_LIMITS', - 'VALIDATION_ERROR', - 'VALIDATION_FORMULA', - 'VALIDATION_PASS', - 'VALIDATION_RULE', - 'WF_FLOW_ACTION_ERROR', - 'WF_FLOW_ACTION_ERROR_DETAIL', - 'WF_FIELD_UPDATE', - 'WF_RULE_EVAL_BEGIN', - 'WF_RULE_EVAL_VALUE', - 'WF_RULE_FILTER', - 'WF_CRITERIA_BEGIN', - 'WF_FORMULA', - 'WF_ACTION', - 'WF_ACTIONS_END', - 'WF_ACTION_TASK', - 'WF_APPROVAL', - 'WF_APPROVAL_REMOVE', - 'WF_APPROVAL_SUBMIT', - 'WF_APPROVAL_SUBMITTER', - 'WF_ASSIGN', - 'WF_EMAIL_ALERT', - 'WF_EMAIL_SENT', - 'WF_ENQUEUE_ACTIONS', - 'WF_ESCALATION_ACTION', - 'WF_EVAL_ENTRY_CRITERIA', - 'WF_FLOW_ACTION_DETAIL', - 'WF_NEXT_APPROVER', - 'WF_OUTBOUND_MSG', - 'WF_PROCESS_FOUND', - 'WF_PROCESS_NODE', - 'WF_REASSIGN_RECORD', - 'WF_RESPONSE_NOTIFY', - 'WF_RULE_ENTRY_ORDER', - 'WF_RULE_INVOCATION', - 'WF_SOFT_REJECT', - 'WF_SPOOL_ACTION_BEGIN', - 'WF_TIME_TRIGGER', - 'EXCEPTION_THROWN', - 'FATAL_ERROR', - 'XDS_DETAIL', - 'XDS_RESPONSE', - 'XDS_RESPONSE_DETAIL', - 'XDS_RESPONSE_ERROR', - 'DUPLICATE_DETECTION_BEGIN', - 'DUPLICATE_DETECTION_RULE_INVOCATION', - 'DUPLICATE_DETECTION_MATCH_INVOCATION_DETAILS', - 'DUPLICATE_DETECTION_MATCH_INVOCATION_SUMMARY', - 'BULK_COUNTABLE_STATEMENT_EXECUTE', - 'TEMPLATE_PROCESSING_ERROR', - 'EXTERNAL_SERVICE_REQUEST', - 'FLOW_START_INTERVIEW_END', - 'FLOW_CREATE_INTERVIEW_BEGIN', - 'FLOW_CREATE_INTERVIEW_END', - 'VARIABLE_SCOPE_END', - 'PUSH_NOTIFICATION_NOT_ENABLED', - 'SLA_NULL_START_DATE', - 'TEMPLATE_PROCESSING_ERROR', - 'VALIDATION_FAIL', - `WF_FLOW_ACTION_BEGIN`, - 'WF_FLOW_ACTION_END', - 'WF_ESCALATION_RULE', - 'WF_HARD_REJECT', - 'WF_NO_PROCESS_FOUND', - 'WF_TIME_TRIGGERS_BEGIN', - 'WF_KNOWLEDGE_ACTION', - 'WF_SEND_ACTION', - 'WAVE_APP_LIFECYCLE', - 'WF_QUICK_CREATE', - 'WF_APEX_ACTION', - 'INVOCABLE_ACTION_DETAIL', - 'INVOCABLE_ACTION_ERROR', - 'FLOW_COLLECTION_PROCESSOR_DETAIL', - 'FLOW_SCHEDULED_PATH_QUEUED', - 'ROUTE_WORK_ACTION', - 'ADD_SKILL_REQUIREMENT_ACTION', - 'ADD_SCREEN_POP_ACTION', - 'CALLOUT_REQUEST_PREPARE', - 'CALLOUT_REQUEST_FINALIZE', - 'FUNCTION_INVOCATION_REQUEST', - 'APP_CONTAINER_INITIATED', - 'FUNCTION_INVOCATION_RESPONSE', - 'XDS_REQUEST_DETAIL', - 'EXTERNAL_SERVICE_RESPONSE', - 'DATAWEAVE_USER_DEBUG', - 'USER_DEBUG_FINER', - 'USER_DEBUG_FINEST', - 'USER_DEBUG_FINE', - 'USER_DEBUG_DEBUG', - 'USER_DEBUG_INFO', - 'USER_DEBUG_WARN', - 'USER_DEBUG_ERROR', - 'VF_APEX_CALL', - 'HEAP_DUMP', - 'SCRIPT_EXECUTION', - 'SESSION_CACHE_MEMORY_USAGE', - 'ORG_CACHE_MEMORY_USAGE', - 'AE_PERSIST_VALIDATION', - 'REFERENCED_OBJECT_LIST', - 'DUPLICATE_RULE_FILTER', - 'DUPLICATE_RULE_FILTER_RESULT', - 'DUPLICATE_RULE_FILTER_VALUE', - 'TEMPLATED_ASSET', - 'TRANSFORMATION_SUMMARY', - 'RULES_EXECUTION_SUMMARY', - 'ASSET_DIFF_SUMMARY', - 'ASSET_DIFF_DETAIL', - 'RULES_EXECUTION_DETAIL', - 'JSON_DIFF_SUMMARY', - 'JSON_DIFF_DETAIL', - 'MATCH_ENGINE_INVOCATION', - 'VF_DESERIALIZE_VIEWSTATE_END', - 'VF_SERIALIZE_VIEWSTATE_END', - 'CUMULATIVE_LIMIT_USAGE_END', - 'CUMULATIVE_PROFILING_END', - 'EXECUTION_FINISHED', - 'FLOW_START_INTERVIEWS_END', - 'FLOW_ELEMENT_END', - 'FLOW_BULK_ELEMENT_END', - 'WF_RULE_EVAL_END', - 'WF_RULE_NOT_EVALUATED', - 'WF_CRITERIA_END', - 'DUPLICATE_DETECTION_END', - 'VF_SERIALIZE_CONTINUATION_STATE_END', - 'VF_DESERIALIZE_CONTINUATION_STATE_END', - 'MATCH_ENGINE_END', - 'ORG_CACHE_PUT_END', - 'ORG_CACHE_GET_END', - 'ORG_CACHE_REMOVE_END', - 'SESSION_CACHE_PUT_END', - 'SESSION_CACHE_GET_END', - 'SESSION_CACHE_REMOVE_END', -] as const; - -export type LogEventType = (typeof _logEventNames)[number]; - -export { DMLBeginLine, SOQLExecuteBeginLine, SOQLExecuteExplainLine }; diff --git a/log-viewer/modules/parsers/LogEvents.ts b/log-viewer/modules/parsers/LogEvents.ts new file mode 100644 index 00000000..fdeaa277 --- /dev/null +++ b/log-viewer/modules/parsers/LogEvents.ts @@ -0,0 +1,2037 @@ +/* + * Copyright (c) 2020 Certinia Inc. All rights reserved. + */ + +import type { ApexLogParser, DebugLevel } from './ApexLogParser'; +import type { + CPUType, + LineNumber, + LogEventType, + LogIssue, + LogSubCategory, + SelfTotal, +} from './types.js'; + +/** + * All log lines extend this base class. + */ +export abstract class LogEvent { + logParser: ApexLogParser; + + // common metadata (available for all lines) + + parent: LogEvent | null = null; + + /** + * All child nodes of the current node + */ + children: LogEvent[] = []; + + /** + * The type of this log line from the log file e.g METHOD_ENTRY + */ + type: LogEventType | null = null; + + /** + * The full raw text of this log line + */ + logLine = ''; // the raw text of this log line + + /** + * A parsed version of the log line text useful for display in UIs + */ + text; + + // optional metadata + /** + * Should this log entry pull in following text lines (as the log entry can contain newlines)? + */ + acceptsText = false; + + /** + * Is this log entry generated by a declarative process? + */ + declarative = false; + /** + * Is a method exit line? + */ + isExit = false; + + /** + * Whether the log event was truncated when the log ended, e,g no matching end event + */ + isTruncated = false; + + /** + * Should the exitstamp be the timestamp of the next line? + * These kind of lines can not be used as exit lines for anything othe than other pseudo exits. + */ + nextLineIsExit = false; + + /** + * The line number within the containing class + */ + lineNumber: LineNumber = null; + + /** + * The package namespace associated with this log line + * @default default + */ + namespace: string | 'default' = ''; + + /** + * The variable value + */ + value: string | null = null; + + /** + * Could match to a corresponding symbol in a file in the workspace? + */ + hasValidSymbols = false; + + /** + * Extra description context + */ + suffix: string | null = null; + + /** + * Does this line cause a discontinuity in the call stack? e.g an exception causing stack unwinding + */ + discontinuity = false; + + /** + * The timestamp of this log line, in nanoseconds + */ + timestamp; + + /** + * The timestamp when the node finished, in nanoseconds + */ + exitStamp: number | null = null; + + /** + * The log sub category this event belongs to + */ + subCategory: LogSubCategory = ''; + + /** + * The CPU type, e.g loading, method, custom + */ + cpuType: CPUType = ''; // the category key to collect our cpu usage + + /** + * The time spent. + */ + duration: SelfTotal = { + /** + * The net (wall) time spent in the node (when not inside children) + */ + self: 0, + /** + * The total (wall) time spent in the node + */ + total: 0, + }; + + /** + * Total + self row counts for DML + */ + dmlRowCount: SelfTotal = { + /** + * The net number of DML rows for this node, excluding child nodes + */ + self: 0, + /** + * The total number of DML rows for this node and child nodes + */ + total: 0, + }; + + /** + * Total + self row counts for SOQL + */ + soqlRowCount: SelfTotal = { + /** + * The net number of SOQL rows for this node, excluding child nodes + */ + self: 0, + /** + * The total number of SOQL rows for this node and child nodes + */ + total: 0, + }; + + /** + * Total + self row counts for SOSL + */ + soslRowCount: SelfTotal = { + /** + * The net number of SOSL rows for this node, excluding child nodes + */ + self: 0, + /** + * The total number of SOSL rows for this node and child nodes + */ + total: 0, + }; + + dmlCount: SelfTotal = { + /** + * The net number of DML operations (DML_BEGIN) in this node. + */ + self: 0, + /** + * The total number of DML operations (DML_BEGIN) in this node and child nodes + */ + total: 0, + }; + + soqlCount: SelfTotal = { + /** + * The net number of SOQL operations (SOQL_EXECUTE_BEGIN) in this node. + */ + self: 0, + /** + * The total number of SOQL operations (SOQL_EXECUTE_BEGIN) in this node and child nodes + */ + total: 0, + }; + + soslCount: SelfTotal = { + /** + * The net number of SOSL operations (SOSL_EXECUTE_BEGIN) in this node. + */ + self: 0, + /** + * The total number of SOSL operations (SOSL_EXECUTE_BEGIN) in this node and child nodes + */ + total: 0, + }; + + /** + * The total number of exceptions thrown (EXCEPTION_THROWN) in this node and child nodes + */ + totalThrownCount = 0; + + /** + * The line types which would legitimately end this method + */ + exitTypes: LogEventType[] = []; + + constructor(parser: ApexLogParser, parts: string[] | null) { + this.logParser = parser; + if (parts) { + const [timeData, type] = parts; + this.text = this.type = type as LogEventType; + this.timestamp = timeData ? this.parseTimestamp(timeData) : 0; + } else { + this.timestamp = 0; + this.text = ''; + } + } + + /** Called if a corresponding end event is found during tree parsing*/ + onEnd?(end: LogEvent, stack: LogEvent[]): void; + + /** Called when the Log event after this one is created in the line parser*/ + onAfter?(parser: ApexLogParser, next?: LogEvent): void; + + public recalculateDurations() { + if (this.exitStamp) { + this.duration.total = this.duration.self = this.exitStamp - this.timestamp; + } + } + + private parseTimestamp(text: string): number { + const start = text.indexOf('('); + if (start !== -1) { + return Number(text.slice(start + 1, -1)); + } + throw new Error(`Unable to parse timestamp: '${text}'`); + } + + protected parseLineNumber(text: string | null | undefined): LineNumber { + switch (true) { + case text === '[EXTERNAL]': + return 'EXTERNAL'; + case !!text: { + const lineNumberStr = text.slice(1, -1); + if (lineNumberStr) { + return Number(lineNumberStr); + } + throw new Error(`Unable to parse line number: '${text}'`); + } + default: + return 0; + } + } +} + +export class BasicLogLine extends LogEvent {} +export class BasicExitLine extends LogEvent { + isExit = true; +} + +/** + * Log lines extend this export class if they have a start-line and an end-line (and hence can have children in-between). + * - The start-line should extend "Method" and collect any children. + * - The end-line should extend "Detail" and terminate the method (also providing the "exitStamp"). + * The method will be rendered as "expandable" in the tree-view, if it has children. + */ +export class Method extends LogEvent { + constructor( + parser: ApexLogParser, + parts: string[] | null, + exitTypes: string[], + timelineKey: LogSubCategory, + cpuType: CPUType, + ) { + super(parser, parts); + this.subCategory = timelineKey; + this.cpuType = cpuType; + this.exitTypes = exitTypes as LogEventType[]; + } +} + +/** + * This export class represents the single root node for the node tree. + * It is a "pseudo" node and not present in the log. + * Since it has children it extends "Method". + */ +export class ApexLog extends Method { + type = null; + text = 'LOG_ROOT'; + timestamp = 0; + exitStamp = 0; + /** + * The size of the log, in bytes + */ + public size = 0; + + /** + * The total CPU time consumed, in ms + */ + public cpuTime: number = 0; + + /** + * The Apex Debug Logging Levels for the current log + */ + public debugLevels: DebugLevel[] = []; + + /** + * All the namespaces that appear in this log. + */ + public namespaces: string[] = []; + + /** + * Any issues within the log, such as cpu time exceeded or max log size reached. + */ + public logIssues: LogIssue[] = []; + + /** + * Any issues that occurred during the parsing of the log, such as an unrecognized log event type. + */ + public parsingErrors: string[] = []; + + /** + * The endtime with nodes of 0 duration excluded + */ + executionEndTime = 0; + + constructor(parser: ApexLogParser) { + super(parser, null, [], 'Code Unit', ''); + } + + setTimes() { + this.timestamp = + this.children.find((child) => { + return child.timestamp; + })?.timestamp || 0; + // We do not just want to use the very last exitStamp because it could be CUMULATIVE_USAGE which is not really part of the code execution time but does have a later time. + let endTime; + const reverseLen = this.children.length - 1; + for (let i = reverseLen; i >= 0; i--) { + const child = this.children[i]; + // If there is no duration on a node then it is not going to be shown on the timeline anyway + if (child?.exitStamp) { + endTime ??= child.exitStamp; + if (child.duration) { + this.executionEndTime = child.exitStamp; + break; + } + } + endTime ??= child?.timestamp; + } + this.exitStamp = endTime || 0; + this.recalculateDurations(); + } +} + +export function parseObjectNamespace(text: string | null | undefined): string { + if (!text) { + return ''; + } + + const sep = text.indexOf('__'); + if (sep === -1) { + return 'default'; + } + return text.slice(0, sep); +} + +export function parseVfNamespace(text: string): string { + const sep = text.indexOf('__'); + if (sep === -1) { + return 'default'; + } + const firstSlash = text.indexOf('/'); + if (firstSlash === -1) { + return 'default'; + } + const secondSlash = text.indexOf('/', firstSlash + 1); + if (secondSlash < 0) { + return 'default'; + } + return text.substring(secondSlash + 1, sep); +} + +export function parseRows(text: string | null | undefined): number { + if (!text) { + return 0; + } + + const rowCount = text.slice(text.indexOf('Rows:') + 5); + if (rowCount) { + return Number(rowCount); + } + throw new Error(`Unable to parse row count: '${text}'`); +} + +/* Log line entry Parsers */ + +export class BulkHeapAllocateLine extends LogEvent { + logCategory: 'Apex Code'; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + this.logCategory = 'Apex Code'; + } +} + +export class CalloutRequestLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[3]} : ${parts[2]}`; + } +} + +export class CalloutResponseLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[3]} : ${parts[2]}`; + } +} +export class NamedCredentialRequestLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[3]} : ${parts[4]} : ${parts[5]} : ${parts[6]}`; + } +} + +export class NamedCredentialResponseLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]}`; + } +} + +export class NamedCredentialResponseDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[3]} : ${parts[4]} ${parts[5]} : ${parts[6]} ${parts[7]}`; + } +} + +export class ConstructorEntryLine extends Method { + hasValidSymbols = true; + suffix = ' (constructor)'; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['CONSTRUCTOR_EXIT'], 'Method', 'method'); + this.lineNumber = this.parseLineNumber(parts[2]); + const [, , , , args, className] = parts; + + this.text = className + (args ? args.substring(args.lastIndexOf('(')) : ''); + const possibleNS = this._parseConstructorNamespace(className || ''); + if (possibleNS) { + this.namespace = possibleNS; + } + } + + _parseConstructorNamespace(className: string): string { + let possibleNs = className.slice(0, className.indexOf('.')); + if (this.logParser.namespaces.has(possibleNs)) { + return possibleNs; + } + + const constructorParts = (className ?? '').split('.'); + possibleNs = constructorParts[0] || ''; + // inmner export class with a namespace + if (constructorParts.length === 3) { + return possibleNs; + } + + return ''; + } +} + +export class ConstructorExitLine extends LogEvent { + isExit = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + } +} + +export class EmailQueueLine extends LogEvent { + acceptsText = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + } +} + +export class MethodEntryLine extends Method { + hasValidSymbols = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['METHOD_EXIT'], 'Method', 'method'); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = parts[4] || this.type || this.text; + if (this.text.indexOf('System.Type.forName(') !== -1) { + // assume we are not charged for export class loading (or at least not lengthy remote-loading / compiling) + this.cpuType = 'loading'; + } else { + const possibleNs = this._parseMethodNamespace(parts[4]); + if (possibleNs) { + this.namespace = possibleNs; + } + } + } + + onEnd(end: MethodExitLine, _stack: LogEvent[]): void { + if (end.namespace && !end.text.endsWith(')')) { + this.namespace = end.namespace; + } + } + + _parseMethodNamespace(methodName: string | undefined): string { + if (!methodName) { + return ''; + } + + const methodBracketIndex = methodName.indexOf('('); + if (methodBracketIndex === -1) { + return ''; + } + + const nsSeparator = methodName.indexOf('.'); + if (nsSeparator === -1) { + return ''; + } + + const possibleNs = methodName.slice(0, nsSeparator); + if (this.logParser.namespaces.has(possibleNs)) { + return possibleNs; + } + + const methodNameParts = methodName.slice(0, methodBracketIndex)?.split('.'); + if (methodNameParts.length === 4) { + return methodNameParts[0] ?? ''; + } else if (methodNameParts.length === 2) { + return 'default'; + } + + return ''; + } +} +export class MethodExitLine extends LogEvent { + isExit = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = parts[4] ?? parts[3] ?? this.text; + + /*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 export class (outer or inner) will always have an METHOD_EXIT containing the Outer export 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 export class e.g new ns.Outer.Inner();*/ + // If does not end in ) then we have a reference to the class, either via outer or inner. + if (!this.text.endsWith(')')) { + // if there is a . the we have a namespace e.g ns.Outer + const index = this.text.indexOf('.'); + if (index !== -1) { + this.namespace = this.text.slice(0, index); + } + } + } +} + +export class SystemConstructorEntryLine extends Method { + suffix = '(system constructor)'; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['SYSTEM_CONSTRUCTOR_EXIT'], 'System Method', 'method'); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = parts[3] || ''; + } +} + +export class SystemConstructorExitLine extends LogEvent { + isExit = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + } +} +export class SystemMethodEntryLine extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['SYSTEM_METHOD_EXIT'], 'System Method', 'method'); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = parts[3] || ''; + } +} + +export class SystemMethodExitLine extends LogEvent { + isExit = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + } +} + +export class CodeUnitStartedLine extends Method { + suffix = ' (entrypoint)'; + codeUnitType = ''; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['CODE_UNIT_FINISHED'], 'Code Unit', 'custom'); + + const typeString = parts[5] || parts[4] || parts[3] || ''; + let sepIndex = typeString.indexOf(':'); + if (sepIndex === -1) { + sepIndex = typeString.indexOf('/'); + } + this.codeUnitType = sepIndex !== -1 ? typeString.slice(0, sepIndex) : ''; + + const name = parts[4] || parts[3] || this.codeUnitType || ''; + switch (this.codeUnitType) { + case 'EventService': + this.cpuType = 'method'; + this.namespace = parseObjectNamespace(typeString.slice(sepIndex + 1)); + this.text = name; + break; + case 'Validation': + this.cpuType = 'custom'; + this.declarative = true; + + this.text = name; + break; + case 'Workflow': + this.cpuType = 'custom'; + this.declarative = true; + this.text = name; + break; + case 'Flow': + this.cpuType = 'custom'; + this.declarative = true; + this.text = name; + break; + case 'VF': + this.cpuType = 'method'; + this.namespace = parseVfNamespace(name); + this.text = name; + break; + case 'apex': { + this.cpuType = 'method'; + const namespaceIndex = name.indexOf('.'); + this.namespace = + namespaceIndex !== -1 + ? name.slice(name.indexOf('apex://') + 7, namespaceIndex) + : 'default'; + this.text = name; + break; + } + case '__sfdc_trigger': { + this.cpuType = 'method'; + this.text = name || parts[4] || ''; + const triggerParts = parts[5]?.split('/') || ''; + this.namespace = triggerParts.length === 3 ? triggerParts[1] || 'default' : 'default'; + break; + } + default: { + this.cpuType = 'method'; + this.text = name; + const openBracket = name.lastIndexOf('('); + const methodName = + openBracket !== -1 ? name.slice(0, openBracket + 1).split('.') : name.split('.'); + if (methodName.length === 3 || (methodName.length === 2 && !methodName[1]?.endsWith('('))) { + this.namespace = methodName[0] || 'default'; + } + break; + } + } + + this.namespace ||= 'default'; + } +} +export class CodeUnitFinishedLine extends LogEvent { + isExit = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class VFApexCallStartLine extends Method { + suffix = ' (VF APEX)'; + invalidClasses = [ + 'pagemessagescomponentcontroller', + 'pagemessagecomponentcontroller', + 'severitymessages', + ]; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['VF_APEX_CALL_END'], 'Method', 'method'); + this.lineNumber = this.parseLineNumber(parts[2]); + + const classText = parts[5] || parts[3] || ''; + let methodtext = parts[4] || ''; + if ( + !methodtext && + (!classText.includes(' ') || + this.invalidClasses.some((invalidCls: string) => + classText.toLowerCase().includes(invalidCls), + )) + ) { + // we have a system entry and they do not have exits + // e.g |VF_APEX_CALL_START|[EXTERNAL]|/apexpage/pagemessagescomponentcontroller.apex + // and they really mess with the logs so skip handling them. + this.exitTypes = []; + } else if (methodtext) { + this.hasValidSymbols = true; + // method call + const methodIndex = methodtext.indexOf('('); + const constructorIndex = methodtext.indexOf(''); + if (methodIndex > -1) { + // Method + methodtext = '.' + methodtext.substring(methodIndex).slice(1, -1) + '()'; + } else if (constructorIndex > -1) { + // Constructor + methodtext = methodtext.substring(constructorIndex + 6) + '()'; + } else { + // Property + methodtext = '.' + methodtext; + } + } else { + this.hasValidSymbols = true; + } + this.text = classText + methodtext; + } +} + +export class VFApexCallEndLine extends LogEvent { + isExit = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class VFDeserializeViewstateBeginLine extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['VF_DESERIALIZE_VIEWSTATE_END'], 'System Method', 'method'); + } +} + +export class VFFormulaStartLine extends Method { + suffix = ' (VF FORMULA)'; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['VF_EVALUATE_FORMULA_END'], 'System Method', 'custom'); + this.text = parts[3] || ''; + } +} + +export class VFFormulaEndLine extends LogEvent { + isExit = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class VFSeralizeViewStateStartLine extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['VF_SERIALIZE_VIEWSTATE_END'], 'System Method', 'method'); + } +} + +export class VFPageMessageLine extends LogEvent { + acceptsText = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class DMLBeginLine extends Method { + dmlCount = { + self: 1, + total: 1, + }; + + namespace = 'default'; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['DML_END'], 'DML', 'free'); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = 'DML ' + parts[3] + ' ' + parts[4]; + const rowCountString = parts[5]; + this.dmlRowCount.total = this.dmlRowCount.self = rowCountString ? parseRows(rowCountString) : 0; + } +} + +export class DMLEndLine extends LogEvent { + isExit = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + } +} + +export class IdeasQueryExecuteLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + } +} + +export class SOQLExecuteBeginLine extends Method { + aggregations = 0; + soqlCount = { + self: 1, + total: 1, + }; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['SOQL_EXECUTE_END'], 'SOQL', 'free'); + this.lineNumber = this.parseLineNumber(parts[2]); + + const [, , , aggregations, soqlString] = parts; + + const aggregationText = aggregations || ''; + if (aggregationText) { + const aggregationIndex = aggregationText.indexOf('Aggregations:'); + this.aggregations = Number(aggregationText.slice(aggregationIndex + 13)); + } + this.text = soqlString || ''; + } + + onEnd(end: SOQLExecuteEndLine, _stack: LogEvent[]): void { + this.soqlRowCount.total = this.soqlRowCount.self = end.soqlRowCount.total; + } +} + +export class SOQLExecuteEndLine extends LogEvent { + isExit = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.soqlRowCount.total = this.soqlRowCount.self = parseRows(parts[3] || ''); + } +} + +export class SOQLExecuteExplainLine extends LogEvent { + cardinality: number | null = null; // The estimated number of records that the leading operation type would return + fields: string[] | null = null; //The indexed field(s) used by the Query Optimizer. If the leading operation type is Index, the fields value is Index. Otherwise, the fields value is null. + leadingOperationType: string | null = null; // The primary operation type that Salesforce will use to optimize the query. + relativeCost: number | null = null; // The cost of the query compared to the Force.com Query Optimizer’s selectivity threshold. Values above 1 mean that the query won’t be selective. + sObjectCardinality: number | null = null; // The approximate record count for the queried object. + sObjectType: string | null = null; //T he name of the queried SObject + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + + const queryPlanDetails = parts[3] || ''; + this.text = queryPlanDetails; + + const queryplanParts = queryPlanDetails.split('],'); + if (queryplanParts.length > 1) { + const planExplain = queryplanParts[0] || ''; + const [cardinalityText, sobjCardinalityText, costText] = (queryplanParts[1] || '').split(','); + + const onIndex = planExplain.indexOf(' on'); + this.leadingOperationType = planExplain.slice(0, onIndex); + + const colonIndex = planExplain.indexOf(' :'); + this.sObjectType = planExplain.slice(onIndex + 4, colonIndex); + + // remove whitespace if there is any. we could have [ field1__c, field2__c ] + // I am not 100% sure of format when we have multiple fields so this is safer + const fieldsAsString = planExplain.slice(planExplain.indexOf('[') + 1).replace(/\s+/g, ''); + this.fields = fieldsAsString === '' ? [] : fieldsAsString.split(','); + + this.cardinality = cardinalityText + ? Number(cardinalityText.slice(cardinalityText.indexOf('cardinality: ') + 13)) + : null; + this.sObjectCardinality = sobjCardinalityText + ? Number( + sobjCardinalityText.slice(sobjCardinalityText.indexOf('sobjectCardinality: ') + 20), + ) + : null; + this.relativeCost = costText + ? Number(costText.slice(costText.indexOf('relativeCost ') + 13)) + : null; + } + } +} + +export class SOSLExecuteBeginLine extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['SOSL_EXECUTE_END'], 'SOQL', 'free'); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = `SOSL: ${parts[3]}`; + + this.soslCount = { + self: 1, + total: 1, + }; + } + + onEnd(end: SOSLExecuteEndLine, _stack: LogEvent[]): void { + this.soslRowCount.total = this.soslRowCount.self = end.soslRowCount.total; + } +} + +export class SOSLExecuteEndLine extends LogEvent { + isExit = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.soslRowCount.total = this.soslRowCount.self = parseRows(parts[3] || ''); + } +} + +export class HeapAllocateLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = parts[3] || ''; + } +} + +export class HeapDeallocateLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + } +} + +export class StatementExecuteLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + } +} + +export class VariableScopeBeginLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = parts.slice(3).join(' | '); + } +} + +export class VariableAssignmentLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = parts.slice(3).join(' | '); + } +} +export class UserInfoLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = parts[3] + ' ' + parts[4]; + } +} + +export class UserDebugLine extends LogEvent { + acceptsText = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = parts.slice(3).join(' | '); + } +} + +export class CumulativeLimitUsageLine extends Method { + namespace = 'default'; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['CUMULATIVE_LIMIT_USAGE_END'], 'System Method', 'system'); + } +} + +export class CumulativeProfilingLine extends LogEvent { + acceptsText = true; + namespace = 'default'; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] + ' ' + (parts[3] ?? ''); + } +} + +export class CumulativeProfilingBeginLine extends Method { + namespace = 'default'; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['CUMULATIVE_PROFILING_END'], 'System Method', 'custom'); + } +} + +export class LimitUsageLine extends LogEvent { + namespace = 'default'; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = parts[3] + ' ' + parts[4] + ' out of ' + parts[5]; + } +} + +export class LimitUsageForNSLine extends LogEvent { + acceptsText = true; + namespace = 'default'; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } + + onAfter(parser: ApexLogParser, _next?: LogEvent): void { + const matched = this.text.match(/Maximum CPU time: (\d+)/), + cpuText = matched?.[1] || '0', + cpuTime = parseInt(cpuText, 10) * 1000000; // convert from milli-seconds to nano-seconds + + if (!parser.cpuUsed || cpuTime > parser.cpuUsed) { + parser.cpuUsed = cpuTime; + } + } +} + +export class NBANodeBegin extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['NBA_NODE_END'], 'System Method', 'method'); + this.text = parts.slice(2).join(' | '); + } +} + +export class NBANodeDetail extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts.slice(2).join(' | '); + } +} +export class NBANodeEnd extends LogEvent { + isExit = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts.slice(2).join(' | '); + } +} +export class NBANodeError extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts.slice(2).join(' | '); + } +} +export class NBAOfferInvalid extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts.slice(2).join(' | '); + } +} +export class NBAStrategyBegin extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['NBA_STRATEGY_END'], 'System Method', 'method'); + this.text = parts.slice(2).join(' | '); + } +} +export class NBAStrategyEnd extends LogEvent { + isExit = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts.slice(2).join(' | '); + } +} +export class NBAStrategyError extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts.slice(2).join(' | '); + } +} + +export class PushTraceFlagsLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = parts[4] + ', line:' + this.lineNumber + ' - ' + parts[5]; + } +} + +export class PopTraceFlagsLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = parts[4] + ', line:' + this.lineNumber + ' - ' + parts[5]; + } +} + +export class QueryMoreBeginLine extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['QUERY_MORE_END'], 'SOQL', 'custom'); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = `line: ${this.lineNumber}`; + } +} + +export class QueryMoreEndLine extends LogEvent { + isExit = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = `line: ${this.lineNumber}`; + } +} +export class QueryMoreIterationsLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = `line: ${this.lineNumber}, iterations:${parts[3]}`; + } +} + +export class SavepointRollbackLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = `${parts[3]}, line: ${this.lineNumber}`; + } +} + +export class SavePointSetLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = `${parts[3]}, line: ${this.lineNumber}`; + } +} + +export class TotalEmailRecipientsQueuedLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class StackFrameVariableListLine extends LogEvent { + acceptsText = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + } +} + +export class StaticVariableListLine extends LogEvent { + acceptsText = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + } +} + +// This looks like a method, but the exit line is often missing... +export class SystemModeEnterLine extends LogEvent { + // namespace = "system"; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class SystemModeExitLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class ExecutionStartedLine extends Method { + namespace = 'default'; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['EXECUTION_FINISHED'], 'Method', 'method'); + } +} + +export class EnteringManagedPackageLine extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, [], 'Method', 'pkg'); + const rawNs = parts[2] || '', + lastDot = rawNs.lastIndexOf('.'); + + this.text = this.namespace = lastDot < 0 ? rawNs : rawNs.substring(lastDot + 1); + } + + onAfter(parser: ApexLogParser, end?: LogEvent): void { + if (end) { + this.exitStamp = end.timestamp; + } + } +} + +export class EventSericePubBeginLine extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['EVENT_SERVICE_PUB_END'], 'Flow', 'custom'); + this.text = parts[2] || ''; + } +} + +export class EventSericePubEndLine extends LogEvent { + isExit = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class EventSericePubDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] + ' ' + parts[3] + ' ' + parts[4]; + } +} + +export class EventSericeSubBeginLine extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['EVENT_SERVICE_SUB_END'], 'Flow', 'custom'); + this.text = `${parts[2]} ${parts[3]}`; + } +} + +export class EventSericeSubEndLine extends LogEvent { + isExit = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} ${parts[3]}`; + } +} + +export class EventSericeSubDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} ${parts[3]} ${parts[4]} ${parts[6]} ${parts[6]}`; + } +} + +export class FlowStartInterviewsBeginLine extends Method { + declarative = true; + text = 'FLOW_START_INTERVIEWS : '; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['FLOW_START_INTERVIEWS_END'], 'Flow', 'custom'); + } + + onEnd(end: LogEvent, stack: LogEvent[]) { + const flowType = this.getFlowType(stack); + this.suffix = ` (${flowType})`; + this.text += this.getFlowName(); + } + + getFlowType(stack: LogEvent[]) { + let flowType; + // ignore the last one on stack is it will be this FlowStartInterviewsBeginLine + const len = stack.length - 2; + for (let i = len; i >= 0; i--) { + const elem = stack[i]; + // type = "CODE_UNIT_STARTED" a flow or Processbuilder was started directly + // type = "FLOW_START_INTERVIEWS_BEGIN" a flow was started from a process builder + if (elem instanceof CodeUnitStartedLine) { + flowType = elem.codeUnitType === 'Flow' ? 'Flow' : 'Process Builder'; + break; + } else if (elem && elem.type === 'FLOW_START_INTERVIEWS_BEGIN') { + flowType = 'Flow'; + break; + } + } + return flowType || ''; + } + + getFlowName() { + if (this.children.length) { + return this.children[0]?.text || ''; + } + return ''; + } +} + +export class FlowStartInterviewsErrorLine extends LogEvent { + acceptsText = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} - ${parts[4]}`; + } +} + +export class FlowStartInterviewBeginLine extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['FLOW_START_INTERVIEW_END'], 'Flow', 'custom'); + this.text = parts[3] || ''; + } +} + +export class FlowStartInterviewLimitUsageLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class FlowStartScheduledRecordsLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]}`; + } +} + +export class FlowCreateInterviewErrorLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]}`; + } +} + +export class FlowElementBeginLine extends Method { + declarative = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['FLOW_ELEMENT_END'], 'Flow', 'custom'); + this.text = parts[3] + ' ' + parts[4]; + } +} + +export class FlowElementDeferredLine extends LogEvent { + declarative = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] + ' ' + parts[3]; + } +} + +export class FlowElementAssignmentLine extends LogEvent { + declarative = true; + acceptsText = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[3] + ' ' + parts[4]; + } +} + +export class FlowWaitEventResumingDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]}`; + } +} + +export class FlowWaitEventWaitingDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]} : ${parts[6]}`; + } +} + +export class FlowWaitResumingDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; + } +} + +export class FlowWaitWaitingDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]}`; + } +} + +export class FlowInterviewFinishedLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[3] || ''; + } +} + +export class FlowInterviewResumedLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]}`; + } +} + +export class FlowInterviewPausedLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; + } +} + +export class FlowElementErrorLine extends LogEvent { + acceptsText = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[1] || '' + parts[2] + ' ' + parts[3] + ' ' + parts[4]; + } +} + +export class FlowElementFaultLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; + } +} + +export class FlowElementLimitUsageLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]}`; + } +} + +export class FlowInterviewFinishedLimitUsageLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]}`; + } +} + +export class FlowSubflowDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]}`; + } +} + +export class FlowActionCallDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[3] + ' : ' + parts[4] + ' : ' + parts[5] + ' : ' + parts[6]; + } +} + +export class FlowAssignmentDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[3] + ' : ' + parts[4] + ' : ' + parts[5]; + } +} + +export class FlowLoopDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[3] + ' : ' + parts[4]; + } +} + +export class FlowRuleDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[3] + ' : ' + parts[4]; + } +} + +export class FlowBulkElementBeginLine extends Method { + declarative = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['FLOW_BULK_ELEMENT_END'], 'Flow', 'custom'); + this.text = `${parts[2]} - ${parts[3]}`; + } +} + +export class FlowBulkElementDetailLine extends LogEvent { + declarative = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] + ' : ' + parts[3] + ' : ' + parts[4]; + } +} + +export class FlowBulkElementNotSupportedLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; + } +} + +export class FlowBulkElementLimitUsageLine extends LogEvent { + declarative = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class PNInvalidAppLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]}.${parts[3]}`; + } +} + +export class PNInvalidCertificateLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]}.${parts[3]}`; + } +} +export class PNInvalidNotificationLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]}.${parts[3]} : ${parts[4]} : ${parts[5]} : ${parts[6]} : ${parts[7]} : ${parts[8]}`; + } +} +export class PNNoDevicesLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]}.${parts[3]}`; + } +} + +export class PNSentLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]}.${parts[3]} : ${parts[4]} : ${parts[5]} : ${parts[6]} : ${parts[7]}`; + } +} + +export class SLAEndLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]} : ${parts[6]}`; + } +} + +export class SLAEvalMilestoneLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]}`; + } +} + +export class SLAProcessCaseLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]}`; + } +} + +export class TestingLimitsLine extends LogEvent { + acceptsText = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + } +} + +export class ValidationRuleLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[3] || ''; + } +} + +export class ValidationErrorLine extends LogEvent { + acceptsText = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class ValidationFormulaLine extends LogEvent { + acceptsText = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + const extra = parts.length > 3 ? ' ' + parts[3] : ''; + + this.text = parts[2] + extra; + } +} + +export class ValidationPassLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[3] || ''; + } +} + +export class WFFlowActionErrorLine extends LogEvent { + acceptsText = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[1] + ' ' + parts[4]; + } +} + +export class WFFlowActionErrorDetailLine extends LogEvent { + acceptsText = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[1] + ' ' + parts[2]; + } +} + +export class WFFieldUpdateLine extends Method { + isExit = true; + nextLineIsExit = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['WF_FIELD_UPDATE'], 'Workflow', 'custom'); + this.text = ' ' + parts[2] + ' ' + parts[3] + ' ' + parts[4] + ' ' + parts[5] + ' ' + parts[6]; + } +} + +export class WFRuleEvalBeginLine extends Method { + declarative = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['WF_RULE_EVAL_END'], 'Workflow', 'custom'); + this.text = parts[2] || ''; + } +} + +export class WFRuleEvalValueLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class WFRuleFilterLine extends LogEvent { + acceptsText = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class WFCriteriaBeginLine extends Method { + declarative = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['WF_CRITERIA_END', 'WF_RULE_NOT_EVALUATED'], 'Workflow', 'custom'); + this.text = 'WF_CRITERIA : ' + parts[5] + ' : ' + parts[3]; + } +} + +export class WFFormulaLine extends Method { + acceptsText = true; + isExit = true; + nextLineIsExit = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['WF_FORMULA'], 'Workflow', 'custom'); + this.text = parts[2] + ' : ' + parts[3]; + } +} + +export class WFActionLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class WFActionsEndLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class WFActionTaskLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]} : ${parts[6]} : ${parts[7]}`; + } +} + +export class WFApprovalLine extends Method { + isExit = true; + nextLineIsExit = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['WF_APPROVAL'], 'Workflow', 'custom'); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; + } +} + +export class WFApprovalRemoveLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]}`; + } +} + +export class WFApprovalSubmitLine extends Method { + isExit = true; + nextLineIsExit = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['WF_APPROVAL_SUBMIT'], 'Workflow', 'custom'); + this.text = `${parts[2]}`; + } +} + +export class WFApprovalSubmitterLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; + } +} + +export class WFAssignLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]}`; + } +} + +export class WFEmailAlertLine extends Method { + isExit = true; + nextLineIsExit = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['WF_EMAIL_ALERT'], 'Workflow', 'custom'); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; + } +} + +export class WFEmailSentLine extends Method { + isExit = true; + nextLineIsExit = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['WF_EMAIL_SENT'], 'Workflow', 'custom'); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; + } +} + +export class WFEnqueueActionsLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class WFEscalationActionLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]}`; + } +} + +export class WFEvalEntryCriteriaLine extends Method { + isExit = true; + nextLineIsExit = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['WF_EVAL_ENTRY_CRITERIA'], 'Workflow', 'custom'); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; + } +} + +export class WFFlowActionDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + const optional = parts[4] ? ` : ${parts[4]} :${parts[5]}` : ''; + this.text = `${parts[2]} : ${parts[3]}` + optional; + } +} + +export class WFNextApproverLine extends Method { + isExit = true; + nextLineIsExit = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['WF_NEXT_APPROVER'], 'Workflow', 'custom'); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]}`; + } +} + +export class WFOutboundMsgLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]}`; + } +} + +export class WFProcessFoundLine extends Method { + isExit = true; + nextLineIsExit = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['WF_PROCESS_FOUND'], 'Workflow', 'custom'); + this.text = `${parts[2]} : ${parts[3]}`; + } +} + +export class WFProcessNode extends Method { + isExit = true; + nextLineIsExit = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['WF_PROCESS_NODE'], 'Workflow', 'custom'); + this.text = parts[2] || ''; + } +} + +export class WFReassignRecordLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]}`; + } +} + +export class WFResponseNotifyLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]}`; + } +} + +export class WFRuleEntryOrderLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class WFRuleInvocationLine extends Method { + isExit = true; + nextLineIsExit = true; + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['WF_RULE_INVOCATION'], 'Workflow', 'custom'); + this.text = parts[2] || ''; + } +} + +export class WFSoftRejectLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class WFTimeTriggerLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]}`; + } +} + +export class WFSpoolActionBeginLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class ExceptionThrownLine extends LogEvent { + discontinuity = true; + acceptsText = true; + totalThrownCount = 1; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.lineNumber = this.parseLineNumber(parts[2]); + this.text = parts[3] || ''; + } + + onAfter(parser: ApexLogParser, _next?: LogEvent): void { + if (this.text.indexOf('System.LimitException') >= 0) { + const isMultiLine = this.text.indexOf('\n'); + const len = isMultiLine < 0 ? 99 : isMultiLine; + const truncateText = this.text.length > len; + const summary = this.text.slice(0, len + 1) + (truncateText ? '…' : ''); + const message = truncateText ? this.text : ''; + parser.addLogIssue(this.timestamp, summary, message, 'error'); + } + } +} + +export class FatalErrorLine extends LogEvent { + acceptsText = true; + hideable = false; + discontinuity = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } + + onAfter(parser: ApexLogParser, _next?: LogEvent): void { + const newLineIndex = this.text.indexOf('\n'); + const summary = newLineIndex > -1 ? this.text.slice(0, newLineIndex + 1) : this.text; + const detailText = summary.length !== this.text.length ? this.text : ''; + parser.addLogIssue(this.timestamp, 'FATAL ERROR! cause=' + summary, detailText, 'error'); + } +} + +export class XDSDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class XDSResponseLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[2]} : ${parts[3]} : ${parts[4]} : ${parts[5]} : ${parts[6]}`; + } +} +export class XDSResponseDetailLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +export class XDSResponseErrorLine extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +// e.g. "09:45:31.888 (38889007737)|DUPLICATE_DETECTION_BEGIN" +export class DuplicateDetectionBegin extends Method { + declarative = true; + + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['DUPLICATE_DETECTION_END'], 'Workflow', 'custom'); + } +} + +// e.g. "09:45:31.888 (38889067408)|DUPLICATE_DETECTION_RULE_INVOCATION|DuplicateRuleId:0Bm20000000CaSP|DuplicateRuleName:Duplicate Account|DmlType:UPDATE" +export class DuplicateDetectionRule extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = `${parts[3]} - ${parts[4]}`; + } +} + +/** + * NOTE: These can be found in the org on the create new debug level page but are not found in the docs here + * https://help.salesforce.com/s/articleView?id=sf.code_setting_debug_log_levels.htm + */ +export class BulkDMLEntry extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts[2] || ''; + } +} + +/** + * DUPLICATE_DETECTION_MATCH_INVOCATION_DETAILS|EntityType:Account|ActionTaken:Allow_[Alert,Report]|DuplicateRecordIds: + */ +export class DuplicateDetectionDetails extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts.slice(2).join(' | '); + } +} + +/** + * DUPLICATE_DETECTION_MATCH_INVOCATION_SUMMARY|EntityType:Account|NumRecordsToBeSaved:200|NumRecordsToBeSavedWithDuplicates:0|NumDuplicateRecordsFound:0 + */ +export class DuplicateDetectionSummary extends LogEvent { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts); + this.text = parts.slice(2).join(' | '); + } +} + +export class SessionCachePutBegin extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['SESSION_CACHE_PUT_END'], 'Method', 'method'); + } +} +export class SessionCacheGetBegin extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['SESSION_CACHE_GET_END'], 'Method', 'method'); + } +} + +export class SessionCacheRemoveBegin extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['SESSION_CACHE_REMOVE_END'], 'Method', 'method'); + } +} + +export class OrgCachePutBegin extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['ORG_CACHE_PUT_END'], 'Method', 'method'); + } +} + +export class OrgCacheGetBegin extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['ORG_CACHE_GET_END'], 'Method', 'method'); + } +} + +export class OrgCacheRemoveBegin extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['ORG_CACHE_REMOVE_END'], 'Method', 'method'); + } +} + +export class VFSerializeContinuationStateBegin extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['VF_SERIALIZE_CONTINUATION_STATE_END'], 'Method', 'method'); + } +} + +export class VFDeserializeContinuationStateBegin extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['VF_SERIALIZE_CONTINUATION_STATE_END'], 'Method', 'method'); + } +} + +export class MatchEngineBegin extends Method { + constructor(parser: ApexLogParser, parts: string[]) { + super(parser, parts, ['MATCH_ENGINE_END'], 'Method', 'method'); + } +} diff --git a/log-viewer/modules/parsers/LogLineMapping.ts b/log-viewer/modules/parsers/LogLineMapping.ts new file mode 100644 index 00000000..1a30d048 --- /dev/null +++ b/log-viewer/modules/parsers/LogLineMapping.ts @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2020 Certinia Inc. All rights reserved. + */ + +import type { ApexLogParser } from './ApexLogParser.js'; +import { + BasicExitLine, + BasicLogLine, + BulkDMLEntry, + BulkHeapAllocateLine, + CalloutRequestLine, + CalloutResponseLine, + CodeUnitFinishedLine, + CodeUnitStartedLine, + ConstructorEntryLine, + ConstructorExitLine, + CumulativeLimitUsageLine, + CumulativeProfilingBeginLine, + CumulativeProfilingLine, + DMLBeginLine, + DMLEndLine, + DuplicateDetectionBegin, + DuplicateDetectionDetails, + DuplicateDetectionRule, + DuplicateDetectionSummary, + EmailQueueLine, + EnteringManagedPackageLine, + EventSericePubBeginLine, + EventSericePubDetailLine, + EventSericePubEndLine, + EventSericeSubBeginLine, + EventSericeSubDetailLine, + EventSericeSubEndLine, + ExceptionThrownLine, + ExecutionStartedLine, + FatalErrorLine, + FlowActionCallDetailLine, + FlowAssignmentDetailLine, + FlowBulkElementBeginLine, + FlowBulkElementDetailLine, + FlowBulkElementLimitUsageLine, + FlowBulkElementNotSupportedLine, + FlowCreateInterviewErrorLine, + FlowElementAssignmentLine, + FlowElementBeginLine, + FlowElementDeferredLine, + FlowElementErrorLine, + FlowElementFaultLine, + FlowElementLimitUsageLine, + FlowInterviewFinishedLimitUsageLine, + FlowInterviewFinishedLine, + FlowInterviewPausedLine, + FlowInterviewResumedLine, + FlowLoopDetailLine, + FlowRuleDetailLine, + FlowStartInterviewBeginLine, + FlowStartInterviewLimitUsageLine, + FlowStartInterviewsBeginLine, + FlowStartInterviewsErrorLine, + FlowStartScheduledRecordsLine, + FlowSubflowDetailLine, + FlowWaitEventResumingDetailLine, + FlowWaitEventWaitingDetailLine, + FlowWaitResumingDetailLine, + FlowWaitWaitingDetailLine, + HeapAllocateLine, + HeapDeallocateLine, + IdeasQueryExecuteLine, + LimitUsageForNSLine, + LimitUsageLine, + LogEvent, + MatchEngineBegin, + MethodEntryLine, + MethodExitLine, + NamedCredentialRequestLine, + NamedCredentialResponseDetailLine, + NamedCredentialResponseLine, + NBANodeBegin, + NBANodeDetail, + NBANodeEnd, + NBANodeError, + NBAOfferInvalid, + NBAStrategyBegin, + NBAStrategyEnd, + NBAStrategyError, + OrgCacheGetBegin, + OrgCachePutBegin, + OrgCacheRemoveBegin, + PNInvalidAppLine, + PNInvalidCertificateLine, + PNInvalidNotificationLine, + PNNoDevicesLine, + PNSentLine, + PopTraceFlagsLine, + PushTraceFlagsLine, + QueryMoreBeginLine, + QueryMoreEndLine, + QueryMoreIterationsLine, + SavepointRollbackLine, + SavePointSetLine, + SessionCacheGetBegin, + SessionCachePutBegin, + SessionCacheRemoveBegin, + SLAEndLine, + SLAEvalMilestoneLine, + SLAProcessCaseLine, + SOQLExecuteBeginLine, + SOQLExecuteEndLine, + SOQLExecuteExplainLine, + SOSLExecuteBeginLine, + SOSLExecuteEndLine, + StackFrameVariableListLine, + StatementExecuteLine, + StaticVariableListLine, + SystemConstructorEntryLine, + SystemConstructorExitLine, + SystemMethodEntryLine, + SystemMethodExitLine, + SystemModeEnterLine, + SystemModeExitLine, + TestingLimitsLine, + TotalEmailRecipientsQueuedLine, + UserDebugLine, + UserInfoLine, + ValidationErrorLine, + ValidationFormulaLine, + ValidationPassLine, + ValidationRuleLine, + VariableAssignmentLine, + VariableScopeBeginLine, + VFApexCallEndLine, + VFApexCallStartLine, + VFDeserializeContinuationStateBegin, + VFDeserializeViewstateBeginLine, + VFFormulaEndLine, + VFFormulaStartLine, + VFPageMessageLine, + VFSeralizeViewStateStartLine, + VFSerializeContinuationStateBegin, + WFActionLine, + WFActionsEndLine, + WFActionTaskLine, + WFApprovalLine, + WFApprovalRemoveLine, + WFApprovalSubmitLine, + WFApprovalSubmitterLine, + WFAssignLine, + WFCriteriaBeginLine, + WFEmailAlertLine, + WFEmailSentLine, + WFEnqueueActionsLine, + WFEscalationActionLine, + WFEvalEntryCriteriaLine, + WFFieldUpdateLine, + WFFlowActionDetailLine, + WFFlowActionErrorDetailLine, + WFFlowActionErrorLine, + WFFormulaLine, + WFNextApproverLine, + WFOutboundMsgLine, + WFProcessFoundLine, + WFProcessNode, + WFReassignRecordLine, + WFResponseNotifyLine, + WFRuleEntryOrderLine, + WFRuleEvalBeginLine, + WFRuleEvalValueLine, + WFRuleFilterLine, + WFRuleInvocationLine, + WFSoftRejectLine, + WFSpoolActionBeginLine, + WFTimeTriggerLine, + XDSDetailLine, + XDSResponseDetailLine, + XDSResponseErrorLine, + XDSResponseLine, +} from './LogEvents.js'; +import type { LogEventType, LogLineConstructor } from './types.js'; + +export function getLogEventClass( + eventName: LogEventType, +): LogLineConstructor | null | undefined { + if (!eventName) { + return null; + } + + // Fast path for the most commonly occuring types + switch (eventName) { + case 'METHOD_ENTRY': + return MethodEntryLine; + + case 'METHOD_EXIT': + return MethodExitLine; + + case 'CONSTRUCTOR_ENTRY': + return ConstructorEntryLine; + + case 'CONSTRUCTOR_EXIT': + return ConstructorExitLine; + + default: + break; + } + + // Handle all other types + const logType = lineTypeMap.get(eventName); + if (logType) { + return logType; + } else if (basicLogEvents.indexOf(eventName) !== -1) { + return BasicLogLine; + } else if (basicExitLogEvents.indexOf(eventName) !== -1) { + return BasicExitLine; + } + + return null; +} + +export const lineTypeMap: ReadonlyMap< + LogEventType, + LogLineConstructor +> = new Map>([ + ['BULK_DML_RETRY', BulkDMLEntry], + ['BULK_HEAP_ALLOCATE', BulkHeapAllocateLine], + ['CALLOUT_REQUEST', CalloutRequestLine], + ['CALLOUT_RESPONSE', CalloutResponseLine], + ['NAMED_CREDENTIAL_REQUEST', NamedCredentialRequestLine], + ['NAMED_CREDENTIAL_RESPONSE', NamedCredentialResponseLine], + ['NAMED_CREDENTIAL_RESPONSE_DETAIL', NamedCredentialResponseDetailLine], + ['CONSTRUCTOR_ENTRY', ConstructorEntryLine], + ['CONSTRUCTOR_EXIT', ConstructorExitLine], + ['EMAIL_QUEUE', EmailQueueLine], + ['METHOD_ENTRY', MethodEntryLine], + ['METHOD_EXIT', MethodExitLine], + ['SYSTEM_CONSTRUCTOR_ENTRY', SystemConstructorEntryLine], + ['SYSTEM_CONSTRUCTOR_EXIT', SystemConstructorExitLine], + ['SYSTEM_METHOD_ENTRY', SystemMethodEntryLine], + ['SYSTEM_METHOD_EXIT', SystemMethodExitLine], + ['CODE_UNIT_STARTED', CodeUnitStartedLine], + ['CODE_UNIT_FINISHED', CodeUnitFinishedLine], + ['VF_APEX_CALL_START', VFApexCallStartLine], + ['VF_APEX_CALL_END', VFApexCallEndLine], + ['VF_DESERIALIZE_VIEWSTATE_BEGIN', VFDeserializeViewstateBeginLine], + ['VF_EVALUATE_FORMULA_BEGIN', VFFormulaStartLine], + ['VF_EVALUATE_FORMULA_END', VFFormulaEndLine], + ['VF_SERIALIZE_CONTINUATION_STATE_BEGIN', VFSerializeContinuationStateBegin], + ['VF_DESERIALIZE_CONTINUATION_STATE_BEGIN', VFDeserializeContinuationStateBegin], + ['VF_SERIALIZE_VIEWSTATE_BEGIN', VFSeralizeViewStateStartLine], + ['VF_PAGE_MESSAGE', VFPageMessageLine], + ['DML_BEGIN', DMLBeginLine], + ['DML_END', DMLEndLine], + ['IDEAS_QUERY_EXECUTE', IdeasQueryExecuteLine], + ['SOQL_EXECUTE_BEGIN', SOQLExecuteBeginLine], + ['SOQL_EXECUTE_END', SOQLExecuteEndLine], + ['SOQL_EXECUTE_EXPLAIN', SOQLExecuteExplainLine], + ['SOSL_EXECUTE_BEGIN', SOSLExecuteBeginLine], + ['SOSL_EXECUTE_END', SOSLExecuteEndLine], + ['HEAP_ALLOCATE', HeapAllocateLine], + ['HEAP_DEALLOCATE', HeapDeallocateLine], + ['STATEMENT_EXECUTE', StatementExecuteLine], + ['VARIABLE_SCOPE_BEGIN', VariableScopeBeginLine], + ['VARIABLE_ASSIGNMENT', VariableAssignmentLine], + ['USER_INFO', UserInfoLine], + ['USER_DEBUG', UserDebugLine], + ['CUMULATIVE_LIMIT_USAGE', CumulativeLimitUsageLine], + ['CUMULATIVE_PROFILING', CumulativeProfilingLine], + ['CUMULATIVE_PROFILING_BEGIN', CumulativeProfilingBeginLine], + ['LIMIT_USAGE', LimitUsageLine], + ['LIMIT_USAGE_FOR_NS', LimitUsageForNSLine], + ['NBA_NODE_BEGIN', NBANodeBegin], + ['NBA_NODE_DETAIL', NBANodeDetail], + ['NBA_NODE_END', NBANodeEnd], + ['NBA_NODE_ERROR', NBANodeError], + ['NBA_OFFER_INVALID', NBAOfferInvalid], + ['NBA_STRATEGY_BEGIN', NBAStrategyBegin], + ['NBA_STRATEGY_END', NBAStrategyEnd], + ['NBA_STRATEGY_ERROR', NBAStrategyError], + ['POP_TRACE_FLAGS', PopTraceFlagsLine], + ['PUSH_TRACE_FLAGS', PushTraceFlagsLine], + ['QUERY_MORE_BEGIN', QueryMoreBeginLine], + ['QUERY_MORE_END', QueryMoreEndLine], + ['QUERY_MORE_ITERATIONS', QueryMoreIterationsLine], + ['TOTAL_EMAIL_RECIPIENTS_QUEUED', TotalEmailRecipientsQueuedLine], + ['SAVEPOINT_ROLLBACK', SavepointRollbackLine], + ['SAVEPOINT_SET', SavePointSetLine], + ['STACK_FRAME_VARIABLE_LIST', StackFrameVariableListLine], + ['STATIC_VARIABLE_LIST', StaticVariableListLine], + ['SYSTEM_MODE_ENTER', SystemModeEnterLine], + ['SYSTEM_MODE_EXIT', SystemModeExitLine], + ['EXECUTION_STARTED', ExecutionStartedLine], + ['ENTERING_MANAGED_PKG', EnteringManagedPackageLine], + ['EVENT_SERVICE_PUB_BEGIN', EventSericePubBeginLine], + ['EVENT_SERVICE_PUB_END', EventSericePubEndLine], + ['EVENT_SERVICE_PUB_DETAIL', EventSericePubDetailLine], + ['EVENT_SERVICE_SUB_BEGIN', EventSericeSubBeginLine], + ['EVENT_SERVICE_SUB_DETAIL', EventSericeSubDetailLine], + ['EVENT_SERVICE_SUB_END', EventSericeSubEndLine], + ['FLOW_START_INTERVIEWS_BEGIN', FlowStartInterviewsBeginLine], + ['FLOW_START_INTERVIEWS_ERROR', FlowStartInterviewsErrorLine], + ['FLOW_START_INTERVIEW_BEGIN', FlowStartInterviewBeginLine], + ['FLOW_START_INTERVIEW_LIMIT_USAGE', FlowStartInterviewLimitUsageLine], + ['FLOW_START_SCHEDULED_RECORDS', FlowStartScheduledRecordsLine], + ['FLOW_CREATE_INTERVIEW_ERROR', FlowCreateInterviewErrorLine], + ['FLOW_ELEMENT_BEGIN', FlowElementBeginLine], + ['FLOW_ELEMENT_DEFERRED', FlowElementDeferredLine], + ['FLOW_ELEMENT_ERROR', FlowElementErrorLine], + ['FLOW_ELEMENT_FAULT', FlowElementFaultLine], + ['FLOW_ELEMENT_LIMIT_USAGE', FlowElementLimitUsageLine], + ['FLOW_INTERVIEW_FINISHED_LIMIT_USAGE', FlowInterviewFinishedLimitUsageLine], + ['FLOW_SUBFLOW_DETAIL', FlowSubflowDetailLine], + ['FLOW_VALUE_ASSIGNMENT', FlowElementAssignmentLine], + ['FLOW_WAIT_EVENT_RESUMING_DETAIL', FlowWaitEventResumingDetailLine], + ['FLOW_WAIT_EVENT_WAITING_DETAIL', FlowWaitEventWaitingDetailLine], + ['FLOW_WAIT_RESUMING_DETAIL', FlowWaitResumingDetailLine], + ['FLOW_WAIT_WAITING_DETAIL', FlowWaitWaitingDetailLine], + ['FLOW_INTERVIEW_FINISHED', FlowInterviewFinishedLine], + ['FLOW_INTERVIEW_PAUSED', FlowInterviewPausedLine], + ['FLOW_INTERVIEW_RESUMED', FlowInterviewResumedLine], + ['FLOW_ACTIONCALL_DETAIL', FlowActionCallDetailLine], + ['FLOW_ASSIGNMENT_DETAIL', FlowAssignmentDetailLine], + ['FLOW_LOOP_DETAIL', FlowLoopDetailLine], + ['FLOW_RULE_DETAIL', FlowRuleDetailLine], + ['FLOW_BULK_ELEMENT_BEGIN', FlowBulkElementBeginLine], + ['FLOW_BULK_ELEMENT_DETAIL', FlowBulkElementDetailLine], + ['FLOW_BULK_ELEMENT_LIMIT_USAGE', FlowBulkElementLimitUsageLine], + ['FLOW_BULK_ELEMENT_NOT_SUPPORTED', FlowBulkElementNotSupportedLine], + ['MATCH_ENGINE_BEGIN', MatchEngineBegin], + ['ORG_CACHE_PUT_BEGIN', OrgCachePutBegin], + ['ORG_CACHE_GET_BEGIN', OrgCacheGetBegin], + ['ORG_CACHE_REMOVE_BEGIN', OrgCacheRemoveBegin], + ['PUSH_NOTIFICATION_INVALID_APP', PNInvalidAppLine], + ['PUSH_NOTIFICATION_INVALID_CERTIFICATE', PNInvalidCertificateLine], + ['PUSH_NOTIFICATION_INVALID_NOTIFICATION', PNInvalidNotificationLine], + ['PUSH_NOTIFICATION_NO_DEVICES', PNNoDevicesLine], + ['PUSH_NOTIFICATION_SENT', PNSentLine], + ['SESSION_CACHE_PUT_BEGIN', SessionCachePutBegin], + ['SESSION_CACHE_GET_BEGIN', SessionCacheGetBegin], + ['SESSION_CACHE_REMOVE_BEGIN', SessionCacheRemoveBegin], + ['SLA_END', SLAEndLine], + ['SLA_EVAL_MILESTONE', SLAEvalMilestoneLine], + ['SLA_PROCESS_CASE', SLAProcessCaseLine], + ['TESTING_LIMITS', TestingLimitsLine], + ['VALIDATION_ERROR', ValidationErrorLine], + ['VALIDATION_FORMULA', ValidationFormulaLine], + ['VALIDATION_PASS', ValidationPassLine], + ['VALIDATION_RULE', ValidationRuleLine], + ['WF_FLOW_ACTION_ERROR', WFFlowActionErrorLine], + ['WF_FLOW_ACTION_ERROR_DETAIL', WFFlowActionErrorDetailLine], + ['WF_FIELD_UPDATE', WFFieldUpdateLine], + ['WF_RULE_EVAL_BEGIN', WFRuleEvalBeginLine], + ['WF_RULE_EVAL_VALUE', WFRuleEvalValueLine], + ['WF_RULE_FILTER', WFRuleFilterLine], + ['WF_CRITERIA_BEGIN', WFCriteriaBeginLine], + ['WF_FORMULA', WFFormulaLine], + ['WF_ACTION', WFActionLine], + ['WF_ACTIONS_END', WFActionsEndLine], + ['WF_ACTION_TASK', WFActionTaskLine], + ['WF_APPROVAL', WFApprovalLine], + ['WF_APPROVAL_REMOVE', WFApprovalRemoveLine], + ['WF_APPROVAL_SUBMIT', WFApprovalSubmitLine], + ['WF_APPROVAL_SUBMITTER', WFApprovalSubmitterLine], + ['WF_ASSIGN', WFAssignLine], + ['WF_EMAIL_ALERT', WFEmailAlertLine], + ['WF_EMAIL_SENT', WFEmailSentLine], + ['WF_ENQUEUE_ACTIONS', WFEnqueueActionsLine], + ['WF_ESCALATION_ACTION', WFEscalationActionLine], + ['WF_EVAL_ENTRY_CRITERIA', WFEvalEntryCriteriaLine], + ['WF_FLOW_ACTION_DETAIL', WFFlowActionDetailLine], + ['WF_NEXT_APPROVER', WFNextApproverLine], + ['WF_OUTBOUND_MSG', WFOutboundMsgLine], + ['WF_PROCESS_FOUND', WFProcessFoundLine], + ['WF_PROCESS_NODE', WFProcessNode], + ['WF_REASSIGN_RECORD', WFReassignRecordLine], + ['WF_RESPONSE_NOTIFY', WFResponseNotifyLine], + ['WF_RULE_ENTRY_ORDER', WFRuleEntryOrderLine], + ['WF_RULE_INVOCATION', WFRuleInvocationLine], + ['WF_SOFT_REJECT', WFSoftRejectLine], + ['WF_SPOOL_ACTION_BEGIN', WFSpoolActionBeginLine], + ['WF_TIME_TRIGGER', WFTimeTriggerLine], + ['EXCEPTION_THROWN', ExceptionThrownLine], + ['FATAL_ERROR', FatalErrorLine], + ['XDS_DETAIL', XDSDetailLine], + ['XDS_RESPONSE', XDSResponseLine], + ['XDS_RESPONSE_DETAIL', XDSResponseDetailLine], + ['XDS_RESPONSE_ERROR', XDSResponseErrorLine], + ['DUPLICATE_DETECTION_BEGIN', DuplicateDetectionBegin], + ['DUPLICATE_DETECTION_RULE_INVOCATION', DuplicateDetectionRule], + ['DUPLICATE_DETECTION_MATCH_INVOCATION_DETAILS', DuplicateDetectionDetails], + ['DUPLICATE_DETECTION_MATCH_INVOCATION_SUMMARY', DuplicateDetectionSummary], +]); + +const basicLogEvents: LogEventType[] = [ + 'BULK_COUNTABLE_STATEMENT_EXECUTE', + 'TEMPLATE_PROCESSING_ERROR', + 'EXTERNAL_SERVICE_REQUEST', + 'FLOW_CREATE_INTERVIEW_BEGIN', + 'FLOW_CREATE_INTERVIEW_END', + 'VARIABLE_SCOPE_END', + 'PUSH_NOTIFICATION_NOT_ENABLED', + 'SLA_NULL_START_DATE', + 'TEMPLATE_PROCESSING_ERROR', + 'VALIDATION_FAIL', + `WF_FLOW_ACTION_BEGIN`, + 'WF_FLOW_ACTION_END', + 'WF_ESCALATION_RULE', + 'WF_HARD_REJECT', + 'WF_NO_PROCESS_FOUND', + 'WF_TIME_TRIGGERS_BEGIN', + 'WF_KNOWLEDGE_ACTION', + 'WF_SEND_ACTION', + 'WAVE_APP_LIFECYCLE', + 'WF_QUICK_CREATE', + 'WF_APEX_ACTION', + 'INVOCABLE_ACTION_DETAIL', + 'INVOCABLE_ACTION_ERROR', + 'FLOW_COLLECTION_PROCESSOR_DETAIL', + 'FLOW_SCHEDULED_PATH_QUEUED', + 'ROUTE_WORK_ACTION', + 'ADD_SKILL_REQUIREMENT_ACTION', + 'ADD_SCREEN_POP_ACTION', + 'CALLOUT_REQUEST_PREPARE', + 'CALLOUT_REQUEST_FINALIZE', + 'FUNCTION_INVOCATION_REQUEST', + 'APP_CONTAINER_INITIATED', + 'FUNCTION_INVOCATION_RESPONSE', + 'XDS_REQUEST_DETAIL', + 'EXTERNAL_SERVICE_RESPONSE', + 'DATAWEAVE_USER_DEBUG', + 'USER_DEBUG_FINER', + 'USER_DEBUG_FINEST', + 'USER_DEBUG_FINE', + 'USER_DEBUG_DEBUG', + 'USER_DEBUG_INFO', + 'USER_DEBUG_WARN', + 'USER_DEBUG_ERROR', + 'VF_APEX_CALL', + 'HEAP_DUMP', + 'SCRIPT_EXECUTION', + 'SESSION_CACHE_MEMORY_USAGE', + 'ORG_CACHE_MEMORY_USAGE', + 'AE_PERSIST_VALIDATION', + 'REFERENCED_OBJECT_LIST', + 'DUPLICATE_RULE_FILTER', + 'DUPLICATE_RULE_FILTER_RESULT', + 'DUPLICATE_RULE_FILTER_VALUE', + 'TEMPLATED_ASSET', + 'TRANSFORMATION_SUMMARY', + 'RULES_EXECUTION_SUMMARY', + 'ASSET_DIFF_SUMMARY', + 'ASSET_DIFF_DETAIL', + 'RULES_EXECUTION_DETAIL', + 'JSON_DIFF_SUMMARY', + 'JSON_DIFF_DETAIL', + 'MATCH_ENGINE_INVOCATION', +]; + +const basicExitLogEvents: LogEventType[] = [ + 'FLOW_START_INTERVIEW_END', + 'VF_DESERIALIZE_VIEWSTATE_END', + 'VF_SERIALIZE_VIEWSTATE_END', + 'CUMULATIVE_LIMIT_USAGE_END', + 'CUMULATIVE_PROFILING_END', + 'EXECUTION_FINISHED', + 'FLOW_START_INTERVIEWS_END', + 'FLOW_ELEMENT_END', + 'FLOW_BULK_ELEMENT_END', + 'WF_RULE_EVAL_END', + 'WF_RULE_NOT_EVALUATED', + 'WF_CRITERIA_END', + 'DUPLICATE_DETECTION_END', + 'VF_SERIALIZE_CONTINUATION_STATE_END', + 'VF_DESERIALIZE_CONTINUATION_STATE_END', + 'MATCH_ENGINE_END', + 'ORG_CACHE_PUT_END', + 'ORG_CACHE_GET_END', + 'ORG_CACHE_REMOVE_END', + 'SESSION_CACHE_PUT_END', + 'SESSION_CACHE_GET_END', + 'SESSION_CACHE_REMOVE_END', +]; diff --git a/log-viewer/modules/parsers/types.ts b/log-viewer/modules/parsers/types.ts new file mode 100644 index 00000000..283440e6 --- /dev/null +++ b/log-viewer/modules/parsers/types.ts @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2020 Certinia Inc. All rights reserved. + */ + +export type CPUType = 'loading' | 'custom' | 'method' | 'free' | 'system' | 'pkg' | ''; + +export type IssueType = 'unexpected' | 'error' | 'skip'; + +export type LineNumber = number | 'EXTERNAL' | null; // an actual line-number or 'EXTERNAL' + +export type LogSubCategory = + | '' + | 'Method' + | 'System Method' + | 'Code Unit' + | 'DML' + | 'SOQL' + | 'Flow' + | 'Workflow'; + +export interface LogIssue { + startTime?: number; + summary: string; + description: string; + type: IssueType; +} +export type LogLineConstructor = new (parser: P, parts: string[]) => T; + +export type LogEventType = (typeof _logEventNames)[number]; + +export interface SelfTotal { + self: number; + total: number; +} + +const _logEventNames = [ + 'BULK_DML_RETRY', + 'BULK_HEAP_ALLOCATE', + 'CALLOUT_REQUEST', + 'CALLOUT_RESPONSE', + 'NAMED_CREDENTIAL_REQUEST', + 'NAMED_CREDENTIAL_RESPONSE', + 'NAMED_CREDENTIAL_RESPONSE_DETAIL', + 'CONSTRUCTOR_ENTRY', + 'CONSTRUCTOR_EXIT', + 'EMAIL_QUEUE', + 'METHOD_ENTRY', + 'METHOD_EXIT', + 'SYSTEM_CONSTRUCTOR_ENTRY', + 'SYSTEM_CONSTRUCTOR_EXIT', + 'SYSTEM_METHOD_ENTRY', + 'SYSTEM_METHOD_EXIT', + 'CODE_UNIT_STARTED', + 'CODE_UNIT_FINISHED', + 'VF_APEX_CALL_START', + 'VF_APEX_CALL_END', + 'VF_DESERIALIZE_VIEWSTATE_BEGIN', + 'VF_EVALUATE_FORMULA_BEGIN', + 'VF_EVALUATE_FORMULA_END', + 'VF_SERIALIZE_CONTINUATION_STATE_BEGIN', + 'VF_DESERIALIZE_CONTINUATION_STATE_BEGIN', + 'VF_SERIALIZE_VIEWSTATE_BEGIN', + 'VF_PAGE_MESSAGE', + 'DML_BEGIN', + 'DML_END', + 'IDEAS_QUERY_EXECUTE', + 'SOQL_EXECUTE_BEGIN', + 'SOQL_EXECUTE_END', + 'SOQL_EXECUTE_EXPLAIN', + 'SOSL_EXECUTE_BEGIN', + 'SOSL_EXECUTE_END', + 'HEAP_ALLOCATE', + 'HEAP_DEALLOCATE', + 'STATEMENT_EXECUTE', + 'VARIABLE_SCOPE_BEGIN', + 'VARIABLE_ASSIGNMENT', + 'USER_INFO', + 'USER_DEBUG', + 'CUMULATIVE_LIMIT_USAGE', + 'CUMULATIVE_PROFILING', + 'CUMULATIVE_PROFILING_BEGIN', + 'LIMIT_USAGE', + 'LIMIT_USAGE_FOR_NS', + 'NBA_NODE_BEGIN', + 'NBA_NODE_DETAIL', + 'NBA_NODE_END', + 'NBA_NODE_ERROR', + 'NBA_OFFER_INVALID', + 'NBA_STRATEGY_BEGIN', + 'NBA_STRATEGY_END', + 'NBA_STRATEGY_ERROR', + 'POP_TRACE_FLAGS', + 'PUSH_TRACE_FLAGS', + 'QUERY_MORE_BEGIN', + 'QUERY_MORE_END', + 'QUERY_MORE_ITERATIONS', + 'TOTAL_EMAIL_RECIPIENTS_QUEUED', + 'SAVEPOINT_ROLLBACK', + 'SAVEPOINT_SET', + 'STACK_FRAME_VARIABLE_LIST', + 'STATIC_VARIABLE_LIST', + 'SYSTEM_MODE_ENTER', + 'SYSTEM_MODE_EXIT', + 'EXECUTION_STARTED', + 'ENTERING_MANAGED_PKG', + 'EVENT_SERVICE_PUB_BEGIN', + 'EVENT_SERVICE_PUB_END', + 'EVENT_SERVICE_PUB_DETAIL', + 'EVENT_SERVICE_SUB_BEGIN', + 'EVENT_SERVICE_SUB_DETAIL', + 'EVENT_SERVICE_SUB_END', + 'FLOW_START_INTERVIEWS_BEGIN', + 'FLOW_START_INTERVIEWS_ERROR', + 'FLOW_START_INTERVIEW_BEGIN', + 'FLOW_START_INTERVIEW_LIMIT_USAGE', + 'FLOW_START_SCHEDULED_RECORDS', + 'FLOW_CREATE_INTERVIEW_ERROR', + 'FLOW_ELEMENT_BEGIN', + 'FLOW_ELEMENT_DEFERRED', + 'FLOW_ELEMENT_ERROR', + 'FLOW_ELEMENT_FAULT', + 'FLOW_ELEMENT_LIMIT_USAGE', + 'FLOW_INTERVIEW_FINISHED_LIMIT_USAGE', + 'FLOW_SUBFLOW_DETAIL', + 'FLOW_VALUE_ASSIGNMENT', + 'FLOW_WAIT_EVENT_RESUMING_DETAIL', + 'FLOW_WAIT_EVENT_WAITING_DETAIL', + 'FLOW_WAIT_RESUMING_DETAIL', + 'FLOW_WAIT_WAITING_DETAIL', + 'FLOW_INTERVIEW_FINISHED', + 'FLOW_INTERVIEW_PAUSED', + 'FLOW_INTERVIEW_RESUMED', + 'FLOW_ACTIONCALL_DETAIL', + 'FLOW_ASSIGNMENT_DETAIL', + 'FLOW_LOOP_DETAIL', + 'FLOW_RULE_DETAIL', + 'FLOW_BULK_ELEMENT_BEGIN', + 'FLOW_BULK_ELEMENT_DETAIL', + 'FLOW_BULK_ELEMENT_LIMIT_USAGE', + 'FLOW_BULK_ELEMENT_NOT_SUPPORTED', + 'MATCH_ENGINE_BEGIN', + 'ORG_CACHE_PUT_BEGIN', + 'ORG_CACHE_GET_BEGIN', + 'ORG_CACHE_REMOVE_BEGIN', + 'PUSH_NOTIFICATION_INVALID_APP', + 'PUSH_NOTIFICATION_INVALID_CERTIFICATE', + 'PUSH_NOTIFICATION_INVALID_NOTIFICATION', + 'PUSH_NOTIFICATION_NO_DEVICES', + 'PUSH_NOTIFICATION_SENT', + 'SESSION_CACHE_PUT_BEGIN', + 'SESSION_CACHE_GET_BEGIN', + 'SESSION_CACHE_REMOVE_BEGIN', + 'SLA_END', + 'SLA_EVAL_MILESTONE', + 'SLA_PROCESS_CASE', + 'TESTING_LIMITS', + 'VALIDATION_ERROR', + 'VALIDATION_FORMULA', + 'VALIDATION_PASS', + 'VALIDATION_RULE', + 'WF_FLOW_ACTION_ERROR', + 'WF_FLOW_ACTION_ERROR_DETAIL', + 'WF_FIELD_UPDATE', + 'WF_RULE_EVAL_BEGIN', + 'WF_RULE_EVAL_VALUE', + 'WF_RULE_FILTER', + 'WF_CRITERIA_BEGIN', + 'WF_FORMULA', + 'WF_ACTION', + 'WF_ACTIONS_END', + 'WF_ACTION_TASK', + 'WF_APPROVAL', + 'WF_APPROVAL_REMOVE', + 'WF_APPROVAL_SUBMIT', + 'WF_APPROVAL_SUBMITTER', + 'WF_ASSIGN', + 'WF_EMAIL_ALERT', + 'WF_EMAIL_SENT', + 'WF_ENQUEUE_ACTIONS', + 'WF_ESCALATION_ACTION', + 'WF_EVAL_ENTRY_CRITERIA', + 'WF_FLOW_ACTION_DETAIL', + 'WF_NEXT_APPROVER', + 'WF_OUTBOUND_MSG', + 'WF_PROCESS_FOUND', + 'WF_PROCESS_NODE', + 'WF_REASSIGN_RECORD', + 'WF_RESPONSE_NOTIFY', + 'WF_RULE_ENTRY_ORDER', + 'WF_RULE_INVOCATION', + 'WF_SOFT_REJECT', + 'WF_SPOOL_ACTION_BEGIN', + 'WF_TIME_TRIGGER', + 'EXCEPTION_THROWN', + 'FATAL_ERROR', + 'XDS_DETAIL', + 'XDS_RESPONSE', + 'XDS_RESPONSE_DETAIL', + 'XDS_RESPONSE_ERROR', + 'DUPLICATE_DETECTION_BEGIN', + 'DUPLICATE_DETECTION_RULE_INVOCATION', + 'DUPLICATE_DETECTION_MATCH_INVOCATION_DETAILS', + 'DUPLICATE_DETECTION_MATCH_INVOCATION_SUMMARY', + 'BULK_COUNTABLE_STATEMENT_EXECUTE', + 'TEMPLATE_PROCESSING_ERROR', + 'EXTERNAL_SERVICE_REQUEST', + 'FLOW_START_INTERVIEW_END', + 'FLOW_CREATE_INTERVIEW_BEGIN', + 'FLOW_CREATE_INTERVIEW_END', + 'VARIABLE_SCOPE_END', + 'PUSH_NOTIFICATION_NOT_ENABLED', + 'SLA_NULL_START_DATE', + 'TEMPLATE_PROCESSING_ERROR', + 'VALIDATION_FAIL', + `WF_FLOW_ACTION_BEGIN`, + 'WF_FLOW_ACTION_END', + 'WF_ESCALATION_RULE', + 'WF_HARD_REJECT', + 'WF_NO_PROCESS_FOUND', + 'WF_TIME_TRIGGERS_BEGIN', + 'WF_KNOWLEDGE_ACTION', + 'WF_SEND_ACTION', + 'WAVE_APP_LIFECYCLE', + 'WF_QUICK_CREATE', + 'WF_APEX_ACTION', + 'INVOCABLE_ACTION_DETAIL', + 'INVOCABLE_ACTION_ERROR', + 'FLOW_COLLECTION_PROCESSOR_DETAIL', + 'FLOW_SCHEDULED_PATH_QUEUED', + 'ROUTE_WORK_ACTION', + 'ADD_SKILL_REQUIREMENT_ACTION', + 'ADD_SCREEN_POP_ACTION', + 'CALLOUT_REQUEST_PREPARE', + 'CALLOUT_REQUEST_FINALIZE', + 'FUNCTION_INVOCATION_REQUEST', + 'APP_CONTAINER_INITIATED', + 'FUNCTION_INVOCATION_RESPONSE', + 'XDS_REQUEST_DETAIL', + 'EXTERNAL_SERVICE_RESPONSE', + 'DATAWEAVE_USER_DEBUG', + 'USER_DEBUG_FINER', + 'USER_DEBUG_FINEST', + 'USER_DEBUG_FINE', + 'USER_DEBUG_DEBUG', + 'USER_DEBUG_INFO', + 'USER_DEBUG_WARN', + 'USER_DEBUG_ERROR', + 'VF_APEX_CALL', + 'HEAP_DUMP', + 'SCRIPT_EXECUTION', + 'SESSION_CACHE_MEMORY_USAGE', + 'ORG_CACHE_MEMORY_USAGE', + 'AE_PERSIST_VALIDATION', + 'REFERENCED_OBJECT_LIST', + 'DUPLICATE_RULE_FILTER', + 'DUPLICATE_RULE_FILTER_RESULT', + 'DUPLICATE_RULE_FILTER_VALUE', + 'TEMPLATED_ASSET', + 'TRANSFORMATION_SUMMARY', + 'RULES_EXECUTION_SUMMARY', + 'ASSET_DIFF_SUMMARY', + 'ASSET_DIFF_DETAIL', + 'RULES_EXECUTION_DETAIL', + 'JSON_DIFF_SUMMARY', + 'JSON_DIFF_DETAIL', + 'MATCH_ENGINE_INVOCATION', + 'VF_DESERIALIZE_VIEWSTATE_END', + 'VF_SERIALIZE_VIEWSTATE_END', + 'CUMULATIVE_LIMIT_USAGE_END', + 'CUMULATIVE_PROFILING_END', + 'EXECUTION_FINISHED', + 'FLOW_START_INTERVIEWS_END', + 'FLOW_ELEMENT_END', + 'FLOW_BULK_ELEMENT_END', + 'WF_RULE_EVAL_END', + 'WF_RULE_NOT_EVALUATED', + 'WF_CRITERIA_END', + 'DUPLICATE_DETECTION_END', + 'VF_SERIALIZE_CONTINUATION_STATE_END', + 'VF_DESERIALIZE_CONTINUATION_STATE_END', + 'MATCH_ENGINE_END', + 'ORG_CACHE_PUT_END', + 'ORG_CACHE_GET_END', + 'ORG_CACHE_REMOVE_END', + 'SESSION_CACHE_PUT_END', + 'SESSION_CACHE_GET_END', + 'SESSION_CACHE_REMOVE_END', +] as const; diff --git a/log-viewer/modules/timeline/Timeline.ts b/log-viewer/modules/timeline/Timeline.ts index 51af7094..57af1211 100644 --- a/log-viewer/modules/timeline/Timeline.ts +++ b/log-viewer/modules/timeline/Timeline.ts @@ -4,16 +4,8 @@ //TODO:Refactor - usage should look more like `new TimeLine(timelineContainer, {tooltip:true}:Config)`; import formatDuration, { debounce } from '../Util.js'; import { goToRow } from '../components/calltree-view/CalltreeView.js'; -import { - ApexLog, - LogLine, - Method, - TimedNode, - type LogIssue, - type LogSubCategory, -} from '../parsers/ApexLogParser.js'; - -export { ApexLog }; +import type { ApexLog, LogEvent } from '../parsers/LogEvents.js'; +import type { LogIssue, LogSubCategory } from '../parsers/types.js'; export interface TimelineGroup { label: string; @@ -173,13 +165,13 @@ let findArgs: { text: string; count: number; options: { matchCase: boolean } } = }; let totalMatches = 0; -function getMaxDepth(nodes: LogLine[]): number { +function getMaxDepth(nodes: LogEvent[]): number { let maxDepth = 0; let currentLevel = nodes.filter((n) => n.exitTypes.length); while (currentLevel.length) { maxDepth++; - const nextLevel: LogLine[] = []; + const nextLevel: LogEvent[] = []; for (const node of currentLevel) { for (const child of node.children) { if (child.exitTypes.length) { @@ -257,12 +249,13 @@ function drawScale(ctx: CanvasRenderingContext2D) { ctx.stroke(); } -function nodesToRectangles(rootNodes: Method[]) { +function nodesToRectangles(rootNodes: LogEvent[]) { + // seed depth 0 let depth = 0; let currentLevel = rootNodes.filter((n) => n.exitTypes.length); while (currentLevel.length) { - const nextLevel: Method[] = []; + const nextLevel: LogEvent[] = []; for (const node of currentLevel) { if (node.subCategory && node.duration) { @@ -270,7 +263,7 @@ function nodesToRectangles(rootNodes: Method[]) { } for (const child of node.children) { - if (child instanceof Method) { + if (child.exitTypes.length) { nextLevel.push(child); } } @@ -297,14 +290,14 @@ let currentFindMatchColor = '#9e6a03'; * @param node The node to be rendered * @param y The call depth of the node */ -function addToRectQueue(node: Method, y: number) { +function addToRectQueue(node: LogEvent, y: number) { const { subCategory: subCategory, timestamp: x, duration: { total: w }, } = node; - let borderColor = ''; + let borderColor = ''; if (hasFindMatch(node)) { borderColor = findMatchColor; } @@ -323,15 +316,15 @@ function addToRectQueue(node: Method, y: number) { borders.push(rect); } -function hasFindMatch(node: Method) { +function hasFindMatch(node: LogEvent) { if (!searchString || !node) { return false; } - const nodeType = node.type ?? ''; + const nodeType = node.type; const matchType = findArgs.options.matchCase - ? nodeType.includes(searchString) - : nodeType.toLowerCase().includes(searchString); + ? nodeType?.includes(searchString) + : nodeType?.toLowerCase().includes(searchString); if (matchType) { return matchType; } @@ -560,7 +553,7 @@ export function init(timelineContainer: HTMLDivElement, rootMethod: ApexLog) { onInitTimeline(); calculateSizes(); - nodesToRectangles(timelineRoot.children as Method[]); + nodesToRectangles(timelineRoot.children); if (ctx) { requestAnimationFrame(drawTimeLine); } @@ -593,12 +586,12 @@ function drawTimeLine() { } function findByPosition( - nodes: LogLine[], + nodes: LogEvent[], depth: number, x: number, targetDepth: number, shouldIgnoreWidth: boolean, -): LogLine | null { +): LogEvent | null { if (!nodes) { return null; } @@ -656,7 +649,7 @@ function findTimelineTooltip( ): HTMLDivElement | null { const target = findByPosition(timelineRoot.children, 0, x, depth, shouldIgnoreWidth); - if (target && target instanceof TimedNode) { + if (target) { canvas.classList.remove('timeline-hover', 'timeline-dragging'); canvas.classList.add('timeline-event--hover'); @@ -849,13 +842,7 @@ function onClickCanvas(): void { const isClick = mouseDownPosition.x === lastMouseX && mouseDownPosition.y === lastMouseY; if (!dragging && isClick) { const depth = getDepth(lastMouseY); - let timeStamp = findByPosition( - timelineRoot.children as TimedNode[], - 0, - lastMouseX, - depth, - false, - )?.timestamp; + let timeStamp = findByPosition(timelineRoot.children, 0, lastMouseX, depth, false)?.timestamp; if (!timeStamp) { timeStamp = findLogIssue(lastMouseX)?.startTime; @@ -974,7 +961,7 @@ function _findOnTimeline( if (newSearch || clearHighlights) { rectRenderQueue.clear(); borderRenderQueue.clear(); - nodesToRectangles(timelineRoot.children as Method[]); + nodesToRectangles(timelineRoot.children); const findResults = borderRenderQueue.get(findMatchColor) || []; totalMatches = findResults.length; diff --git a/log-viewer/modules/timeline/TimelineView.ts b/log-viewer/modules/timeline/TimelineView.ts index f9e18ba9..d5bda4a5 100644 --- a/log-viewer/modules/timeline/TimelineView.ts +++ b/log-viewer/modules/timeline/TimelineView.ts @@ -5,8 +5,9 @@ import { LitElement, css, html, type PropertyValues } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { skeletonStyles } from '../components/skeleton/skeleton.styles.js'; +import type { ApexLog } from '../parsers/LogEvents.js'; import { globalStyles } from '../styles/global.styles.js'; -import { ApexLog, init as timelineInit, type TimelineGroup } from './Timeline.js'; +import { init as timelineInit, type TimelineGroup } from './Timeline.js'; import './TimelineKey.js'; @customElement('timeline-view')