@trifrost/core
Version:
Blazingly fast, runtime-agnostic server framework for modern edge and node environments
151 lines (150 loc) • 4.61 kB
JavaScript
"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;