bulltrackers-module 1.0.228 → 1.0.229
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Structured Logging System for Computation Engine
|
|
3
3
|
* Provides comprehensive tracking with process IDs, context, and filtering capabilities.
|
|
4
|
-
* UPDATED:
|
|
4
|
+
* UPDATED: Fixed Date Analysis keys to match WorkflowOrchestrator (failedDependency vs resolving).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const crypto = require('crypto');
|
|
@@ -28,21 +28,11 @@ const PROCESS_TYPES = {
|
|
|
28
28
|
STORAGE: 'storage',
|
|
29
29
|
WORKER: 'worker',
|
|
30
30
|
ANALYSIS: 'analysis',
|
|
31
|
-
// Legacy / Specific aliases
|
|
32
|
-
SCHEMA_GENERATION: 'schema_generation',
|
|
33
|
-
COMPUTATION_EXECUTION: 'computation_execution',
|
|
34
|
-
DEPENDENCY_FETCH: 'dependency_fetch',
|
|
35
|
-
DATA_AVAILABILITY: 'data_availability',
|
|
36
31
|
DISPATCH: 'dispatch'
|
|
37
32
|
};
|
|
38
33
|
|
|
39
34
|
/**
|
|
40
|
-
* Generates a deterministic process ID
|
|
41
|
-
* Ensures that logs for the same computation/date always have the same Trace ID.
|
|
42
|
-
* @param {string} type - Process type (e.g., 'orchestrator')
|
|
43
|
-
* @param {string} identifier - Unique key (e.g., pass name, calculation name)
|
|
44
|
-
* @param {string} date - Date string YYYY-MM-DD (optional)
|
|
45
|
-
* @returns {string} 16-character hexadecimal process ID
|
|
35
|
+
* Generates a deterministic process ID.
|
|
46
36
|
*/
|
|
47
37
|
function generateProcessId(type, identifier, date = '') {
|
|
48
38
|
const input = `${type}|${identifier}|${date}`;
|
|
@@ -63,16 +53,12 @@ function formatLogEntry(entry) {
|
|
|
63
53
|
message: entry.message,
|
|
64
54
|
context: entry.context,
|
|
65
55
|
metadata: entry.metadata,
|
|
66
|
-
// Specific fields for specialized logs
|
|
67
56
|
stats: entry.stats,
|
|
68
57
|
storage: entry.storage,
|
|
69
58
|
details: entry.details
|
|
70
59
|
});
|
|
71
60
|
}
|
|
72
61
|
|
|
73
|
-
/**
|
|
74
|
-
* Main Structured Logger Class
|
|
75
|
-
*/
|
|
76
62
|
class StructuredLogger {
|
|
77
63
|
constructor(config = {}) {
|
|
78
64
|
this.config = {
|
|
@@ -86,15 +72,7 @@ class StructuredLogger {
|
|
|
86
72
|
this.activeProcesses = new Map();
|
|
87
73
|
}
|
|
88
74
|
|
|
89
|
-
/**
|
|
90
|
-
* Starts a tracked process and returns a ProcessLogger
|
|
91
|
-
* @param {string} processType - Type from PROCESS_TYPES
|
|
92
|
-
* @param {string} computationName - Optional computation name
|
|
93
|
-
* @param {string} date - Optional date string
|
|
94
|
-
* @returns {ProcessLogger}
|
|
95
|
-
*/
|
|
96
75
|
startProcess(processType, computationName = null, date = null) {
|
|
97
|
-
// Use deterministic ID if components are present, else random/time-based fallback
|
|
98
76
|
const processId = (computationName || date)
|
|
99
77
|
? generateProcessId(processType, computationName || 'general', date)
|
|
100
78
|
: crypto.randomBytes(8).toString('hex');
|
|
@@ -112,9 +90,6 @@ class StructuredLogger {
|
|
|
112
90
|
return processLogger;
|
|
113
91
|
}
|
|
114
92
|
|
|
115
|
-
/**
|
|
116
|
-
* Ends a tracked process
|
|
117
|
-
*/
|
|
118
93
|
endProcess(processId) {
|
|
119
94
|
const process = this.activeProcesses.get(processId);
|
|
120
95
|
if (process) {
|
|
@@ -126,13 +101,20 @@ class StructuredLogger {
|
|
|
126
101
|
}
|
|
127
102
|
|
|
128
103
|
/**
|
|
129
|
-
* The Main Date Analysis Logger (
|
|
130
|
-
*
|
|
104
|
+
* The Main Date Analysis Logger (FIXED)
|
|
105
|
+
* Handles 'failedDependency', 'skipped', and 'blocked' correctly.
|
|
131
106
|
*/
|
|
132
107
|
logDateAnalysis(dateStr, analysisReport) {
|
|
133
|
-
|
|
108
|
+
// Destructure with SAFETY DEFAULTS to prevent crashes
|
|
109
|
+
const {
|
|
110
|
+
runnable = [],
|
|
111
|
+
blocked = [],
|
|
112
|
+
reRuns = [],
|
|
113
|
+
failedDependency = [], // Matches WorkflowOrchestrator
|
|
114
|
+
skipped = [] // Matches WorkflowOrchestrator
|
|
115
|
+
} = analysisReport;
|
|
134
116
|
|
|
135
|
-
// 1. Structured Output
|
|
117
|
+
// 1. Structured Output
|
|
136
118
|
if (this.config.enableStructured) {
|
|
137
119
|
console.log(JSON.stringify({
|
|
138
120
|
timestamp: new Date().toISOString(),
|
|
@@ -144,29 +126,32 @@ class StructuredLogger {
|
|
|
144
126
|
runnable: runnable.length,
|
|
145
127
|
blocked: blocked.length,
|
|
146
128
|
reRuns: reRuns.length,
|
|
147
|
-
|
|
129
|
+
failedDependency: failedDependency.length,
|
|
130
|
+
skipped: skipped.length
|
|
148
131
|
},
|
|
149
132
|
details: analysisReport
|
|
150
133
|
}));
|
|
151
134
|
}
|
|
152
135
|
|
|
153
|
-
// 2. Human Readable Output
|
|
136
|
+
// 2. Human Readable Output
|
|
154
137
|
if (this.config.enableConsole) {
|
|
155
|
-
const symbols = { info: 'ℹ️', warn: '⚠️', check: '✅', block: '⛔', link: '🔗', cycle: '🔄' };
|
|
138
|
+
const symbols = { info: 'ℹ️', warn: '⚠️', check: '✅', block: '⛔', link: '🔗', cycle: '🔄', skip: '⏭️' };
|
|
156
139
|
|
|
157
140
|
console.log(`\n🔍 === DATE ANALYSIS REPORT: ${dateStr} ===`);
|
|
158
141
|
|
|
159
142
|
if (reRuns.length) {
|
|
160
143
|
console.log(`\n${symbols.cycle} [HASH MISMATCH / RE-RUNS]`);
|
|
161
144
|
reRuns.forEach(item => {
|
|
162
|
-
console.log(` • ${item.name}: Hash changed (Old: ${item.oldHash?.substring(0,6)}... New: ${item.newHash?.substring(0,6)}...)
|
|
145
|
+
console.log(` • ${item.name}: Hash changed. (Old: ${item.oldHash?.substring(0,6)}... New: ${item.newHash?.substring(0,6)}...)`);
|
|
163
146
|
});
|
|
164
147
|
}
|
|
165
148
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
149
|
+
// Updated to handle failedDependency instead of resolving
|
|
150
|
+
if (failedDependency.length) {
|
|
151
|
+
console.log(`\n${symbols.block} [FAILED DEPENDENCIES] (Missing upstream data)`);
|
|
152
|
+
failedDependency.forEach(item => {
|
|
153
|
+
// item.missing comes from Orchestrator (not item.missingDeps)
|
|
154
|
+
console.log(` • ${item.name}: Missing ${item.missing ? item.missing.join(', ') : 'dependencies'}`);
|
|
170
155
|
});
|
|
171
156
|
}
|
|
172
157
|
|
|
@@ -176,20 +161,27 @@ class StructuredLogger {
|
|
|
176
161
|
}
|
|
177
162
|
|
|
178
163
|
if (blocked.length) {
|
|
179
|
-
console.log(`\n${symbols.
|
|
164
|
+
console.log(`\n${symbols.warn} [BLOCKED] (Missing Root Data)`);
|
|
180
165
|
blocked.forEach(item => {
|
|
181
166
|
console.log(` • ${item.name}: ${item.reason}`);
|
|
182
167
|
});
|
|
183
168
|
}
|
|
169
|
+
|
|
170
|
+
if (skipped.length) {
|
|
171
|
+
// Optional: Only show count if list is huge
|
|
172
|
+
if (skipped.length > 10) {
|
|
173
|
+
console.log(`\n${symbols.skip} [SKIPPED] (${skipped.length} calculations up-to-date)`);
|
|
174
|
+
} else {
|
|
175
|
+
console.log(`\n${symbols.skip} [SKIPPED]`);
|
|
176
|
+
skipped.forEach(item => console.log(` • ${item.name}`));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
184
180
|
console.log(`\n=============================================\n`);
|
|
185
181
|
}
|
|
186
182
|
}
|
|
187
183
|
|
|
188
|
-
/**
|
|
189
|
-
* Storage Observability Logger (NEW)
|
|
190
|
-
*/
|
|
191
184
|
logStorage(processId, calcName, date, path, sizeBytes, isSharded) {
|
|
192
|
-
// Standard Log Call with extended metadata
|
|
193
185
|
this.log(LOG_LEVELS.INFO, `Results stored for ${calcName}`, {
|
|
194
186
|
storage: {
|
|
195
187
|
path,
|
|
@@ -200,16 +192,10 @@ class StructuredLogger {
|
|
|
200
192
|
}, PROCESS_TYPES.STORAGE, processId, calcName, date);
|
|
201
193
|
}
|
|
202
194
|
|
|
203
|
-
/**
|
|
204
|
-
* Core logging method
|
|
205
|
-
*/
|
|
206
195
|
log(level, message, context = {}, processType = null, processId = null, computationName = null, date = null) {
|
|
207
196
|
const numericLevel = typeof level === 'string' ? LOG_LEVELS[level] : level;
|
|
208
|
-
|
|
209
197
|
if (numericLevel < this.config.minLevel) return;
|
|
210
198
|
|
|
211
|
-
// Support passing "meta" object in place of context for newer calls
|
|
212
|
-
// Logic: If context contains 'processId' or 'storage', it's likely a meta object
|
|
213
199
|
let finalContext = context;
|
|
214
200
|
let finalMetadata = {};
|
|
215
201
|
let finalStats = undefined;
|
|
@@ -222,7 +208,6 @@ class StructuredLogger {
|
|
|
222
208
|
if (context.date) date = context.date;
|
|
223
209
|
if (context.stats) finalStats = context.stats;
|
|
224
210
|
if (context.storage) finalStorage = context.storage;
|
|
225
|
-
// Clean up context to be just the data remaining
|
|
226
211
|
finalContext = { ...context };
|
|
227
212
|
delete finalContext.processId; delete finalContext.processType;
|
|
228
213
|
delete finalContext.computationName; delete finalContext.date;
|
|
@@ -243,25 +228,19 @@ class StructuredLogger {
|
|
|
243
228
|
storage: finalStorage
|
|
244
229
|
};
|
|
245
230
|
|
|
246
|
-
// Add stack trace for errors
|
|
247
231
|
if (numericLevel >= LOG_LEVELS.ERROR && this.config.includeStackTrace && finalContext.stack) {
|
|
248
232
|
entry.metadata.stackTrace = finalContext.stack;
|
|
249
233
|
}
|
|
250
234
|
|
|
251
|
-
// Console output (pretty-printed for development)
|
|
252
235
|
if (this.config.enableConsole) {
|
|
253
236
|
this._consoleLog(entry);
|
|
254
237
|
}
|
|
255
238
|
|
|
256
|
-
// Structured output (for log aggregation systems)
|
|
257
239
|
if (this.config.enableStructured) {
|
|
258
240
|
console.log(formatLogEntry(entry));
|
|
259
241
|
}
|
|
260
242
|
}
|
|
261
243
|
|
|
262
|
-
/**
|
|
263
|
-
* Pretty console output for development
|
|
264
|
-
*/
|
|
265
244
|
_consoleLog(entry) {
|
|
266
245
|
const symbols = { TRACE: '🔍', DEBUG: '🐛', INFO: 'ℹ️', WARN: '⚠️', ERROR: '❌', FATAL: '💀' };
|
|
267
246
|
const colors = {
|
|
@@ -273,38 +252,19 @@ class StructuredLogger {
|
|
|
273
252
|
const symbol = symbols[entry.level] || 'ℹ️';
|
|
274
253
|
|
|
275
254
|
let output = `${color}${symbol} [${entry.level}]${reset}`;
|
|
276
|
-
|
|
277
255
|
if (entry.processType) output += ` [${entry.processType}]`;
|
|
278
|
-
if (entry.processId) output += ` [${entry.processId.substring(0, 8)}]`;
|
|
279
256
|
if (entry.computationName) output += ` [${entry.computationName}]`;
|
|
280
|
-
if (entry.date) output += ` [${entry.date}]`;
|
|
281
257
|
|
|
282
258
|
output += ` ${entry.message}`;
|
|
283
259
|
|
|
284
260
|
console.log(output);
|
|
285
|
-
|
|
286
|
-
// Print context if present and not empty
|
|
287
261
|
if (entry.context && Object.keys(entry.context).length > 0) {
|
|
288
|
-
console.log(`
|
|
289
|
-
}
|
|
290
|
-
if (entry.storage) {
|
|
291
|
-
console.log(` ${color}Storage:${reset}`, entry.storage);
|
|
262
|
+
console.log(` Context:`, entry.context);
|
|
292
263
|
}
|
|
293
264
|
}
|
|
294
|
-
|
|
295
|
-
// Convenience methods
|
|
296
|
-
trace(message, context = {}) { this.log(LOG_LEVELS.TRACE, message, context); }
|
|
297
|
-
debug(message, context = {}) { this.log(LOG_LEVELS.DEBUG, message, context); }
|
|
298
|
-
info(message, context = {}) { this.log(LOG_LEVELS.INFO, message, context); }
|
|
299
|
-
warn(message, context = {}) { this.log(LOG_LEVELS.WARN, message, context); }
|
|
300
|
-
error(message, context = {}) { this.log(LOG_LEVELS.ERROR, message, context); }
|
|
301
|
-
fatal(message, context = {}) { this.log(LOG_LEVELS.FATAL, message, context); }
|
|
302
265
|
}
|
|
303
266
|
|
|
304
|
-
|
|
305
|
-
* Process-scoped Logger
|
|
306
|
-
* Automatically includes process context in all log calls
|
|
307
|
-
*/
|
|
267
|
+
// ... ProcessLogger and Templates (unchanged) ...
|
|
308
268
|
class ProcessLogger {
|
|
309
269
|
constructor(parent, processType, processId, computationName, date) {
|
|
310
270
|
this.parent = parent;
|
|
@@ -313,142 +273,20 @@ class ProcessLogger {
|
|
|
313
273
|
this.computationName = computationName;
|
|
314
274
|
this.date = date;
|
|
315
275
|
this.startTime = Date.now();
|
|
316
|
-
this.metrics = {
|
|
317
|
-
operations: 0,
|
|
318
|
-
errors: 0,
|
|
319
|
-
warnings: 0
|
|
320
|
-
};
|
|
276
|
+
this.metrics = { operations: 0, errors: 0, warnings: 0 };
|
|
321
277
|
}
|
|
322
|
-
|
|
323
278
|
log(level, message, context = {}) {
|
|
324
|
-
const numericLevel = typeof level === 'string' ? LOG_LEVELS[level] : level;
|
|
325
|
-
|
|
326
279
|
this.metrics.operations++;
|
|
327
|
-
|
|
328
|
-
this.metrics.errors++;
|
|
329
|
-
}
|
|
330
|
-
if (numericLevel === LOG_LEVELS.WARN) {
|
|
331
|
-
this.metrics.warnings++;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
this.parent.log(
|
|
335
|
-
level,
|
|
336
|
-
message,
|
|
337
|
-
context,
|
|
338
|
-
this.processType,
|
|
339
|
-
this.processId,
|
|
340
|
-
this.computationName,
|
|
341
|
-
this.date
|
|
342
|
-
);
|
|
280
|
+
this.parent.log(level, message, context, this.processType, this.processId, this.computationName, this.date);
|
|
343
281
|
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Complete the process and log summary
|
|
347
|
-
*/
|
|
348
282
|
complete(success = true, finalMessage = null) {
|
|
349
283
|
const duration = Date.now() - this.startTime;
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
? `Process completed successfully`
|
|
354
|
-
: `Process completed with errors`);
|
|
355
|
-
|
|
356
|
-
this.log(level, summaryMessage, {
|
|
357
|
-
stats: {
|
|
358
|
-
duration: `${duration}ms`,
|
|
359
|
-
durationMs: duration,
|
|
360
|
-
operations: this.metrics.operations,
|
|
361
|
-
errors: this.metrics.errors,
|
|
362
|
-
warnings: this.metrics.warnings,
|
|
363
|
-
success
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
|
|
284
|
+
this.log(success ? LOG_LEVELS.INFO : LOG_LEVELS.ERROR,
|
|
285
|
+
finalMessage || (success ? 'Process completed' : 'Process failed'),
|
|
286
|
+
{ stats: { durationMs: duration, ...this.metrics, success } });
|
|
367
287
|
this.parent.endProcess(this.processId);
|
|
368
288
|
return duration;
|
|
369
289
|
}
|
|
370
|
-
|
|
371
|
-
// Convenience methods
|
|
372
|
-
trace(message, context = {}) { this.log(LOG_LEVELS.TRACE, message, context); }
|
|
373
|
-
debug(message, context = {}) { this.log(LOG_LEVELS.DEBUG, message, context); }
|
|
374
|
-
info(message, context = {}) { this.log(LOG_LEVELS.INFO, message, context); }
|
|
375
|
-
warn(message, context = {}) { this.log(LOG_LEVELS.WARN, message, context); }
|
|
376
|
-
error(message, context = {}) { this.log(LOG_LEVELS.ERROR, message, context); }
|
|
377
|
-
fatal(message, context = {}) { this.log(LOG_LEVELS.FATAL, message, context); }
|
|
378
290
|
}
|
|
379
291
|
|
|
380
|
-
|
|
381
|
-
* Log Message Templates
|
|
382
|
-
*/
|
|
383
|
-
const LOG_TEMPLATES = {
|
|
384
|
-
// Schema Generation
|
|
385
|
-
SCHEMA_SUCCESS: (computationName) =>
|
|
386
|
-
`Schema generation successful for ${computationName}`,
|
|
387
|
-
SCHEMA_FAILURE: (computationName, reason) =>
|
|
388
|
-
`Schema generation failed for ${computationName}: ${reason}`,
|
|
389
|
-
|
|
390
|
-
// Computation Execution
|
|
391
|
-
COMPUTATION_START: (computationName, date) =>
|
|
392
|
-
`Starting computation ${computationName} for ${date}`,
|
|
393
|
-
COMPUTATION_SUCCESS: (computationName, date) =>
|
|
394
|
-
`Computation successful for ${computationName} on ${date}`,
|
|
395
|
-
COMPUTATION_FAILURE: (computationName, date, reason) =>
|
|
396
|
-
`Computation failed for ${computationName} on ${date}: ${reason}`,
|
|
397
|
-
|
|
398
|
-
// Storage
|
|
399
|
-
STORAGE_SUCCESS: (computationName, date, path, size) =>
|
|
400
|
-
`Results stored for ${computationName} on ${date} at ${path} (${size} bytes)`,
|
|
401
|
-
STORAGE_FAILURE: (computationName, date, path, reason) =>
|
|
402
|
-
`Failed to store results for ${computationName} on ${date} at ${path}: ${reason}`,
|
|
403
|
-
|
|
404
|
-
// Hash Validation
|
|
405
|
-
HASH_MISMATCH: (computationName, storedHash, currentHash) =>
|
|
406
|
-
`Hash mismatch for ${computationName}: stored=${storedHash}, current=${currentHash}`,
|
|
407
|
-
HASH_MATCH: (computationName) =>
|
|
408
|
-
`Hash match for ${computationName}, no code changes detected`,
|
|
409
|
-
HASH_CASCADE: (computationName, affectedComputations) =>
|
|
410
|
-
`Code change in ${computationName} will cascade to: ${affectedComputations.join(', ')}`,
|
|
411
|
-
|
|
412
|
-
// Manifest
|
|
413
|
-
MANIFEST_SUCCESS: (computationCount) =>
|
|
414
|
-
`Manifest built successfully with ${computationCount} computations`,
|
|
415
|
-
MANIFEST_TREE: (tree) =>
|
|
416
|
-
`Dependency tree:\n${tree}`,
|
|
417
|
-
|
|
418
|
-
// Date Analysis
|
|
419
|
-
DATE_ANALYSIS: (date, runnable, notRunnable) =>
|
|
420
|
-
`Date ${date}: ${runnable.length} runnable, ${notRunnable.length} blocked`,
|
|
421
|
-
DATE_MISSING_ROOTDATA: (date, computationName, missingData) =>
|
|
422
|
-
`${computationName} on ${date}: Cannot run due to missing root data: ${missingData.join(', ')}`,
|
|
423
|
-
DATE_MISSING_DEPENDENCY: (date, computationName, missingDep) =>
|
|
424
|
-
`${computationName} on ${date}: Will resolve after ${missingDep} completes`,
|
|
425
|
-
DATE_HASH_RERUN: (date, computationName, affectedDeps) =>
|
|
426
|
-
`${computationName} on ${date}: Hash mismatch, re-running (affects: ${affectedDeps.join(', ')})`,
|
|
427
|
-
|
|
428
|
-
// Availability
|
|
429
|
-
DATA_AVAILABLE: (date, types) =>
|
|
430
|
-
`Data available for ${date}: ${types.join(', ')}`,
|
|
431
|
-
DATA_MISSING: (date, types) =>
|
|
432
|
-
`Data missing for ${date}: ${types.join(', ')}`,
|
|
433
|
-
|
|
434
|
-
// Dispatch/Worker
|
|
435
|
-
DISPATCH_START: (pass, dateCount) =>
|
|
436
|
-
`Dispatching Pass ${pass} for ${dateCount} dates`,
|
|
437
|
-
DISPATCH_COMPLETE: (pass, dispatched) =>
|
|
438
|
-
`Dispatch complete: ${dispatched} tasks for Pass ${pass}`,
|
|
439
|
-
WORKER_TASK_START: (date, pass) =>
|
|
440
|
-
`Worker starting task: Date=${date}, Pass=${pass}`,
|
|
441
|
-
WORKER_TASK_COMPLETE: (date, pass, updateCount) =>
|
|
442
|
-
`Worker completed task: Date=${date}, Pass=${pass}, Updates=${updateCount}`,
|
|
443
|
-
WORKER_TASK_SKIP: (date, pass, reason) =>
|
|
444
|
-
`Worker skipped task: Date=${date}, Pass=${pass}, Reason=${reason}`
|
|
445
|
-
};
|
|
446
|
-
|
|
447
|
-
module.exports = {
|
|
448
|
-
StructuredLogger,
|
|
449
|
-
ProcessLogger,
|
|
450
|
-
LOG_LEVELS,
|
|
451
|
-
PROCESS_TYPES,
|
|
452
|
-
LOG_TEMPLATES,
|
|
453
|
-
generateProcessId
|
|
454
|
-
};
|
|
292
|
+
module.exports = { StructuredLogger, ProcessLogger, LOG_LEVELS, PROCESS_TYPES, generateProcessId };
|