@orbytautomation/engine 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/OrbytEngine.d.ts +3 -21
- package/dist/core/OrbytEngine.d.ts.map +1 -1
- package/dist/core/OrbytEngine.js +48 -139
- package/dist/core/OrbytEngine.js.map +1 -1
- package/dist/errors/ErrorDebugger.d.ts +78 -25
- package/dist/errors/ErrorDebugger.d.ts.map +1 -1
- package/dist/errors/ErrorDebugger.js +383 -2
- package/dist/errors/ErrorDebugger.js.map +1 -1
- package/dist/errors/ErrorDetector.d.ts +107 -5
- package/dist/errors/ErrorDetector.d.ts.map +1 -1
- package/dist/errors/ErrorDetector.js +195 -40
- package/dist/errors/ErrorDetector.js.map +1 -1
- package/dist/errors/ErrorFormatter.d.ts +51 -0
- package/dist/errors/ErrorFormatter.d.ts.map +1 -1
- package/dist/errors/ErrorFormatter.js +128 -0
- package/dist/errors/ErrorFormatter.js.map +1 -1
- package/dist/errors/ErrorHandler.d.ts +93 -7
- package/dist/errors/ErrorHandler.d.ts.map +1 -1
- package/dist/errors/ErrorHandler.js +91 -42
- package/dist/errors/ErrorHandler.js.map +1 -1
- package/dist/errors/OrbytError.d.ts +28 -0
- package/dist/errors/OrbytError.d.ts.map +1 -1
- package/dist/errors/OrbytError.js.map +1 -1
- package/dist/errors/SecurityErrors.d.ts +2 -25
- package/dist/errors/SecurityErrors.d.ts.map +1 -1
- package/dist/errors/SecurityErrors.js +5 -119
- package/dist/errors/SecurityErrors.js.map +1 -1
- package/dist/errors/WorkflowError.d.ts +11 -1
- package/dist/errors/WorkflowError.d.ts.map +1 -1
- package/dist/errors/WorkflowError.js +104 -0
- package/dist/errors/WorkflowError.js.map +1 -1
- package/dist/loader/WorkflowLoader.d.ts +77 -5
- package/dist/loader/WorkflowLoader.d.ts.map +1 -1
- package/dist/loader/WorkflowLoader.js +170 -35
- package/dist/loader/WorkflowLoader.js.map +1 -1
- package/dist/logging/EngineLogger.d.ts +240 -289
- package/dist/logging/EngineLogger.d.ts.map +1 -1
- package/dist/logging/EngineLogger.js +424 -842
- package/dist/logging/EngineLogger.js.map +1 -1
- package/dist/logging/LoggerManager.d.ts +111 -22
- package/dist/logging/LoggerManager.d.ts.map +1 -1
- package/dist/logging/LoggerManager.js +138 -32
- package/dist/logging/LoggerManager.js.map +1 -1
- package/dist/logging/index.d.ts.map +1 -1
- package/dist/logging/index.js.map +1 -1
- package/dist/parser/WorkflowParser.d.ts.map +1 -1
- package/dist/parser/WorkflowParser.js +0 -5
- package/dist/parser/WorkflowParser.js.map +1 -1
- package/dist/security/ReservedFields.d.ts +11 -0
- package/dist/security/ReservedFields.d.ts.map +1 -1
- package/dist/security/ReservedFields.js +71 -34
- package/dist/security/ReservedFields.js.map +1 -1
- package/dist/types/log-types.d.ts +134 -2
- package/dist/types/log-types.d.ts.map +1 -1
- package/dist/types/log-types.js +10 -0
- package/dist/types/log-types.js.map +1 -1
- package/package.json +1 -1
|
@@ -20,34 +20,124 @@
|
|
|
20
20
|
* - Monitoring systems
|
|
21
21
|
* - Audit trails
|
|
22
22
|
*
|
|
23
|
-
* @module
|
|
23
|
+
* @module logging
|
|
24
24
|
*/
|
|
25
25
|
import { LogLevel, LogLevelSeverity, formatLog, createLogEntry, shouldLog, formatTimestamp, } from '@dev-ecosystem/core';
|
|
26
|
-
import { EngineLogType } from '../types/log-types.js';
|
|
26
|
+
import { EngineLogType, LogCategoryEnum, } from '../types/log-types.js';
|
|
27
|
+
// ─── CategoryLogger ────────────────────────────────────────────────────────────
|
|
27
28
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
29
|
+
* A thin wrapper that pins every log call to a specific category.
|
|
30
|
+
*
|
|
31
|
+
* Obtain instances via `EngineLogger.runtime`, `.analysis`, `.system`, or `.security`.
|
|
32
|
+
*
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const logger = LoggerManager.getLogger();
|
|
35
|
+
*
|
|
36
|
+
* // inside run() — runtime category
|
|
37
|
+
* logger.runtime.info('Step started', { stepId });
|
|
38
|
+
*
|
|
39
|
+
* // inside explain() / validate() — analysis category
|
|
40
|
+
* logger.analysis.debug('Building execution plan');
|
|
41
|
+
* ```
|
|
30
42
|
*/
|
|
31
|
-
export
|
|
43
|
+
export class CategoryLogger {
|
|
44
|
+
parent;
|
|
45
|
+
category;
|
|
46
|
+
constructor(parent, category) {
|
|
47
|
+
this.parent = parent;
|
|
48
|
+
this.category = category;
|
|
49
|
+
}
|
|
50
|
+
debug(message, context) {
|
|
51
|
+
this.parent._logWithCategory(LogLevel.DEBUG, message, context, undefined, this.category);
|
|
52
|
+
}
|
|
53
|
+
info(message, context) {
|
|
54
|
+
this.parent._logWithCategory(LogLevel.INFO, message, context, undefined, this.category);
|
|
55
|
+
}
|
|
56
|
+
warn(message, context) {
|
|
57
|
+
this.parent._logWithCategory(LogLevel.WARN, message, context, undefined, this.category);
|
|
58
|
+
}
|
|
59
|
+
error(message, error, context) {
|
|
60
|
+
this.parent._logWithCategory(LogLevel.ERROR, message, context, error, this.category);
|
|
61
|
+
}
|
|
62
|
+
fatal(message, error, context) {
|
|
63
|
+
this.parent._logWithCategory(LogLevel.FATAL, message, context, error, this.category);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Measure execution time of `fn` and log the result under this category.
|
|
67
|
+
*
|
|
68
|
+
* ```typescript
|
|
69
|
+
* const result = await logger.runtime.measureExecution(
|
|
70
|
+
* 'step:http-request',
|
|
71
|
+
* () => httpAdapter.run(step),
|
|
72
|
+
* { warn: 3000, error: 10000 },
|
|
73
|
+
* );
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
async measureExecution(label, fn, thresholds) {
|
|
77
|
+
const start = Date.now();
|
|
78
|
+
try {
|
|
79
|
+
const result = await fn();
|
|
80
|
+
const duration = Date.now() - start;
|
|
81
|
+
let level = LogLevel.INFO;
|
|
82
|
+
if (thresholds?.error && duration > thresholds.error)
|
|
83
|
+
level = LogLevel.ERROR;
|
|
84
|
+
else if (thresholds?.warn && duration > thresholds.warn)
|
|
85
|
+
level = LogLevel.WARN;
|
|
86
|
+
this.parent._logWithCategory(level, `${label} completed`, { duration: `${duration}ms` }, undefined, this.category, EngineLogType.EXECUTION_TIME);
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
const duration = Date.now() - start;
|
|
91
|
+
this.parent._logWithCategory(LogLevel.ERROR, `${label} failed`, { duration: `${duration}ms` }, err instanceof Error ? err : new Error(String(err)), this.category, EngineLogType.ERROR_DETECTED);
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// ─── EngineLogger ──────────────────────────────────────────────────────────────
|
|
32
97
|
/**
|
|
33
98
|
* Engine Logger
|
|
34
99
|
*
|
|
35
100
|
* Wraps ecosystem-core logging utilities with engine-specific configuration.
|
|
101
|
+
* Emits structured JSON logs and maintains a typed log history.
|
|
102
|
+
*
|
|
103
|
+
* ### Category sub-loggers
|
|
104
|
+
* Every log is tagged with a phase-based category so tooling / formatters can
|
|
105
|
+
* filter or colour-code entries without parsing the message text.
|
|
106
|
+
*
|
|
107
|
+
* | Sub-logger | Category | When to use |
|
|
108
|
+
* |--------------------|--------------|---------------------------------------------------|
|
|
109
|
+
* | `logger.runtime` | `runtime` | `run()` — step start/complete/fail/retry |
|
|
110
|
+
* | `logger.analysis` | `analysis` | `explain()` / `validate()` — plan & parse phase |
|
|
111
|
+
* | `logger.system` | `system` | Engine init/shutdown, adapter registration |
|
|
112
|
+
* | `logger.security` | `security` | Reserved-field violations, permission rejections |
|
|
113
|
+
*
|
|
114
|
+
* Generic `logger.info(...)` etc. fall back to the category set at construction.
|
|
36
115
|
*/
|
|
37
116
|
export class EngineLogger {
|
|
38
117
|
config;
|
|
39
118
|
formatOptions;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
119
|
+
logHistory = [];
|
|
120
|
+
/** Workflow context set by the engine before run/explain/validate */
|
|
121
|
+
workflowContext = null;
|
|
122
|
+
// ─── Category sub-loggers ────────────────────────────────────────────────
|
|
123
|
+
/** Use inside `run()` and step-execution paths. Category: `runtime` */
|
|
124
|
+
runtime;
|
|
125
|
+
/** Use inside `explain()` and `validate()` paths. Category: `analysis` */
|
|
126
|
+
analysis;
|
|
127
|
+
/** Use for engine init, shutdown, adapter registration. Category: `system` */
|
|
128
|
+
system;
|
|
129
|
+
/** Use for reserved-field violations, permission rejections. Category: `security` */
|
|
130
|
+
security;
|
|
43
131
|
constructor(config) {
|
|
44
132
|
this.config = {
|
|
45
133
|
level: config.level,
|
|
46
|
-
format: config.format || '
|
|
134
|
+
format: config.format || 'json',
|
|
47
135
|
colors: config.colors ?? true,
|
|
48
136
|
timestamp: config.timestamp ?? true,
|
|
49
137
|
source: config.source || 'Orbyt',
|
|
50
|
-
structuredEvents: config.structuredEvents ?? true,
|
|
138
|
+
structuredEvents: config.structuredEvents ?? true,
|
|
139
|
+
category: config.category || LogCategoryEnum.SYSTEM,
|
|
140
|
+
maxHistorySize: config.maxHistorySize ?? 0,
|
|
51
141
|
};
|
|
52
142
|
this.formatOptions = {
|
|
53
143
|
format: this.config.format,
|
|
@@ -55,8 +145,11 @@ export class EngineLogger {
|
|
|
55
145
|
timestamp: this.config.timestamp,
|
|
56
146
|
includeSource: true,
|
|
57
147
|
};
|
|
58
|
-
|
|
59
|
-
this.
|
|
148
|
+
// Bind category sub-loggers
|
|
149
|
+
this.runtime = new CategoryLogger(this, LogCategoryEnum.RUNTIME);
|
|
150
|
+
this.analysis = new CategoryLogger(this, LogCategoryEnum.ANALYSIS);
|
|
151
|
+
this.system = new CategoryLogger(this, LogCategoryEnum.SYSTEM);
|
|
152
|
+
this.security = new CategoryLogger(this, LogCategoryEnum.SECURITY);
|
|
60
153
|
}
|
|
61
154
|
/**
|
|
62
155
|
* Log a debug message
|
|
@@ -88,670 +181,417 @@ export class EngineLogger {
|
|
|
88
181
|
fatal(message, error, context) {
|
|
89
182
|
this.log(LogLevel.FATAL, message, context, error);
|
|
90
183
|
}
|
|
91
|
-
// ============================================================================
|
|
92
|
-
// WORKFLOW LIFECYCLE LOGGING
|
|
93
|
-
// ============================================================================
|
|
94
|
-
/**
|
|
95
|
-
* Log workflow started event
|
|
96
|
-
*/
|
|
97
|
-
workflowStarted(workflowName, context) {
|
|
98
|
-
this.logEvent({
|
|
99
|
-
type: EngineLogType.WORKFLOW_STARTED,
|
|
100
|
-
timestamp: new Date(),
|
|
101
|
-
message: `Workflow "${workflowName}" started`,
|
|
102
|
-
context,
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Log workflow completed event
|
|
107
|
-
*/
|
|
108
|
-
workflowCompleted(workflowName, duration, context) {
|
|
109
|
-
this.logEvent({
|
|
110
|
-
type: EngineLogType.WORKFLOW_COMPLETED,
|
|
111
|
-
timestamp: new Date(),
|
|
112
|
-
message: `Workflow "${workflowName}" completed successfully`,
|
|
113
|
-
context,
|
|
114
|
-
metrics: { duration },
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Log workflow failed event
|
|
119
|
-
*/
|
|
120
|
-
workflowFailed(workflowName, error, duration, context) {
|
|
121
|
-
this.logEvent({
|
|
122
|
-
type: EngineLogType.WORKFLOW_FAILED,
|
|
123
|
-
timestamp: new Date(),
|
|
124
|
-
message: `Workflow "${workflowName}" failed: ${error.message}`,
|
|
125
|
-
context,
|
|
126
|
-
error,
|
|
127
|
-
metrics: { duration },
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Log workflow validation event
|
|
132
|
-
*/
|
|
133
|
-
workflowValidation(workflowName, isValid, errors) {
|
|
134
|
-
this.logEvent({
|
|
135
|
-
type: EngineLogType.WORKFLOW_VALIDATION,
|
|
136
|
-
timestamp: new Date(),
|
|
137
|
-
message: `Workflow "${workflowName}" validation: ${isValid ? 'PASSED' : 'FAILED'}`,
|
|
138
|
-
context: errors ? { errors } : undefined,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
// ============================================================================
|
|
142
|
-
// STEP LIFECYCLE LOGGING
|
|
143
|
-
// ============================================================================
|
|
144
|
-
/**
|
|
145
|
-
* Log step started event
|
|
146
|
-
*/
|
|
147
|
-
stepStarted(stepId, stepName, context) {
|
|
148
|
-
this.logEvent({
|
|
149
|
-
type: EngineLogType.STEP_STARTED,
|
|
150
|
-
timestamp: new Date(),
|
|
151
|
-
message: `Step "${stepName}" (${stepId}) started`,
|
|
152
|
-
context,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Log step completed event
|
|
157
|
-
*/
|
|
158
|
-
stepCompleted(stepId, stepName, duration, context) {
|
|
159
|
-
this.logEvent({
|
|
160
|
-
type: EngineLogType.STEP_COMPLETED,
|
|
161
|
-
timestamp: new Date(),
|
|
162
|
-
message: `Step "${stepName}" (${stepId}) completed`,
|
|
163
|
-
context,
|
|
164
|
-
metrics: { duration },
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Log step failed event
|
|
169
|
-
*/
|
|
170
|
-
stepFailed(stepId, stepName, error, context) {
|
|
171
|
-
this.logEvent({
|
|
172
|
-
type: EngineLogType.STEP_FAILED,
|
|
173
|
-
timestamp: new Date(),
|
|
174
|
-
message: `Step "${stepName}" (${stepId}) failed: ${error.message}`,
|
|
175
|
-
context,
|
|
176
|
-
error,
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Log step retry event
|
|
181
|
-
*/
|
|
182
|
-
stepRetry(stepId, stepName, attempt, maxAttempts) {
|
|
183
|
-
this.logEvent({
|
|
184
|
-
type: EngineLogType.STEP_RETRY,
|
|
185
|
-
timestamp: new Date(),
|
|
186
|
-
message: `Step "${stepName}" (${stepId}) retry ${attempt}/${maxAttempts}`,
|
|
187
|
-
context: { attempt, maxAttempts },
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Log step timeout event
|
|
192
|
-
*/
|
|
193
|
-
stepTimeout(stepId, stepName, timeout) {
|
|
194
|
-
this.logEvent({
|
|
195
|
-
type: EngineLogType.STEP_TIMEOUT,
|
|
196
|
-
timestamp: new Date(),
|
|
197
|
-
message: `Step "${stepName}" (${stepId}) timed out after ${timeout}ms`,
|
|
198
|
-
context: { timeout },
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
// ============================================================================
|
|
202
|
-
// EXPLANATION LOGGING
|
|
203
|
-
// ============================================================================
|
|
204
|
-
/**
|
|
205
|
-
* Log explanation generated event
|
|
206
|
-
*/
|
|
207
|
-
explanationGenerated(workflowName, stepCount, strategy) {
|
|
208
|
-
this.logEvent({
|
|
209
|
-
type: EngineLogType.EXPLANATION_GENERATED,
|
|
210
|
-
timestamp: new Date(),
|
|
211
|
-
message: `Explanation generated for "${workflowName}" (${stepCount} steps, ${strategy} strategy)`,
|
|
212
|
-
context: { workflowName, stepCount, strategy },
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
184
|
/**
|
|
216
|
-
* Log
|
|
217
|
-
*/
|
|
218
|
-
explanationCycles(workflowName, cycles) {
|
|
219
|
-
this.logEvent({
|
|
220
|
-
type: EngineLogType.EXPLANATION_CYCLES,
|
|
221
|
-
timestamp: new Date(),
|
|
222
|
-
message: `Circular dependencies detected in "${workflowName}"`,
|
|
223
|
-
context: { cycles },
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
// ============================================================================
|
|
227
|
-
// ADAPTER & PLUGIN LOGGING
|
|
228
|
-
// ============================================================================
|
|
229
|
-
/**
|
|
230
|
-
* Log adapter loaded event
|
|
231
|
-
*/
|
|
232
|
-
adapterLoaded(adapterName, version) {
|
|
233
|
-
this.logEvent({
|
|
234
|
-
type: EngineLogType.ADAPTER_LOADED,
|
|
235
|
-
timestamp: new Date(),
|
|
236
|
-
message: `Adapter "${adapterName}" loaded${version ? ` (v${version})` : ''}`,
|
|
237
|
-
context: { adapterName, version },
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Log adapter failed event
|
|
185
|
+
* Log with a custom level
|
|
242
186
|
*/
|
|
243
|
-
|
|
244
|
-
this.
|
|
245
|
-
type: EngineLogType.ADAPTER_FAILED,
|
|
246
|
-
timestamp: new Date(),
|
|
247
|
-
message: `Adapter "${adapterName}" failed: ${error.message}`,
|
|
248
|
-
context: { adapterName },
|
|
249
|
-
error,
|
|
250
|
-
});
|
|
187
|
+
logWithLevel(level, message, context) {
|
|
188
|
+
this.log(level, message, context);
|
|
251
189
|
}
|
|
252
190
|
/**
|
|
253
|
-
* Log
|
|
191
|
+
* Log only if severity meets minimum threshold
|
|
254
192
|
*/
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
message: `Plugin "${pluginName}" installed from ${source}`,
|
|
260
|
-
context: { pluginName, source },
|
|
261
|
-
});
|
|
193
|
+
logIfSeverity(minSeverity, level, message, context) {
|
|
194
|
+
if (LogLevelSeverity[level] >= minSeverity) {
|
|
195
|
+
this.log(level, message, context);
|
|
196
|
+
}
|
|
262
197
|
}
|
|
263
198
|
/**
|
|
264
|
-
*
|
|
199
|
+
* Measure and log execution time with appropriate log level based on duration.
|
|
200
|
+
*
|
|
201
|
+
* Pass `category` to override (`'runtime'` during `run()`, `'analysis'` during `explain()`).
|
|
202
|
+
* Prefer `logger.runtime.measureExecution(...)` or `logger.analysis.measureExecution(...)`.
|
|
265
203
|
*/
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
204
|
+
async measureExecution(label, fn, thresholds, category) {
|
|
205
|
+
const start = Date.now();
|
|
206
|
+
try {
|
|
207
|
+
const result = await fn();
|
|
208
|
+
const duration = Date.now() - start;
|
|
209
|
+
let level = LogLevel.DEBUG;
|
|
210
|
+
if (thresholds?.error && duration > thresholds.error) {
|
|
211
|
+
level = LogLevel.ERROR;
|
|
212
|
+
}
|
|
213
|
+
else if (thresholds?.warn && duration > thresholds.warn) {
|
|
214
|
+
level = LogLevel.WARN;
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
level = LogLevel.INFO;
|
|
218
|
+
}
|
|
219
|
+
this._logWithCategory(level, `${label} completed`, { duration: `${duration}ms` }, undefined, category, EngineLogType.EXECUTION_TIME);
|
|
220
|
+
return result;
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
const duration = Date.now() - start;
|
|
224
|
+
this._logWithCategory(LogLevel.ERROR, `${label} failed`, { duration: `${duration}ms` }, error instanceof Error ? error : new Error(String(error)), category, EngineLogType.ERROR_DETECTED);
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
273
227
|
}
|
|
274
|
-
//
|
|
275
|
-
// ERROR & DEBUG LOGGING
|
|
276
|
-
// ============================================================================
|
|
228
|
+
// ─── Core log dispatch ────────────────────────────────────────────────────
|
|
277
229
|
/**
|
|
278
|
-
*
|
|
230
|
+
* Central log dispatcher — used directly by `CategoryLogger` sub-loggers
|
|
231
|
+
* and by the private `log()` helper below.
|
|
232
|
+
*
|
|
233
|
+
* @param level - Severity level
|
|
234
|
+
* @param message - Human-readable message
|
|
235
|
+
* @param context - Optional key-value metadata (merged with workflow ctx)
|
|
236
|
+
* @param error - Optional error object
|
|
237
|
+
* @param categoryOverride - Pin to a specific category (defaults to config category)
|
|
238
|
+
* @param typeOverride - Explicit `EngineLogType`; inferred from level when omitted
|
|
239
|
+
*
|
|
240
|
+
* @internal Called via sub-loggers (`runtime`, `analysis`, etc.) or internally.
|
|
279
241
|
*/
|
|
280
|
-
|
|
281
|
-
this.
|
|
282
|
-
|
|
242
|
+
_logWithCategory(level, message, context, error, categoryOverride, typeOverride) {
|
|
243
|
+
if (!this.shouldLogLevel(level))
|
|
244
|
+
return;
|
|
245
|
+
// Map severity level → structured event type
|
|
246
|
+
const typeMap = {
|
|
247
|
+
[LogLevel.DEBUG]: EngineLogType.DEBUG,
|
|
248
|
+
[LogLevel.INFO]: EngineLogType.INFO,
|
|
249
|
+
[LogLevel.WARN]: EngineLogType.WARNING,
|
|
250
|
+
[LogLevel.ERROR]: EngineLogType.ERROR_DETECTED,
|
|
251
|
+
[LogLevel.FATAL]: EngineLogType.ERROR,
|
|
252
|
+
};
|
|
253
|
+
const eventType = typeOverride ?? typeMap[level] ?? EngineLogType.INFO;
|
|
254
|
+
const category = categoryOverride ?? this.config.category;
|
|
255
|
+
// Automatically merge workflow context so every log carries the active workflow
|
|
256
|
+
const enrichedContext = {};
|
|
257
|
+
if (this.workflowContext) {
|
|
258
|
+
enrichedContext['workflow'] = { ...this.workflowContext };
|
|
259
|
+
}
|
|
260
|
+
if (context) {
|
|
261
|
+
Object.assign(enrichedContext, context);
|
|
262
|
+
}
|
|
263
|
+
const finalContext = Object.keys(enrichedContext).length > 0 ? enrichedContext : undefined;
|
|
264
|
+
// Record in history
|
|
265
|
+
const event = {
|
|
266
|
+
type: eventType,
|
|
283
267
|
timestamp: new Date(),
|
|
284
|
-
message
|
|
285
|
-
|
|
268
|
+
message,
|
|
269
|
+
category,
|
|
270
|
+
source: this.config.source,
|
|
271
|
+
context: finalContext,
|
|
286
272
|
error,
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
context:
|
|
273
|
+
};
|
|
274
|
+
this.logHistory.push(event);
|
|
275
|
+
// Ring-buffer: drop the oldest entry when the limit is reached.
|
|
276
|
+
// A maxHistorySize of 0 means unbounded (no trimming).
|
|
277
|
+
if (this.config.maxHistorySize > 0 && this.logHistory.length > this.config.maxHistorySize) {
|
|
278
|
+
this.logHistory.shift();
|
|
279
|
+
}
|
|
280
|
+
// Build + emit formatted entry
|
|
281
|
+
const entry = createLogEntry(level, message, {
|
|
282
|
+
source: this.config.source,
|
|
283
|
+
context: finalContext,
|
|
298
284
|
error,
|
|
299
285
|
});
|
|
286
|
+
const formatted = formatLog(entry, this.formatOptions);
|
|
287
|
+
console.log(formatted);
|
|
300
288
|
}
|
|
301
289
|
/**
|
|
302
|
-
*
|
|
303
|
-
|
|
304
|
-
validationError(message, errors) {
|
|
305
|
-
this.logEvent({
|
|
306
|
-
type: EngineLogType.VALIDATION_ERROR,
|
|
307
|
-
timestamp: new Date(),
|
|
308
|
-
message,
|
|
309
|
-
context: { errors },
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
// ============================================================================
|
|
313
|
-
// PERFORMANCE LOGGING
|
|
314
|
-
// ============================================================================
|
|
315
|
-
/**
|
|
316
|
-
* Log performance metric
|
|
290
|
+
* Private shorthand — uses the default category from config.
|
|
291
|
+
* Prefer the categorised sub-loggers (`runtime`, `analysis`, etc.) for new code.
|
|
317
292
|
*/
|
|
318
|
-
|
|
319
|
-
this.
|
|
320
|
-
type: EngineLogType.PERFORMANCE_METRIC,
|
|
321
|
-
timestamp: new Date(),
|
|
322
|
-
message: `Performance: ${label}`,
|
|
323
|
-
metrics,
|
|
324
|
-
});
|
|
293
|
+
log(level, message, context, error) {
|
|
294
|
+
this._logWithCategory(level, message, context, error);
|
|
325
295
|
}
|
|
296
|
+
// ─── Log History ────────────────────────────────────────────────────
|
|
326
297
|
/**
|
|
327
|
-
*
|
|
298
|
+
* Export collected logs as a structured ExportedLogs object.
|
|
299
|
+
* Includes `byCategory` statistics and the active `workflowContext` snapshot.
|
|
328
300
|
*/
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
301
|
+
exportLogs() {
|
|
302
|
+
const grouped = {};
|
|
303
|
+
let withErrors = 0;
|
|
304
|
+
let withMetrics = 0;
|
|
305
|
+
let first;
|
|
306
|
+
let last;
|
|
307
|
+
const byType = {};
|
|
308
|
+
const byCategory = {};
|
|
309
|
+
for (const event of this.logHistory) {
|
|
310
|
+
const typeKey = event.type;
|
|
311
|
+
grouped[typeKey] = grouped[typeKey] ? [...grouped[typeKey], event] : [event];
|
|
312
|
+
byType[typeKey] = (byType[typeKey] ?? 0) + 1;
|
|
313
|
+
byCategory[event.category] = (byCategory[event.category] ?? 0) + 1;
|
|
314
|
+
if (event.error)
|
|
315
|
+
withErrors++;
|
|
316
|
+
if (event.metrics)
|
|
317
|
+
withMetrics++;
|
|
318
|
+
if (!first || event.timestamp < first)
|
|
319
|
+
first = event.timestamp;
|
|
320
|
+
if (!last || event.timestamp > last)
|
|
321
|
+
last = event.timestamp;
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
raw: [...this.logHistory],
|
|
325
|
+
grouped,
|
|
326
|
+
workflowContext: this.workflowContext ?? undefined,
|
|
327
|
+
stats: {
|
|
328
|
+
total: this.logHistory.length,
|
|
329
|
+
byType,
|
|
330
|
+
byCategory,
|
|
331
|
+
withErrors,
|
|
332
|
+
withMetrics,
|
|
333
|
+
timeRange: { first, last },
|
|
334
|
+
},
|
|
335
|
+
};
|
|
337
336
|
}
|
|
338
|
-
// ============================================================================
|
|
339
|
-
// FIELD-LEVEL EXECUTION LOGGING (Dynamic Explanation)
|
|
340
|
-
// ============================================================================
|
|
341
337
|
/**
|
|
342
|
-
*
|
|
343
|
-
* This enables dynamic explanation generation from runtime logs
|
|
338
|
+
* Get all collected logs as a JSON string
|
|
344
339
|
*/
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (field.startsWith('_'))
|
|
348
|
-
return;
|
|
349
|
-
this.debug(`[Field] ${type}.${field} = ${this.formatValue(value)}`, {
|
|
350
|
-
fieldType: type,
|
|
351
|
-
fieldName: field,
|
|
352
|
-
value: this.sanitizeValue(value),
|
|
353
|
-
...context,
|
|
354
|
-
});
|
|
340
|
+
getJSONLogs() {
|
|
341
|
+
return JSON.stringify(this.exportLogs(), null, 2);
|
|
355
342
|
}
|
|
356
343
|
/**
|
|
357
|
-
*
|
|
344
|
+
* Clear the log history (does NOT clear the workflow context)
|
|
358
345
|
*/
|
|
359
|
-
|
|
360
|
-
this.
|
|
361
|
-
input: inputName,
|
|
362
|
-
source,
|
|
363
|
-
value: this.sanitizeValue(value),
|
|
364
|
-
});
|
|
346
|
+
clearHistory() {
|
|
347
|
+
this.logHistory = [];
|
|
365
348
|
}
|
|
349
|
+
// ─── Workflow Context ─────────────────────────────────────────────────────
|
|
366
350
|
/**
|
|
367
|
-
*
|
|
351
|
+
* Attach workflow context so every subsequent log entry automatically
|
|
352
|
+
* includes a `workflow` field. Call this before `run()`, `explain()`,
|
|
353
|
+
* or `validate()` with the parsed workflow data.
|
|
354
|
+
*
|
|
355
|
+
* ```typescript
|
|
356
|
+
* logger.setWorkflowContext({
|
|
357
|
+
* name: workflow.name,
|
|
358
|
+
* version: workflow.version,
|
|
359
|
+
* kind: workflow.kind,
|
|
360
|
+
* stepCount: workflow.steps.length,
|
|
361
|
+
* filePath: resolvedPath,
|
|
362
|
+
* });
|
|
363
|
+
* ```
|
|
368
364
|
*/
|
|
369
|
-
|
|
370
|
-
this.
|
|
371
|
-
output: outputName,
|
|
372
|
-
step,
|
|
373
|
-
value: this.sanitizeValue(value),
|
|
374
|
-
});
|
|
365
|
+
setWorkflowContext(ctx) {
|
|
366
|
+
this.workflowContext = { ...ctx };
|
|
375
367
|
}
|
|
376
368
|
/**
|
|
377
|
-
*
|
|
369
|
+
* Detach the workflow context (e.g. after a run completes).
|
|
370
|
+
* Logs emitted after this call will no longer include the `workflow` field.
|
|
378
371
|
*/
|
|
379
|
-
|
|
380
|
-
this.
|
|
381
|
-
variable,
|
|
382
|
-
step,
|
|
383
|
-
value: this.sanitizeValue(value),
|
|
384
|
-
});
|
|
372
|
+
clearWorkflowContext() {
|
|
373
|
+
this.workflowContext = null;
|
|
385
374
|
}
|
|
386
375
|
/**
|
|
387
|
-
*
|
|
376
|
+
* Returns a read-only snapshot of the currently active workflow context,
|
|
377
|
+
* or `null` if none has been set.
|
|
388
378
|
*/
|
|
389
|
-
|
|
390
|
-
this.
|
|
391
|
-
secretKey,
|
|
392
|
-
step,
|
|
393
|
-
exposed: false, // Never log secret values
|
|
394
|
-
});
|
|
379
|
+
getWorkflowContext() {
|
|
380
|
+
return this.workflowContext ? { ...this.workflowContext } : null;
|
|
395
381
|
}
|
|
396
382
|
/**
|
|
397
|
-
*
|
|
383
|
+
* Partially update the active workflow context without replacing it.
|
|
384
|
+
* Only the supplied fields are merged in; the rest are preserved.
|
|
385
|
+
*
|
|
386
|
+
* Useful for enriching the context mid-run (e.g. after the execution
|
|
387
|
+
* strategy is determined):
|
|
388
|
+
*
|
|
389
|
+
* ```typescript
|
|
390
|
+
* logger.patchWorkflowContext({ executionStrategy: 'parallel' });
|
|
391
|
+
* ```
|
|
392
|
+
*
|
|
393
|
+
* If no context has been set yet, the patch is applied as if it were
|
|
394
|
+
* a fresh `setWorkflowContext` call.
|
|
398
395
|
*/
|
|
399
|
-
|
|
400
|
-
this.
|
|
401
|
-
variable,
|
|
402
|
-
resolved: this.sanitizeValue(resolvedValue),
|
|
403
|
-
...context,
|
|
404
|
-
});
|
|
396
|
+
patchWorkflowContext(partial) {
|
|
397
|
+
this.workflowContext = { ...(this.workflowContext ?? {}), ...partial };
|
|
405
398
|
}
|
|
399
|
+
// ─── Dynamic Context Helpers ──────────────────────────────────────────────
|
|
406
400
|
/**
|
|
407
|
-
*
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
401
|
+
* Build a log-context object for a step event.
|
|
402
|
+
*
|
|
403
|
+
* Use this wherever you log step lifecycle events so context is consistent:
|
|
404
|
+
* ```typescript
|
|
405
|
+
* logger.runtime.info('Step started', logger.stepCtx({ id: step.id, adapter: step.adapter }));
|
|
406
|
+
* logger.runtime.error('Step failed', error, logger.stepCtx({ id: step.id, adapter: step.adapter }));
|
|
407
|
+
* ```
|
|
408
|
+
*/
|
|
409
|
+
stepCtx(step) {
|
|
410
|
+
const ctx = { stepId: step.id };
|
|
411
|
+
if (step.adapter)
|
|
412
|
+
ctx['adapter'] = step.adapter;
|
|
413
|
+
if (step.name)
|
|
414
|
+
ctx['stepName'] = step.name;
|
|
415
|
+
// Forward any extra fields the caller added
|
|
416
|
+
for (const [k, v] of Object.entries(step)) {
|
|
417
|
+
if (k !== 'id' && k !== 'adapter' && k !== 'name')
|
|
418
|
+
ctx[k] = v;
|
|
417
419
|
}
|
|
418
|
-
|
|
419
|
-
// ============================================================================
|
|
420
|
-
// PARSING & VALIDATION LOGGING
|
|
421
|
-
// ============================================================================
|
|
422
|
-
/**
|
|
423
|
-
* Log parsing start
|
|
424
|
-
*/
|
|
425
|
-
parsingStarted(source, format) {
|
|
426
|
-
this.debug(`[Parser] Parsing ${format} from ${source}`, {
|
|
427
|
-
source,
|
|
428
|
-
format,
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* Log parsing success
|
|
433
|
-
*/
|
|
434
|
-
parsingCompleted(source, duration, stats) {
|
|
435
|
-
this.info(`[Parser] Parsed successfully in ${duration}ms`, {
|
|
436
|
-
source,
|
|
437
|
-
duration,
|
|
438
|
-
...stats,
|
|
439
|
-
});
|
|
420
|
+
return ctx;
|
|
440
421
|
}
|
|
441
422
|
/**
|
|
442
|
-
*
|
|
423
|
+
* Build a log-context object for a workflow-level event.
|
|
424
|
+
*
|
|
425
|
+
* Returns a flat object suitable for passing as `context` to any log call.
|
|
426
|
+
* If no workflow context is set, returns `{}`.
|
|
427
|
+
*
|
|
428
|
+
* ```typescript
|
|
429
|
+
* logger.system.info('Workflow validated', logger.workflowCtx());
|
|
430
|
+
* ```
|
|
443
431
|
*/
|
|
444
|
-
|
|
445
|
-
this.
|
|
446
|
-
source,
|
|
447
|
-
...context,
|
|
448
|
-
});
|
|
432
|
+
workflowCtx() {
|
|
433
|
+
return this.workflowContext ? { ...this.workflowContext } : {};
|
|
449
434
|
}
|
|
435
|
+
// ─── Dynamic Lifecycle Helpers ────────────────────────────────────────────
|
|
450
436
|
/**
|
|
451
|
-
* Log
|
|
437
|
+
* Log workflow execution started (runtime category).
|
|
438
|
+
* Called at the top of `WorkflowExecutor.execute()`.
|
|
452
439
|
*/
|
|
453
|
-
|
|
454
|
-
this.
|
|
455
|
-
target,
|
|
456
|
-
type,
|
|
457
|
-
});
|
|
440
|
+
workflowStarted(workflowName, context) {
|
|
441
|
+
this.runtime.info(`Workflow "${workflowName}" started`, { workflowName, ...context });
|
|
458
442
|
}
|
|
459
443
|
/**
|
|
460
|
-
* Log
|
|
444
|
+
* Log a workflow input field that was resolved (runtime category).
|
|
461
445
|
*/
|
|
462
|
-
|
|
463
|
-
this.
|
|
464
|
-
target,
|
|
465
|
-
type,
|
|
466
|
-
duration,
|
|
467
|
-
});
|
|
446
|
+
inputProcessed(key, value, source) {
|
|
447
|
+
this.runtime.debug(`Input resolved: ${key}`, { key, value, source });
|
|
468
448
|
}
|
|
469
449
|
/**
|
|
470
|
-
* Log
|
|
450
|
+
* Log a context field used during execution (runtime category).
|
|
471
451
|
*/
|
|
472
|
-
|
|
473
|
-
this.
|
|
474
|
-
target,
|
|
475
|
-
type,
|
|
476
|
-
errors,
|
|
477
|
-
errorCount: errors.length,
|
|
478
|
-
});
|
|
452
|
+
fieldExecution(fieldType, key, value) {
|
|
453
|
+
this.runtime.debug(`Context field accessed: ${fieldType}.${key}`, { fieldType, key, value });
|
|
479
454
|
}
|
|
480
|
-
// ============================================================================
|
|
481
|
-
// ERROR DETECTION & DEBUGGING LOGGING
|
|
482
|
-
// ============================================================================
|
|
483
455
|
/**
|
|
484
|
-
* Log
|
|
456
|
+
* Log step execution started (runtime category).
|
|
457
|
+
* ```typescript
|
|
458
|
+
* logger.stepStarted(step.id, step.name, { adapter: step.adapter });
|
|
459
|
+
* ```
|
|
485
460
|
*/
|
|
486
|
-
|
|
487
|
-
this.
|
|
488
|
-
errorType: error.name,
|
|
489
|
-
errorMessage: error.message,
|
|
490
|
-
...debugInfo,
|
|
491
|
-
});
|
|
461
|
+
stepStarted(stepId, stepName, context) {
|
|
462
|
+
this._logWithCategory(LogLevel.INFO, `Step "${stepName}" (${stepId}) started`, { stepId, stepName, ...context }, undefined, LogCategoryEnum.RUNTIME, EngineLogType.STEP_STARTED);
|
|
492
463
|
}
|
|
493
464
|
/**
|
|
494
|
-
* Log
|
|
465
|
+
* Log step execution completed (runtime category).
|
|
495
466
|
*/
|
|
496
|
-
|
|
497
|
-
this.
|
|
498
|
-
errorType: error.name,
|
|
499
|
-
explanation,
|
|
500
|
-
fixSteps,
|
|
501
|
-
stepCount: fixSteps.length,
|
|
502
|
-
});
|
|
467
|
+
stepCompleted(stepId, stepName, duration, context) {
|
|
468
|
+
this._logWithCategory(LogLevel.INFO, `Step "${stepName}" (${stepId}) completed in ${duration}ms`, { stepId, stepName, duration, ...context }, undefined, LogCategoryEnum.RUNTIME, EngineLogType.STEP_COMPLETED);
|
|
503
469
|
}
|
|
504
|
-
// ============================================================================
|
|
505
|
-
// EXECUTION PHASE LOGGING
|
|
506
|
-
// ============================================================================
|
|
507
470
|
/**
|
|
508
|
-
* Log
|
|
471
|
+
* Log step timeout (runtime category).
|
|
509
472
|
*/
|
|
510
|
-
|
|
511
|
-
this.
|
|
512
|
-
phase,
|
|
513
|
-
stepIds,
|
|
514
|
-
stepCount: stepIds.length,
|
|
515
|
-
});
|
|
473
|
+
stepTimeout(stepId, stepName, timeoutMs) {
|
|
474
|
+
this._logWithCategory(LogLevel.WARN, `Step "${stepName}" (${stepId}) timed out after ${timeoutMs}ms`, { stepId, stepName, timeoutMs }, undefined, LogCategoryEnum.RUNTIME, EngineLogType.STEP_TIMEOUT);
|
|
516
475
|
}
|
|
517
476
|
/**
|
|
518
|
-
* Log
|
|
477
|
+
* Log step retry attempt (runtime category).
|
|
519
478
|
*/
|
|
520
|
-
|
|
521
|
-
this.
|
|
522
|
-
phase,
|
|
523
|
-
duration,
|
|
524
|
-
successCount,
|
|
525
|
-
failureCount,
|
|
526
|
-
totalSteps: successCount + failureCount,
|
|
527
|
-
});
|
|
479
|
+
stepRetry(stepId, stepName, attempt, maxAttempts) {
|
|
480
|
+
this._logWithCategory(LogLevel.WARN, `Step "${stepName}" (${stepId}) retrying — attempt ${attempt}/${maxAttempts}`, { stepId, stepName, attempt, maxAttempts }, undefined, LogCategoryEnum.RUNTIME, EngineLogType.STEP_RETRY);
|
|
528
481
|
}
|
|
529
|
-
// ============================================================================
|
|
530
|
-
// UTILITY METHODS
|
|
531
|
-
// ============================================================================
|
|
532
482
|
/**
|
|
533
|
-
*
|
|
483
|
+
* Log step failure after all attempts (runtime category).
|
|
534
484
|
*/
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
return 'null';
|
|
538
|
-
if (value === undefined)
|
|
539
|
-
return 'undefined';
|
|
540
|
-
if (typeof value === 'string') {
|
|
541
|
-
return value.length > 100 ? `${value.substring(0, 97)}...` : value;
|
|
542
|
-
}
|
|
543
|
-
if (typeof value === 'object') {
|
|
544
|
-
const str = JSON.stringify(value);
|
|
545
|
-
return str.length > 200 ? `${str.substring(0, 197)}...` : str;
|
|
546
|
-
}
|
|
547
|
-
return String(value);
|
|
485
|
+
stepFailed(stepId, stepName, error, context) {
|
|
486
|
+
this._logWithCategory(LogLevel.ERROR, `Step "${stepName}" (${stepId}) failed: ${error.message}`, { stepId, stepName, ...context }, error, LogCategoryEnum.RUNTIME, EngineLogType.STEP_FAILED);
|
|
548
487
|
}
|
|
549
488
|
/**
|
|
550
|
-
*
|
|
489
|
+
* Log adapter action execution result (runtime category).
|
|
551
490
|
*/
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
// For objects, recursively sanitize
|
|
556
|
-
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
557
|
-
const sanitized = {};
|
|
558
|
-
for (const [key, val] of Object.entries(value)) {
|
|
559
|
-
// Skip internal fields
|
|
560
|
-
if (key.startsWith('_'))
|
|
561
|
-
continue;
|
|
562
|
-
// Mask sensitive fields
|
|
563
|
-
if (this.isSensitiveField(key)) {
|
|
564
|
-
sanitized[key] = '***REDACTED***';
|
|
565
|
-
}
|
|
566
|
-
else if (typeof val === 'object') {
|
|
567
|
-
sanitized[key] = this.sanitizeValue(val);
|
|
568
|
-
}
|
|
569
|
-
else {
|
|
570
|
-
sanitized[key] = val;
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
return sanitized;
|
|
574
|
-
}
|
|
575
|
-
return value;
|
|
491
|
+
adapterActionExecuted(adapter, action, duration, success) {
|
|
492
|
+
const level = success ? LogLevel.DEBUG : LogLevel.WARN;
|
|
493
|
+
this._logWithCategory(level, `Adapter "${adapter}" action "${action}" ${success ? 'succeeded' : 'failed'} in ${duration}ms`, { adapter, action, duration, success }, undefined, LogCategoryEnum.RUNTIME, EngineLogType.EXECUTION_TIME);
|
|
576
494
|
}
|
|
577
495
|
/**
|
|
578
|
-
*
|
|
496
|
+
* Log validation phase started (analysis category).
|
|
579
497
|
*/
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
'password', 'secret', 'token', 'key', 'credential',
|
|
583
|
-
'apikey', 'api_key', 'auth', 'private',
|
|
584
|
-
];
|
|
585
|
-
const lowerField = fieldName.toLowerCase();
|
|
586
|
-
return sensitivePatterns.some(pattern => lowerField.includes(pattern));
|
|
498
|
+
validationStarted(type, schema) {
|
|
499
|
+
this._logWithCategory(LogLevel.DEBUG, `Validation started: ${type} against ${schema} schema`, { type, schema }, undefined, LogCategoryEnum.ANALYSIS, EngineLogType.WORKFLOW_VALIDATION);
|
|
587
500
|
}
|
|
588
501
|
/**
|
|
589
|
-
* Log
|
|
502
|
+
* Log validation passed (analysis category).
|
|
590
503
|
*/
|
|
591
|
-
|
|
592
|
-
this.
|
|
504
|
+
validationPassed(type, schema) {
|
|
505
|
+
this._logWithCategory(LogLevel.INFO, `Validation passed: ${type} (${schema})`, { type, schema }, undefined, LogCategoryEnum.ANALYSIS, EngineLogType.WORKFLOW_VALIDATION);
|
|
593
506
|
}
|
|
594
507
|
/**
|
|
595
|
-
* Log
|
|
508
|
+
* Log validation failure (analysis category).
|
|
596
509
|
*/
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
this.log(level, message, context);
|
|
600
|
-
}
|
|
510
|
+
validationFailed(type, schema, errors) {
|
|
511
|
+
this._logWithCategory(LogLevel.ERROR, `Validation failed: ${type} (${schema}) — ${errors.length} error(s)`, { type, schema, errors }, undefined, LogCategoryEnum.ANALYSIS, EngineLogType.VALIDATION_ERROR);
|
|
601
512
|
}
|
|
602
513
|
/**
|
|
603
|
-
*
|
|
514
|
+
* Log parsing started (analysis category).
|
|
604
515
|
*/
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
try {
|
|
608
|
-
const result = await fn();
|
|
609
|
-
const duration = Date.now() - start;
|
|
610
|
-
// Choose log level based on duration thresholds
|
|
611
|
-
let level = LogLevel.DEBUG;
|
|
612
|
-
if (thresholds?.error && duration > thresholds.error) {
|
|
613
|
-
level = LogLevel.ERROR;
|
|
614
|
-
}
|
|
615
|
-
else if (thresholds?.warn && duration > thresholds.warn) {
|
|
616
|
-
level = LogLevel.WARN;
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
level = LogLevel.INFO;
|
|
620
|
-
}
|
|
621
|
-
this.log(level, `${label} completed`, { duration: `${duration}ms` });
|
|
622
|
-
return result;
|
|
623
|
-
}
|
|
624
|
-
catch (error) {
|
|
625
|
-
const duration = Date.now() - start;
|
|
626
|
-
this.log(LogLevel.ERROR, `${label} failed`, { duration: `${duration}ms` }, error instanceof Error ? error : new Error(String(error)));
|
|
627
|
-
throw error;
|
|
628
|
-
}
|
|
516
|
+
parsingStarted(format, source) {
|
|
517
|
+
this._logWithCategory(LogLevel.DEBUG, `Parsing started: ${format} from ${source}`, { format, source }, undefined, LogCategoryEnum.ANALYSIS, EngineLogType.INFO);
|
|
629
518
|
}
|
|
630
519
|
/**
|
|
631
|
-
* Log
|
|
520
|
+
* Log parsing completed (analysis category).
|
|
632
521
|
*/
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
this.addToBuffer(event);
|
|
636
|
-
// Emit event to listeners if structured events enabled
|
|
637
|
-
if (this.config.structuredEvents) {
|
|
638
|
-
this.emitEvent(event);
|
|
639
|
-
}
|
|
640
|
-
// Determine log level based on event type
|
|
641
|
-
const level = this.getLogLevelForEventType(event.type);
|
|
642
|
-
// Check if this level should be logged
|
|
643
|
-
if (!this.shouldLogLevel(level)) {
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
// Build context with metrics if present
|
|
647
|
-
const fullContext = {
|
|
648
|
-
...event.context,
|
|
649
|
-
...(event.metrics && { metrics: event.metrics }),
|
|
650
|
-
};
|
|
651
|
-
// Log through standard log method
|
|
652
|
-
this.log(level, event.message, fullContext, event.error);
|
|
522
|
+
parsingCompleted(format, duration, context) {
|
|
523
|
+
this._logWithCategory(LogLevel.INFO, `Parsing completed: ${format} in ${duration}ms`, { format, duration, ...context }, undefined, LogCategoryEnum.ANALYSIS, EngineLogType.INFO);
|
|
653
524
|
}
|
|
654
525
|
/**
|
|
655
|
-
*
|
|
526
|
+
* Log parsing failure (analysis category).
|
|
656
527
|
*/
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
if (type.includes('failed') || type.includes('error') || type.includes('timeout')) {
|
|
660
|
-
return LogLevel.ERROR;
|
|
661
|
-
}
|
|
662
|
-
// Warning events
|
|
663
|
-
if (type.includes('retry') || type.includes('cycles') || type === EngineLogType.WARNING) {
|
|
664
|
-
return LogLevel.WARN;
|
|
665
|
-
}
|
|
666
|
-
// Debug events
|
|
667
|
-
if (type === EngineLogType.DEBUG || type.includes('debug')) {
|
|
668
|
-
return LogLevel.DEBUG;
|
|
669
|
-
}
|
|
670
|
-
// Default to INFO
|
|
671
|
-
return LogLevel.INFO;
|
|
528
|
+
parsingFailed(format, error, context) {
|
|
529
|
+
this._logWithCategory(LogLevel.ERROR, `Parsing failed: ${format} — ${error.message}`, { format, ...context }, error, LogCategoryEnum.ANALYSIS, EngineLogType.ERROR_DETECTED);
|
|
672
530
|
}
|
|
531
|
+
// ─── Typed Event Logger ───────────────────────────────────────────────────
|
|
673
532
|
/**
|
|
674
|
-
* Emit
|
|
533
|
+
* Emit a log with an explicit `EngineLogType`.
|
|
534
|
+
*
|
|
535
|
+
* Useful when you want the structured event type to be distinct from the
|
|
536
|
+
* generic level-based inference — e.g. to mark a `STEP_RETRY` event:
|
|
537
|
+
*
|
|
538
|
+
* ```typescript
|
|
539
|
+
* logger.logEvent(EngineLogType.STEP_RETRY, LogLevel.WARN, 'Retrying step', {
|
|
540
|
+
* stepId: 'http-request',
|
|
541
|
+
* attempt: 2,
|
|
542
|
+
* }, 'runtime');
|
|
543
|
+
* ```
|
|
675
544
|
*/
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
for (const listener of listeners) {
|
|
679
|
-
try {
|
|
680
|
-
listener(event);
|
|
681
|
-
}
|
|
682
|
-
catch (error) {
|
|
683
|
-
// Don't let listener errors crash the logger
|
|
684
|
-
console.error('Error in log event listener:', error);
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
// Also emit to wildcard listeners
|
|
688
|
-
const wildcardListeners = this.eventListeners.get(EngineLogType.INFO) || [];
|
|
689
|
-
for (const listener of wildcardListeners) {
|
|
690
|
-
try {
|
|
691
|
-
listener(event);
|
|
692
|
-
}
|
|
693
|
-
catch (error) {
|
|
694
|
-
console.error('Error in wildcard log event listener:', error);
|
|
695
|
-
}
|
|
696
|
-
}
|
|
545
|
+
logEvent(type, level, message, context, category) {
|
|
546
|
+
this._logWithCategory(level, message, context, undefined, category, type);
|
|
697
547
|
}
|
|
548
|
+
// ─── History Queries ──────────────────────────────────────────────────────
|
|
698
549
|
/**
|
|
699
|
-
*
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
550
|
+
* Return log events that match every supplied filter field.
|
|
551
|
+
* Omit a field to skip that filter.
|
|
552
|
+
*
|
|
553
|
+
* ```typescript
|
|
554
|
+
* // All runtime errors for the current workflow
|
|
555
|
+
* logger.filterHistory({ category: 'runtime', withErrors: true });
|
|
556
|
+
*
|
|
557
|
+
* // All analysis events since the explain started
|
|
558
|
+
* const since = new Date();
|
|
559
|
+
* logger.filterHistory({ category: 'analysis', since });
|
|
560
|
+
* ```
|
|
561
|
+
*/
|
|
562
|
+
filterHistory(filter = {}) {
|
|
563
|
+
return this.logHistory.filter(event => {
|
|
564
|
+
if (filter.category && event.category !== filter.category)
|
|
565
|
+
return false;
|
|
566
|
+
if (filter.type && event.type !== filter.type)
|
|
567
|
+
return false;
|
|
568
|
+
if (filter.since && event.timestamp < filter.since)
|
|
569
|
+
return false;
|
|
570
|
+
if (filter.until && event.timestamp > filter.until)
|
|
571
|
+
return false;
|
|
572
|
+
if (filter.source && event.source !== filter.source)
|
|
573
|
+
return false;
|
|
574
|
+
if (filter.withErrors && !event.error)
|
|
575
|
+
return false;
|
|
576
|
+
if (filter.workflowName) {
|
|
577
|
+
const wf = event.context?.['workflow'];
|
|
578
|
+
if (!wf || wf['name'] !== filter.workflowName)
|
|
579
|
+
return false;
|
|
714
580
|
}
|
|
715
|
-
|
|
716
|
-
}
|
|
717
|
-
/**
|
|
718
|
-
* Internal log method
|
|
719
|
-
*/
|
|
720
|
-
log(level, message, context, error) {
|
|
721
|
-
// Check if this level should be logged (using severity for better performance)
|
|
722
|
-
if (!this.shouldLogLevel(level)) {
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
|
-
// Create log entry
|
|
726
|
-
const entry = createLogEntry(level, message, {
|
|
727
|
-
source: this.config.source,
|
|
728
|
-
context,
|
|
729
|
-
error,
|
|
581
|
+
return true;
|
|
730
582
|
});
|
|
731
|
-
// Add basic logs to buffer as well (for comprehensive JSON output)
|
|
732
|
-
this.addToBuffer({
|
|
733
|
-
type: this.mapLogLevelToEventType(level),
|
|
734
|
-
timestamp: new Date(),
|
|
735
|
-
message,
|
|
736
|
-
context,
|
|
737
|
-
error,
|
|
738
|
-
});
|
|
739
|
-
// Format and output
|
|
740
|
-
const formatted = formatLog(entry, this.formatOptions);
|
|
741
|
-
console.log(formatted);
|
|
742
583
|
}
|
|
743
584
|
/**
|
|
744
|
-
*
|
|
585
|
+
* Return all history entries grouped by category.
|
|
586
|
+
* Convenience wrapper around `filterHistory`.
|
|
745
587
|
*/
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
case LogLevel.ERROR:
|
|
752
|
-
case LogLevel.FATAL: return EngineLogType.ERROR;
|
|
753
|
-
default: return EngineLogType.INFO;
|
|
588
|
+
getHistoryByCategory() {
|
|
589
|
+
const result = {};
|
|
590
|
+
for (const event of this.logHistory) {
|
|
591
|
+
result[event.category] = result[event.category] ?? [];
|
|
592
|
+
result[event.category].push(event);
|
|
754
593
|
}
|
|
594
|
+
return result;
|
|
755
595
|
}
|
|
756
596
|
/**
|
|
757
597
|
* Check if a level should be logged using severity comparison
|
|
@@ -876,270 +716,6 @@ export class EngineLogger {
|
|
|
876
716
|
isErrorEnabled() {
|
|
877
717
|
return this.willLog(LogLevel.ERROR);
|
|
878
718
|
}
|
|
879
|
-
// ============================================================================
|
|
880
|
-
// JSON LOG BUFFER MANAGEMENT
|
|
881
|
-
// ============================================================================
|
|
882
|
-
/**
|
|
883
|
-
* Add event to JSON log buffer
|
|
884
|
-
*/
|
|
885
|
-
addToBuffer(event) {
|
|
886
|
-
// Add to buffer
|
|
887
|
-
this.logBuffer.push(event);
|
|
888
|
-
// Prevent buffer overflow
|
|
889
|
-
if (this.logBuffer.length > this.maxBufferSize) {
|
|
890
|
-
this.logBuffer.shift(); // Remove oldest log
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
/**
|
|
894
|
-
* Get all logs as JSON array
|
|
895
|
-
* This is what CLI, API, dashboards, and explanation system will consume
|
|
896
|
-
*/
|
|
897
|
-
getJSONLogs() {
|
|
898
|
-
return [...this.logBuffer]; // Return copy to prevent external mutations
|
|
899
|
-
}
|
|
900
|
-
/**
|
|
901
|
-
* Get logs filtered by type
|
|
902
|
-
*/
|
|
903
|
-
getLogsByType(type) {
|
|
904
|
-
return this.logBuffer.filter(log => log.type === type);
|
|
905
|
-
}
|
|
906
|
-
/**
|
|
907
|
-
* Get logs filtered by time range
|
|
908
|
-
*/
|
|
909
|
-
getLogsByTimeRange(startTime, endTime) {
|
|
910
|
-
return this.logBuffer.filter(log => log.timestamp >= startTime && log.timestamp <= endTime);
|
|
911
|
-
}
|
|
912
|
-
/**
|
|
913
|
-
* Get logs filtered by multiple criteria
|
|
914
|
-
*/
|
|
915
|
-
getLogsFiltered(filter) {
|
|
916
|
-
let filtered = this.logBuffer;
|
|
917
|
-
if (filter.types) {
|
|
918
|
-
filtered = filtered.filter(log => filter.types.includes(log.type));
|
|
919
|
-
}
|
|
920
|
-
if (filter.startTime) {
|
|
921
|
-
filtered = filtered.filter(log => log.timestamp >= filter.startTime);
|
|
922
|
-
}
|
|
923
|
-
if (filter.endTime) {
|
|
924
|
-
filtered = filtered.filter(log => log.timestamp <= filter.endTime);
|
|
925
|
-
}
|
|
926
|
-
if (filter.hasError !== undefined) {
|
|
927
|
-
filtered = filtered.filter(log => filter.hasError ? !!log.error : !log.error);
|
|
928
|
-
}
|
|
929
|
-
if (filter.searchText) {
|
|
930
|
-
const searchLower = filter.searchText.toLowerCase();
|
|
931
|
-
filtered = filtered.filter(log => log.message.toLowerCase().includes(searchLower));
|
|
932
|
-
}
|
|
933
|
-
return filtered;
|
|
934
|
-
}
|
|
935
|
-
/**
|
|
936
|
-
* Get logs by category: Parse events
|
|
937
|
-
*/
|
|
938
|
-
getParseLogEvents() {
|
|
939
|
-
return this.logBuffer.filter(log => log.type === EngineLogType.WORKFLOW_VALIDATION);
|
|
940
|
-
}
|
|
941
|
-
/**
|
|
942
|
-
* Get logs by category: Validation events
|
|
943
|
-
*/
|
|
944
|
-
getValidationLogEvents() {
|
|
945
|
-
return this.logBuffer.filter(log => log.type === EngineLogType.WORKFLOW_VALIDATION ||
|
|
946
|
-
log.type === EngineLogType.VALIDATION_ERROR);
|
|
947
|
-
}
|
|
948
|
-
/**
|
|
949
|
-
* Get logs by category: Execution events
|
|
950
|
-
*/
|
|
951
|
-
getExecutionLogEvents() {
|
|
952
|
-
return this.logBuffer.filter(log => log.type === EngineLogType.WORKFLOW_STARTED ||
|
|
953
|
-
log.type === EngineLogType.WORKFLOW_COMPLETED ||
|
|
954
|
-
log.type === EngineLogType.WORKFLOW_FAILED ||
|
|
955
|
-
log.type === EngineLogType.STEP_STARTED ||
|
|
956
|
-
log.type === EngineLogType.STEP_COMPLETED ||
|
|
957
|
-
log.type === EngineLogType.STEP_FAILED ||
|
|
958
|
-
log.type === EngineLogType.STEP_RETRY ||
|
|
959
|
-
log.type === EngineLogType.STEP_TIMEOUT ||
|
|
960
|
-
log.type === EngineLogType.EXECUTION_TIME ||
|
|
961
|
-
log.type === EngineLogType.QUEUE_PROCESSING);
|
|
962
|
-
}
|
|
963
|
-
/**
|
|
964
|
-
* Get logs by category: Error events
|
|
965
|
-
*/
|
|
966
|
-
getErrorLogEvents() {
|
|
967
|
-
return this.logBuffer.filter(log => log.type === EngineLogType.ERROR_DETECTED ||
|
|
968
|
-
log.type === EngineLogType.ERROR_DEBUGGED ||
|
|
969
|
-
log.type === EngineLogType.VALIDATION_ERROR ||
|
|
970
|
-
log.type === EngineLogType.WORKFLOW_FAILED ||
|
|
971
|
-
log.type === EngineLogType.STEP_FAILED ||
|
|
972
|
-
log.type === EngineLogType.ADAPTER_FAILED ||
|
|
973
|
-
log.type === EngineLogType.ERROR);
|
|
974
|
-
}
|
|
975
|
-
/**
|
|
976
|
-
* Get logs by category: Lifecycle events
|
|
977
|
-
*/
|
|
978
|
-
getLifecycleLogEvents() {
|
|
979
|
-
return this.logBuffer.filter(log => log.type === EngineLogType.ENGINE_STARTED ||
|
|
980
|
-
log.type === EngineLogType.ENGINE_STOPPED ||
|
|
981
|
-
log.type === EngineLogType.ADAPTER_LOADED ||
|
|
982
|
-
log.type === EngineLogType.PLUGIN_INSTALLED ||
|
|
983
|
-
log.type === EngineLogType.PLUGIN_VERIFIED);
|
|
984
|
-
}
|
|
985
|
-
/**
|
|
986
|
-
* Get logs by category: Performance events
|
|
987
|
-
*/
|
|
988
|
-
getPerformanceLogEvents() {
|
|
989
|
-
return this.logBuffer.filter(log => log.type === EngineLogType.PERFORMANCE_METRIC ||
|
|
990
|
-
log.type === EngineLogType.EXECUTION_TIME);
|
|
991
|
-
}
|
|
992
|
-
/**
|
|
993
|
-
* Export logs as formatted JSON string
|
|
994
|
-
*/
|
|
995
|
-
exportLogsAsJSON(pretty = true) {
|
|
996
|
-
return JSON.stringify(this.logBuffer, null, pretty ? 2 : 0);
|
|
997
|
-
}
|
|
998
|
-
/**
|
|
999
|
-
* Export logs grouped by type
|
|
1000
|
-
*/
|
|
1001
|
-
exportLogsGrouped() {
|
|
1002
|
-
const grouped = {};
|
|
1003
|
-
for (const log of this.logBuffer) {
|
|
1004
|
-
const type = log.type;
|
|
1005
|
-
if (!grouped[type]) {
|
|
1006
|
-
grouped[type] = [];
|
|
1007
|
-
}
|
|
1008
|
-
grouped[type].push(log);
|
|
1009
|
-
}
|
|
1010
|
-
return grouped;
|
|
1011
|
-
}
|
|
1012
|
-
/**
|
|
1013
|
-
* Get log statistics
|
|
1014
|
-
*/
|
|
1015
|
-
getLogStats() {
|
|
1016
|
-
const stats = {
|
|
1017
|
-
total: this.logBuffer.length,
|
|
1018
|
-
byType: {},
|
|
1019
|
-
withErrors: 0,
|
|
1020
|
-
withMetrics: 0,
|
|
1021
|
-
timeRange: {
|
|
1022
|
-
first: this.logBuffer[0]?.timestamp,
|
|
1023
|
-
last: this.logBuffer[this.logBuffer.length - 1]?.timestamp,
|
|
1024
|
-
},
|
|
1025
|
-
};
|
|
1026
|
-
for (const log of this.logBuffer) {
|
|
1027
|
-
// Count by type
|
|
1028
|
-
stats.byType[log.type] = (stats.byType[log.type] || 0) + 1;
|
|
1029
|
-
// Count errors and metrics
|
|
1030
|
-
if (log.error)
|
|
1031
|
-
stats.withErrors++;
|
|
1032
|
-
if (log.metrics)
|
|
1033
|
-
stats.withMetrics++;
|
|
1034
|
-
}
|
|
1035
|
-
return stats;
|
|
1036
|
-
}
|
|
1037
|
-
/**
|
|
1038
|
-
* Clear log buffer
|
|
1039
|
-
*/
|
|
1040
|
-
clearLogs() {
|
|
1041
|
-
this.logBuffer = [];
|
|
1042
|
-
}
|
|
1043
|
-
/**
|
|
1044
|
-
* Set maximum buffer size
|
|
1045
|
-
*/
|
|
1046
|
-
setMaxBufferSize(size) {
|
|
1047
|
-
this.maxBufferSize = size;
|
|
1048
|
-
// Trim buffer if needed
|
|
1049
|
-
while (this.logBuffer.length > this.maxBufferSize) {
|
|
1050
|
-
this.logBuffer.shift();
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
/**
|
|
1054
|
-
* Get current buffer size
|
|
1055
|
-
*/
|
|
1056
|
-
getBufferSize() {
|
|
1057
|
-
return this.logBuffer.length;
|
|
1058
|
-
}
|
|
1059
|
-
/**
|
|
1060
|
-
* Export logs in a comprehensive format for consumers (CLI, API, dashboards)
|
|
1061
|
-
*/
|
|
1062
|
-
exportLogs() {
|
|
1063
|
-
const stats = this.getLogStats();
|
|
1064
|
-
const grouped = this.exportLogsGrouped();
|
|
1065
|
-
// Build execution summary from logs
|
|
1066
|
-
const execution = this.buildExecutionSummary();
|
|
1067
|
-
return {
|
|
1068
|
-
raw: this.getJSONLogs(),
|
|
1069
|
-
grouped,
|
|
1070
|
-
stats,
|
|
1071
|
-
execution,
|
|
1072
|
-
};
|
|
1073
|
-
}
|
|
1074
|
-
/**
|
|
1075
|
-
* Build execution summary from collected logs
|
|
1076
|
-
* This powers dynamic explanation generation
|
|
1077
|
-
*/
|
|
1078
|
-
buildExecutionSummary() {
|
|
1079
|
-
const workflowLogs = this.logBuffer.filter(log => log.type === EngineLogType.WORKFLOW_STARTED ||
|
|
1080
|
-
log.type === EngineLogType.WORKFLOW_COMPLETED ||
|
|
1081
|
-
log.type === EngineLogType.WORKFLOW_FAILED);
|
|
1082
|
-
const stepLogs = this.logBuffer.filter(log => log.type === EngineLogType.STEP_STARTED ||
|
|
1083
|
-
log.type === EngineLogType.STEP_COMPLETED ||
|
|
1084
|
-
log.type === EngineLogType.STEP_FAILED);
|
|
1085
|
-
const errorLogs = this.logBuffer.filter(log => log.error !== undefined ||
|
|
1086
|
-
log.type === EngineLogType.ERROR_DETECTED ||
|
|
1087
|
-
log.type === EngineLogType.VALIDATION_ERROR);
|
|
1088
|
-
const metricLogs = this.logBuffer.filter(log => log.type === EngineLogType.PERFORMANCE_METRIC ||
|
|
1089
|
-
log.type === EngineLogType.EXECUTION_TIME);
|
|
1090
|
-
// Extract workflow info
|
|
1091
|
-
let workflow;
|
|
1092
|
-
const workflowStarted = workflowLogs.find(log => log.type === EngineLogType.WORKFLOW_STARTED);
|
|
1093
|
-
const workflowEnded = workflowLogs.find(log => log.type === EngineLogType.WORKFLOW_COMPLETED ||
|
|
1094
|
-
log.type === EngineLogType.WORKFLOW_FAILED);
|
|
1095
|
-
if (workflowStarted && workflowEnded) {
|
|
1096
|
-
workflow = {
|
|
1097
|
-
name: workflowStarted.context?.workflowName || 'Unknown',
|
|
1098
|
-
status: workflowEnded.type === EngineLogType.WORKFLOW_COMPLETED ? 'completed' : 'failed',
|
|
1099
|
-
duration: workflowEnded.metrics?.duration,
|
|
1100
|
-
};
|
|
1101
|
-
}
|
|
1102
|
-
// Extract step info
|
|
1103
|
-
const steps = [];
|
|
1104
|
-
const stepMap = new Map();
|
|
1105
|
-
for (const log of stepLogs) {
|
|
1106
|
-
const stepId = log.context?.stepId || '';
|
|
1107
|
-
const stepName = log.context?.stepName || '';
|
|
1108
|
-
if (!stepMap.has(stepId)) {
|
|
1109
|
-
stepMap.set(stepId, { id: stepId, name: stepName });
|
|
1110
|
-
}
|
|
1111
|
-
const step = stepMap.get(stepId);
|
|
1112
|
-
if (log.type === EngineLogType.STEP_COMPLETED) {
|
|
1113
|
-
step.status = 'completed';
|
|
1114
|
-
step.duration = log.metrics?.duration;
|
|
1115
|
-
}
|
|
1116
|
-
else if (log.type === EngineLogType.STEP_FAILED) {
|
|
1117
|
-
step.status = 'failed';
|
|
1118
|
-
}
|
|
1119
|
-
else {
|
|
1120
|
-
step.status = 'started';
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
steps.push(...stepMap.values());
|
|
1124
|
-
// Extract error info
|
|
1125
|
-
const errors = errorLogs.map(log => ({
|
|
1126
|
-
step: log.context?.stepId || log.context?.stepName,
|
|
1127
|
-
message: log.message,
|
|
1128
|
-
error: log.error,
|
|
1129
|
-
}));
|
|
1130
|
-
// Extract metrics
|
|
1131
|
-
const metrics = metricLogs.map(log => ({
|
|
1132
|
-
label: log.context?.label || 'Unknown',
|
|
1133
|
-
duration: log.metrics?.duration,
|
|
1134
|
-
memory: log.metrics?.memory,
|
|
1135
|
-
}));
|
|
1136
|
-
return {
|
|
1137
|
-
workflow,
|
|
1138
|
-
steps,
|
|
1139
|
-
errors,
|
|
1140
|
-
metrics,
|
|
1141
|
-
};
|
|
1142
|
-
}
|
|
1143
719
|
}
|
|
1144
720
|
/**
|
|
1145
721
|
* Create a logger instance from engine config
|
|
@@ -1160,11 +736,17 @@ export function createEngineLogger(logLevel, verbose = false) {
|
|
|
1160
736
|
const level = levelMap[logLevel] || LogLevel.INFO;
|
|
1161
737
|
return new EngineLogger({
|
|
1162
738
|
level,
|
|
1163
|
-
format: verbose ? 'pretty' : '
|
|
739
|
+
format: verbose ? 'pretty' : 'json',
|
|
1164
740
|
colors: true,
|
|
1165
|
-
timestamp: true,
|
|
741
|
+
timestamp: true,
|
|
1166
742
|
source: 'Orbyt',
|
|
1167
|
-
|
|
743
|
+
category: LogCategoryEnum.SYSTEM,
|
|
744
|
+
structuredEvents: true,
|
|
1168
745
|
});
|
|
1169
746
|
}
|
|
747
|
+
/**
|
|
748
|
+
* Re-export formatTimestamp for external use
|
|
749
|
+
* Allows other parts of the system to format timestamps consistently
|
|
750
|
+
*/
|
|
751
|
+
export { formatTimestamp };
|
|
1170
752
|
//# sourceMappingURL=EngineLogger.js.map
|