@comake/skl-js-engine
Version:
Standard Knowledge Language Javascript Engine
223 lines • 9.07 kB
JavaScript
;
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