UNPKG

@trifrost/core

Version:

Blazingly fast, runtime-agnostic server framework for modern edge and node environments

151 lines (150 loc) 4.61 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Logger = void 0; exports.isValidTraceId = isValidTraceId; const Generic_1 = require("../../utils/Generic"); /* Ensure valid trace id, a otel trace id IS a 32 hexadecimal char string 0-9 a-f */ function isValidTraceId(str) { if (str.length !== 32) return false; let c; for (let i = 31; i >= 0; i--) { c = str.charCodeAt(i); if ((c < 48 || c > 57) && (c < 97 || c > 102)) return false; } return true; } class Logger { #debug; #traceId = null; #activeSpanId = null; #attributes = {}; #exporters; #spanAwareExporters = []; constructor(cfg) { this.#debug = cfg.debug; this.#attributes = { ...(cfg.context ?? {}) }; /* Set trace id */ if (cfg.traceId) this.#traceId = isValidTraceId(cfg.traceId) ? cfg.traceId : (0, Generic_1.hexId)(16); this.#exporters = cfg.exporters; this.#spanAwareExporters = cfg.spanAwareExporters; } get traceId() { return this.#traceId; } setDebug(val) { this.#debug = !!val; } setAttribute(key, value) { this.#attributes[key] = value; return this; } setAttributes(obj) { Object.assign(this.#attributes, obj); return this; } debug(msg, data) { if (this.#debug) this.#log('debug', msg, data); } info(msg, data) { this.#log('info', msg, data); } log(msg, data) { this.#log('log', msg, data); } warn(msg, data) { this.#log('warn', msg, data); } error(msg, data) { if (msg instanceof Error) { this.#log('error', msg.message, { ...data, stack: msg.stack, }); } else if (typeof msg === 'string') { this.#log('error', msg, data); } else { this.#log('error', 'Unknown error', { ...data, raw: msg }); } } async span(name, fn) { const span = this.startSpan(name); try { return await fn(); } finally { span.end(); } } startSpan(name) { const start = Date.now(); const attributes = {}; const parentSpanId = this.#activeSpanId; const spanId = (0, Generic_1.hexId)(8); this.#activeSpanId = spanId; const obj = { uid: () => spanId, setAttribute: (key, value) => { attributes[key] = value; return obj; }, setAttributes: val => { Object.assign(attributes, val); return obj; }, end: () => { const end = Date.now(); if (this.#spanAwareExporters.length && this.#traceId) { const span = { traceId: this.#traceId, spanId, parentSpanId: parentSpanId ?? undefined, name, start, end, ctx: { ...this.#attributes, ...attributes }, }; /* Set span status */ if ('otel.status_code' in attributes) { span.status = { code: attributes['otel.status_code'] === 'OK' ? 1 : 2 }; if ('otel.status_message' in attributes) span.status.message = attributes['otel.status_message']; } for (let i = 0; i < this.#spanAwareExporters.length; i++) { this.#spanAwareExporters[i].pushSpan(span); } } this.#activeSpanId = parentSpanId ?? null; }, }; return obj; } async flush() { const proms = []; for (let i = 0; i < this.#exporters.length; i++) { proms.push(this.#exporters[i].flush()); } await Promise.all(proms); } #log(level, message, data) { const log = { level, time: new Date(), message, data, ctx: { ...this.#attributes }, }; if (this.#traceId) log.trace_id = this.#traceId; if (this.#activeSpanId) log.span_id = this.#activeSpanId; for (let i = 0; i < this.#exporters.length; i++) this.#exporters[i].pushLog(log); } } exports.Logger = Logger;