UNPKG

@comake/skl-js-engine

Version:

Standard Knowledge Language Javascript Engine

223 lines 9.07 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PerformanceLogger = void 0; /* eslint-disable require-unicode-regexp */ /* eslint-disable @typescript-eslint/naming-convention */ const perf_hooks_1 = require("perf_hooks"); let AsyncLocalStorage; try { // eslint-disable-next-line @typescript-eslint/no-require-imports, prefer-destructuring, global-require, @typescript-eslint/no-var-requires AsyncLocalStorage = require('async_hooks').AsyncLocalStorage; } catch { // AsyncLocalStorage not available } // eslint-disable-next-line @typescript-eslint/no-extraneous-class class PerformanceLogger { static initialize() { if (this.initialized) { return; } this.initialized = true; // eslint-disable-next-line no-process-env this.enabled = process.env.SKL_ENGINE_DEBUG_QUERIES === 'true' || // eslint-disable-next-line no-process-env process.env.SKL_ENGINE_DEBUG_QUERIES === '1'; if (this.enabled && AsyncLocalStorage) { this.asyncLocalStorage = new AsyncLocalStorage(); } } static generateSpanId() { // eslint-disable-next-line no-plusplus return `span-${++this.spanCounter}-${Date.now()}`; } static startSpan(name, attributes) { this.initialize(); if (!this.enabled) { return null; } const parent = this.asyncLocalStorage?.getStore(); const span = { id: this.generateSpanId(), parentId: parent?.id, name, startTime: perf_hooks_1.performance.now(), attributes: attributes ?? {}, children: [] }; if (parent) { parent.children.push(span); } return span; } static endSpan(span, additionalAttributes) { if (!this.enabled || !span) { return; } span.endTime = perf_hooks_1.performance.now(); span.duration = span.endTime - span.startTime; if (additionalAttributes) { span.attributes = { ...span.attributes, ...additionalAttributes }; } } static async withSpan(name, fn, attributes) { this.initialize(); if (!this.enabled) { return fn(); } const span = this.startSpan(name, attributes); if (this.asyncLocalStorage) { return this.asyncLocalStorage.run(span, async () => { try { const result = await fn(); this.endSpan(span); return result; } catch (error) { this.endSpan(span, { error: true, errorMessage: error instanceof Error ? error.message : String(error) }); throw error; } }); } // Fallback without AsyncLocalStorage try { const result = await fn(); this.endSpan(span); return result; } catch (error) { this.endSpan(span, { error: true, errorMessage: error instanceof Error ? error.message : String(error) }); throw error; } } static async withSpanRoot(name, fn, attributes) { this.initialize(); if (!this.enabled) { return fn(); } const span = this.startSpan(name, attributes); if (this.asyncLocalStorage) { return this.asyncLocalStorage.run(span, async () => { try { const result = await fn(); this.endSpan(span); this.logSpanTree(span); return result; } catch (error) { this.endSpan(span, { error: true, errorMessage: error instanceof Error ? error.message : String(error) }); this.logSpanTree(span); throw error; } }); } // Fallback without AsyncLocalStorage try { const result = await fn(); this.endSpan(span); this.logSpanTree(span); return result; } catch (error) { this.endSpan(span, { error: true, errorMessage: error instanceof Error ? error.message : String(error) }); this.logSpanTree(span); throw error; } } static getCurrentSpan() { this.initialize(); if (!this.enabled || !this.asyncLocalStorage) { return undefined; } return this.asyncLocalStorage.getStore(); } static logSpanTree(rootSpan) { if (!this.enabled || !rootSpan) { return; } const timestamp = new Date().toISOString(); const lines = []; lines.push(''); lines.push('╔══════════════════════════════════════════════════════════════╗'); lines.push(`║ SKL Engine Performance Trace ║`); lines.push(`║ Started: ${timestamp} ║`); lines.push('╚══════════════════════════════════════════════════════════════╝'); lines.push(''); this.buildSpanTreeLines(rootSpan, lines, '', true); lines.push(''); lines.push('═══════════════════════════════════════════════════════════════'); lines.push(''); // eslint-disable-next-line no-console console.log(lines.join('\n')); } static buildSpanTreeLines(span, lines, prefix, isRoot) { const duration = span.duration !== undefined ? `${span.duration.toFixed(2)}ms` : 'running...'; const status = span.attributes.error ? '✗' : '✓'; lines.push(`${prefix}${span.name} [${duration}] ${status}`); // Show important attributes this.addAttributeLines(span, lines, prefix); // Process children const childCount = span.children.length; span.children.forEach((child, index) => { const isLast = index === childCount - 1; const childPrefix = prefix + (isRoot ? '' : '│ '); const connector = isLast ? '└─ ' : '├─ '; const nextPrefix = prefix + (isRoot ? '' : (isLast ? ' ' : '│ ')); this.buildSpanTreeLines(child, lines, childPrefix + connector, false); }); } static addAttributeLines(span, lines, prefix) { const { query, resultCount, options, error, errorMessage, ...otherAttrs } = span.attributes; // Show query if present (and format it) - but make it easy to copy if (query && typeof query === 'string') { lines.push(`${prefix}│ Query:`); // Empty line before query lines.push(''); const formattedQuery = this.formatQuery(query); formattedQuery.split('\n').forEach(line => { // Simple indentation without tree chars lines.push(` ${line}`); }); // Empty line after query lines.push(''); } // Show result count if (resultCount !== undefined) { lines.push(`${prefix}│ Result: ${resultCount} ${resultCount === 1 ? 'row' : 'rows'}`); } // Show error if present if (error && errorMessage) { lines.push(`${prefix}│ Error: ${errorMessage}`); } // Show other important attributes if (Object.keys(otherAttrs).length > 0) { const attrStr = JSON.stringify(otherAttrs); if (attrStr.length < 100) { lines.push(`${prefix}│ Attributes: ${attrStr}`); } } } static formatQuery(query) { // Simple query formatting with proper indentation let formatted = query.trim(); // Add line breaks after key SPARQL keywords for readability formatted = formatted .replace(/\s+/g, ' ') .replace(/\s*{\s*/g, ' {\n ') .replace(/\s*}\s*/g, '\n}') .replace(/\s*\.\s*(?=[?A-Z])/g, ' .\n ') .replace(/\bWHERE\b/g, '\nWHERE') .replace(/\bFILTER\b/g, '\n FILTER') .replace(/\bOPTIONAL\b/g, '\n OPTIONAL') .replace(/\bUNION\b/g, '\nUNION') .replace(/\bGRAPH\b/g, '\n GRAPH') .replace(/\bORDER BY\b/g, '\nORDER BY') .replace(/\bLIMIT\b/g, '\nLIMIT') .replace(/\bOFFSET\b/g, '\nOFFSET'); return formatted; } } exports.PerformanceLogger = PerformanceLogger; PerformanceLogger.initialized = false; PerformanceLogger.spanCounter = 0; //# sourceMappingURL=PerformanceLogger.js.map