autotel
Version:
Write Once, Observe Anywhere
178 lines (175 loc) • 5.73 kB
JavaScript
let _opentelemetry_api = require("@opentelemetry/api");
//#region src/flatten-attributes.ts
/**
* Convert an unknown value to an OTel-compatible AttributeValue.
* Returns undefined when the value cannot be represented.
*/
function toAttributeValue(value) {
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
if (Array.isArray(value)) {
if (value.every((v) => typeof v === "string") || value.every((v) => typeof v === "number") || value.every((v) => typeof v === "boolean")) return value;
try {
return JSON.stringify(value);
} catch {
return "<serialization-failed>";
}
}
if (value instanceof Date) return value.toISOString();
if (value instanceof Error) return value.message;
}
/**
* Recursively flatten a nested object into dot-notation OTel attributes.
* Includes circular reference protection via WeakSet.
*/
function flattenToAttributes(fields, prefix = "") {
const out = {};
const seen = /* @__PURE__ */ new WeakSet();
function flatten(obj, currentPrefix) {
for (const [key, value] of Object.entries(obj)) {
if (value == null) continue;
const nextKey = currentPrefix ? `${currentPrefix}.${key}` : key;
const attr = toAttributeValue(value);
if (attr !== void 0) {
out[nextKey] = attr;
continue;
}
if (typeof value === "object" && value.constructor === Object) {
if (seen.has(value)) {
out[nextKey] = "<circular-reference>";
continue;
}
seen.add(value);
flatten(value, nextKey);
continue;
}
try {
out[nextKey] = JSON.stringify(value);
} catch {
out[nextKey] = "<serialization-failed>";
}
}
}
flatten(fields, prefix);
return out;
}
//#endregion
//#region src/structured-error.ts
const internalKey = Symbol.for("autotel.error.internal");
function createStructuredError(input) {
const error = new Error(input.message, { cause: input.cause });
error.name = input.name ?? "StructuredError";
if (input.why !== void 0) error.why = input.why;
if (input.fix !== void 0) error.fix = input.fix;
if (input.link !== void 0) error.link = input.link;
if (input.code !== void 0) error.code = input.code;
if (input.status !== void 0) error.status = input.status;
if (input.details !== void 0) error.details = input.details;
if (input.internal !== void 0) Object.defineProperty(error, internalKey, {
value: input.internal,
enumerable: false,
writable: false,
configurable: true
});
Object.defineProperty(error, "internal", {
get() {
return this[internalKey];
},
enumerable: false,
configurable: true
});
error.toString = () => {
const lines = [`${error.name}: ${error.message}`];
if (error.why) lines.push(` Why: ${error.why}`);
if (error.fix) lines.push(` Fix: ${error.fix}`);
if (error.link) lines.push(` Link: ${error.link}`);
if (error.code !== void 0) lines.push(` Code: ${error.code}`);
if (error.status !== void 0) lines.push(` Status: ${error.status}`);
if (error.cause) {
const cause = error.cause;
lines.push(` Caused by: ${cause.name}: ${cause.message}`);
}
return lines.join("\n");
};
return error;
}
function structuredErrorToJSON(error) {
const result = {
name: error.name,
message: error.message
};
if (error.status !== void 0) result.status = error.status;
if (error.why || error.fix || error.link) result.data = {
...error.why && { why: error.why },
...error.fix && { fix: error.fix },
...error.link && { link: error.link }
};
if (error.code !== void 0) result.code = error.code;
if (error.details) result.details = error.details;
if (error.cause instanceof Error) result.cause = {
name: error.cause.name,
message: error.cause.message
};
return result;
}
function getStructuredErrorAttributes(error) {
const structured = error;
const attributes = {
"error.type": error.name || "Error",
"error.message": error.message
};
if (error.stack) attributes["error.stack"] = error.stack;
if (structured.why) attributes["error.why"] = structured.why;
if (structured.fix) attributes["error.fix"] = structured.fix;
if (structured.link) attributes["error.link"] = structured.link;
if (structured.code !== void 0) attributes["error.code"] = typeof structured.code === "string" ? structured.code : String(structured.code);
if (structured.status !== void 0) attributes["error.status"] = structured.status;
if (structured.details) Object.assign(attributes, flattenToAttributes(structured.details, "error.details"));
return attributes;
}
function recordStructuredError(ctx, error) {
const maybeRecordException = ctx.recordException;
if (typeof maybeRecordException === "function") maybeRecordException(error);
ctx.setStatus({
code: _opentelemetry_api.SpanStatusCode.ERROR,
message: error.message
});
ctx.setAttributes(getStructuredErrorAttributes(error));
}
//#endregion
Object.defineProperty(exports, 'createStructuredError', {
enumerable: true,
get: function () {
return createStructuredError;
}
});
Object.defineProperty(exports, 'flattenToAttributes', {
enumerable: true,
get: function () {
return flattenToAttributes;
}
});
Object.defineProperty(exports, 'getStructuredErrorAttributes', {
enumerable: true,
get: function () {
return getStructuredErrorAttributes;
}
});
Object.defineProperty(exports, 'recordStructuredError', {
enumerable: true,
get: function () {
return recordStructuredError;
}
});
Object.defineProperty(exports, 'structuredErrorToJSON', {
enumerable: true,
get: function () {
return structuredErrorToJSON;
}
});
Object.defineProperty(exports, 'toAttributeValue', {
enumerable: true,
get: function () {
return toAttributeValue;
}
});
//# sourceMappingURL=structured-error-CHg7DoIQ.cjs.map