@mks2508/better-logger 0.18.3 → 0.18.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Logger.d.ts +0 -18
- package/dist/Logger.d.ts.map +1 -1
- package/dist/chunks/{LogContext-Dyzs61XG.js → LogContext-BO-f0KXu.js} +20 -14
- package/dist/chunks/{LogContext-Dyzs61XG.js.map → LogContext-BO-f0KXu.js.map} +1 -1
- package/dist/chunks/{LogContext-DZasm_P5.cjs → LogContext-CIKxWPrK.cjs} +20 -14
- package/dist/chunks/{LogContext-DZasm_P5.cjs.map → LogContext-CIKxWPrK.cjs.map} +1 -1
- package/dist/context/LogContext.d.ts.map +1 -1
- package/dist/context.cjs +1 -1
- package/dist/context.js +1 -1
- package/dist/index.cjs +25 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +25 -35
- package/dist/index.js.map +1 -1
- package/dist/transports/SpanRuntime.d.ts +3 -0
- package/dist/transports/SpanRuntime.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunks/LogContext-BaMXleWj.cjs +0 -105
- package/dist/chunks/LogContext-BaMXleWj.cjs.map +0 -1
- package/dist/chunks/LogContext-DjlITOzZ.js +0 -100
- package/dist/chunks/LogContext-DjlITOzZ.js.map +0 -1
- package/dist/chunks/transports-BGfwwakw.js +0 -1237
- package/dist/chunks/transports-BGfwwakw.js.map +0 -1
- package/dist/chunks/transports-yK6CL0Ml.cjs +0 -1278
- package/dist/chunks/transports-yK6CL0Ml.cjs.map +0 -1
|
@@ -21,6 +21,9 @@ import type { Span, SpanAttributes, SpanRecord } from '../types/index.js';
|
|
|
21
21
|
* Span activo en el call stack corriente (dentro de `span(fn)`), si lo hay.
|
|
22
22
|
* Lo consumen la correlación log→span (`TransportRecord.traceId/spanId`) y la
|
|
23
23
|
* resolución de `parentSpanId` en spans hijos.
|
|
24
|
+
*
|
|
25
|
+
* Sin `run()` previo el store estaría vacío igual, así que el guard por
|
|
26
|
+
* flag es observable-identico a consultar el ALS real.
|
|
24
27
|
*/
|
|
25
28
|
export declare function getActiveSpan(): SpanRecord | undefined;
|
|
26
29
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpanRuntime.d.ts","sourceRoot":"","sources":["../../src/transports/SpanRuntime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"SpanRuntime.d.ts","sourceRoot":"","sources":["../../src/transports/SpanRuntime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAiC1E;;;;;;;GAOG;AACH,wBAAgB,aAAa,IAAI,UAAU,GAAG,SAAS,CAGtD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAGvE;AAED,qEAAqE;AACrE,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAyBD,6CAA6C;AAC7C,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAmBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CACtB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,cAAc,GAAG,SAAS,EACtC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GACpC;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,CAkEpC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,cAAc,GAAG,SAAS,EACtC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GACpC,IAAI,CAIN;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,IAAI,UAAU,EAAE,CAOlD;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
|
package/package.json
CHANGED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
//#region src/context/LogContext.ts
|
|
2
|
-
const alsInstance = typeof AsyncLocalStorage !== "undefined" ? new AsyncLocalStorage() : void 0;
|
|
3
|
-
/**
|
|
4
|
-
* Factory que crea una instancia de {@link LogContext}.
|
|
5
|
-
*
|
|
6
|
-
* @param options - Configuración (ver {@link ILogContextOptions})
|
|
7
|
-
* @returns Una instancia de LogContext lista para usar
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* const logContext = createLogContext({
|
|
11
|
-
* childLoggerFactory: (cfg) => new Logger(cfg),
|
|
12
|
-
* initialContext: { service: 'orders-api' },
|
|
13
|
-
* initialResource: { serviceName: 'orders-api', environment: 'prod' }
|
|
14
|
-
* });
|
|
15
|
-
*
|
|
16
|
-
* // Child inmutable con contexto persistente
|
|
17
|
-
* const requestLog = logContext.child({ requestId: 'abc-123' });
|
|
18
|
-
*
|
|
19
|
-
* // Scope transitorio vía ALS (Node; en browser sin ALS es no-op)
|
|
20
|
-
* logContext.withContext({ traceId: 't-9' }, () => {
|
|
21
|
-
* requestLog.info('procesando orden');
|
|
22
|
-
* });
|
|
23
|
-
*/
|
|
24
|
-
function createLogContext(options) {
|
|
25
|
-
let context = { ...options.initialContext };
|
|
26
|
-
let resource = options.initialResource ? { ...options.initialResource } : void 0;
|
|
27
|
-
const als = options.alsInstance ?? alsInstance;
|
|
28
|
-
return {
|
|
29
|
-
getContext() {
|
|
30
|
-
return { ...context };
|
|
31
|
-
},
|
|
32
|
-
withContext(bindings, fn) {
|
|
33
|
-
if (!als) {
|
|
34
|
-
if (fn) return fn();
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (!fn) return void 0;
|
|
38
|
-
const merged = {
|
|
39
|
-
...context,
|
|
40
|
-
...bindings
|
|
41
|
-
};
|
|
42
|
-
return als.run(merged, fn);
|
|
43
|
-
},
|
|
44
|
-
async withContextAsync(bindings, fn) {
|
|
45
|
-
if (!als) return fn();
|
|
46
|
-
const merged = {
|
|
47
|
-
...context,
|
|
48
|
-
...bindings
|
|
49
|
-
};
|
|
50
|
-
return als.run(merged, fn);
|
|
51
|
-
},
|
|
52
|
-
clearContext() {
|
|
53
|
-
context = {};
|
|
54
|
-
return this;
|
|
55
|
-
},
|
|
56
|
-
setResource(res) {
|
|
57
|
-
resource = {
|
|
58
|
-
...resource,
|
|
59
|
-
...res
|
|
60
|
-
};
|
|
61
|
-
return this;
|
|
62
|
-
},
|
|
63
|
-
child(_extra) {
|
|
64
|
-
const snapshot = this._getBaseContextRecord();
|
|
65
|
-
const childLogger = options.childLoggerFactory({});
|
|
66
|
-
childLogger["__parentSnapshot"] = snapshot;
|
|
67
|
-
return childLogger;
|
|
68
|
-
},
|
|
69
|
-
_getContextRecord() {
|
|
70
|
-
const base = this._getBaseContextRecord();
|
|
71
|
-
const alsContext = als?.getStore();
|
|
72
|
-
if (alsContext && Object.keys(alsContext).length > 0) return {
|
|
73
|
-
...base,
|
|
74
|
-
...alsContext
|
|
75
|
-
};
|
|
76
|
-
return base;
|
|
77
|
-
},
|
|
78
|
-
_getBaseContextRecord() {
|
|
79
|
-
let base = {};
|
|
80
|
-
const parentRecord = options.getParentContextRecord?.() ?? null;
|
|
81
|
-
if (parentRecord && Object.keys(parentRecord).length > 0) base = parentRecord;
|
|
82
|
-
else if (Object.keys(context).length > 0) base = context;
|
|
83
|
-
if (Object.keys(context).length > 0) base = {
|
|
84
|
-
...base,
|
|
85
|
-
...context
|
|
86
|
-
};
|
|
87
|
-
return base;
|
|
88
|
-
},
|
|
89
|
-
_getResource() {
|
|
90
|
-
return resource;
|
|
91
|
-
},
|
|
92
|
-
_getAlsStore() {
|
|
93
|
-
return als?.getStore();
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
//#endregion
|
|
98
|
-
Object.defineProperty(exports, "createLogContext", {
|
|
99
|
-
enumerable: true,
|
|
100
|
-
get: function() {
|
|
101
|
-
return createLogContext;
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
//# sourceMappingURL=LogContext-BaMXleWj.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LogContext-BaMXleWj.cjs","names":[],"sources":["../../src/context/LogContext.ts"],"sourcesContent":["/**\n * @fileoverview Bridge de LogContext — gestión de MDC (Mapped Diagnostic Context).\n *\n * Encapsula el contexto estructurado por logger, la creación de child loggers y\n * el merge de resource OTel en cada record emitido.\n *\n * Modelo de API:\n * - `withContext(bindings, fn?)` — si se pasa `fn`, lo ejecuta dentro de un\n * scope de AsyncLocalStorage mergeando `bindings`. Sin `fn`: no-op (shim de\n * backwards compat para la vieja forma de setter).\n * - `withContextAsync(bindings, fn)` — variante async callback.\n * - `child(bindings)` — inmutable (patrón canónico de MDC).\n * - Feature-detect de AsyncLocalStorage; en browser sin ALS es no-op.\n */\n\nimport type { ILogResourceRef } from '../types/index.js';\nimport type { LoggerConfig } from '../types/index.js';\n\n/**\n * Snapshot del contexto bound. Lo retorna {@link LogContext.getContext}.\n *\n * Es `Readonly` para marcar contractually que el objeto devuelto es una shallow\n * copy: mutarlo no afecta a los records que emitan futuras llamadas de log.\n */\nexport type ContextSnapshot = Readonly<Record<string, unknown>>;\n\n/**\n * Tipo de la factory function para crear instancias child de Logger.\n *\n * Se inyecta en LogContext para que `child()` pueda instanciar nuevos loggers\n * sin introducir un import circular entre `Logger.ts` y `LogContext.ts`.\n * Retorna `unknown` — la clase Logger concreta la maneja el caller, y la\n * instancia devuelta tiene su campo `context` escrito por LogContext tras la\n * creación.\n */\nexport type ChildLoggerFactory = (config: Partial<LoggerConfig>) => unknown;\n\n/**\n * Shape mínima de una instancia de Logger que LogContext necesita ver.\n *\n * Evita dependencias circulares entre LogContext y Logger. El campo\n * `_parentContextRecord` lo setea el Logger padre después de que\n * `LogContext.child()` retorna, estableciendo la cadena de contextos.\n */\nexport interface ChildLoggerShape {\n _parentContextRecord?: Record<string, unknown>;\n /** Campo legacy — ya no es la fuente canónica del contexto. */\n context?: Record<string, unknown>;\n}\n\n/**\n * Options que se pasan a {@link createLogContext}.\n */\nexport interface ILogContextOptions {\n /** Pares key-value iniciales del contexto. */\n initialContext?: Record<string, unknown>;\n /** Factory para crear instancias child de logger. */\n childLoggerFactory: ChildLoggerFactory;\n /** Resource OTel inicial a mergear en cada record emitido. */\n initialResource?: Partial<ILogResourceRef>;\n /**\n * Retorna el record de contexto mergeado del logger padre en el momento\n * de creación del child. Lo usa `_getContextRecord()` para construir la\n * cadena de contextos.\n * @internal\n */\n getParentContextRecord?: () => Record<string, unknown>;\n /**\n * Instancia de AsyncLocalStorage a usar para el scoping de `withContext`.\n * @internal\n */\n alsInstance?: ALS;\n}\n\n/**\n * Contrato que retorna {@link createLogContext}.\n *\n * Fachada de MDC (Mapped Diagnostic Context) por logger. Combina tres fuentes\n * de contexto:\n * - **base inmutable** vía `child()` (snapshot capturado al crear el child),\n * - **scope transitorio** vía `withContext()` / `withContextAsync()` sobre\n * AsyncLocalStorage,\n * - **resource OTel** mergeado en cada record.\n *\n * En entornos browser sin `AsyncLocalStorage`, las variantes `withContext*`\n * degradan a no-op: ejecutan `fn` sin scoping (o lo skipan si no hay `fn`).\n * `child()` sigue operativo en browser porque no depende de ALS.\n */\nexport interface LogContext {\n /**\n * Snapshot actual del contexto bound.\n *\n * @returns Copia inmutable (shallow) del contexto; mutarla no afecta a los\n * records que emitan futuras llamadas de log.\n *\n * @example\n * const ctx = logContext.getContext();\n * console.log(ctx.requestId); // 'abc-123'\n */\n getContext(): ContextSnapshot;\n\n /**\n * Ejecuta `fn` dentro de un scope de AsyncLocalStorage donde `bindings`\n * se mergean al contexto para todas las llamadas de log dentro de `fn`.\n *\n * Si no se pasa `fn` (la vieja forma de setter), es no-op por backwards\n * compatibility. Para binding persistente prefiere `child()`; para\n * callbacks async usa `withContextAsync()`.\n *\n * **Browser fallback**: sin ALS, ejecuta `fn` directamente sin scoping\n * (los bindings NO se mergean). Si tampoco hay `fn`, retorna `undefined`.\n *\n * @param bindings - Pares key-value a attachar durante la ejecución de `fn`\n * @param fn - Función sincrónica opcional a ejecutar bajo el scope ALS\n * @returns El valor de retorno de `fn`, o `undefined` si no se pasa `fn`\n *\n * @example\n * logContext.withContext({ requestId: 'abc-123' }, () => {\n * logger.info('procesando'); // el record lleva requestId=abc-123\n * });\n * // fuera de fn: requestId ya no está presente en próximos logs\n *\n * @see {@link LogContext.withContextAsync} para callbacks async\n * @see {@link LogContext.child} para binding persistente inmutable (sin ALS)\n */\n withContext<R>(bindings: Record<string, unknown>, fn?: () => R): R | undefined;\n\n /**\n * Variante async de {@link withContext}. Ejecuta `fn` dentro de un scope\n * de AsyncLocalStorage para que los bindings queden disponibles a todas las\n * llamadas de log async dentro de `fn` (incluso tras `await`).\n *\n * **Browser fallback**: sin ALS, ejecuta `fn` directamente sin scoping.\n *\n * @param bindings - Pares key-value a attachar durante la ejecución de `fn`\n * @param fn - Función async a ejecutar bajo el scope ALS\n * @returns El Promise retornado por `fn`\n *\n * @example\n * await logContext.withContextAsync({ traceId }, async () => {\n * const user = await fetchUser();\n * logger.info('user cargado', { id: user.id });\n * // el record lleva el traceId aunque el log ocurra tras un await\n * });\n */\n withContextAsync<R>(bindings: Record<string, unknown>, fn: () => Promise<R>): Promise<R>;\n\n /**\n * Droppea todas las keys del contexto bound. Tras esta llamada, los\n * records emitidos ya no llevan `attributes` hasta que\n * {@link withContext} o {@link child} restablezcan uno.\n *\n * @returns La misma instancia de LogContext, ahora sin contexto\n *\n * @example\n * logContext.clearContext();\n * logger.info('limpio'); // sin attributes\n */\n clearContext(): this;\n\n /**\n * Actualiza el resource OTel por defecto (service.name, version,\n * deployment.environment, ...).\n *\n * Se persiste en el campo `resource` de cada record emitido, salvo que el\n * propio record lo overridee.\n *\n * @param resource - Resource OTel parcial a mergear con el actual\n * @returns La misma instancia de LogContext, para encadenar calls\n *\n * @example\n * logContext.setResource({ serviceName: 'api-gateway', environment: 'prod' });\n */\n setResource(resource: Partial<ILogResourceRef>): this;\n\n /**\n * Devuelve una copia inmutable de este logger con el contexto extra bound.\n *\n * Las llamadas futuras sobre el child emiten con el contexto mergeado, sin\n * mutar al padre — patrón canónico de MDC.\n *\n * A diferencia de {@link withContext}, **no involucra AsyncLocalStorage**:\n * el binding es persistente y queda capturado en el snapshot del child al\n * crearse. Por eso `child()` es operativo también en browser sin ALS.\n *\n * Los bindings transitorios de ALS activos en el momento de `child()` NO\n * se bakean en el child — solo se captura el contexto base. ALS se aplica\n * fresco en cada dispatch vía `_getContextRecord()`.\n *\n * @param extra - Pares key-value a attachar (requestId, userId, ...)\n * @returns Un nuevo Logger con el contexto mergeado\n *\n * @example\n * const requestLog = logContext.child({ requestId: 'abc-123' });\n * requestLog.info('inicio'); // siempre lleva requestId=abc-123\n * requestLog.info('fin');\n * // el logger padre no se ve afectado por estos bindings\n *\n * @see {@link LogContext.withContext} para scoping transitorio (ALS)\n */\n child(extra: Record<string, unknown>): ChildLoggerShape;\n\n /**\n * Record de contexto interno. Expuesto para el ensamblado de TransportRecord\n * (base + overlay ALS si hay store activo).\n * @internal\n */\n _getContextRecord(): Record<string, unknown>;\n /**\n * Retorna el contexto base SIN el overlay de ALS.\n *\n * Lo usa `Logger.child()` para capturar el snapshot del contexto padre al\n * crear un child logger, garantizando que el binding ALS transitorio no\n * se bakeé en el child.\n * @internal\n */\n _getBaseContextRecord(): Record<string, unknown>;\n /**\n * Record de resource interno. Expuesto para el ensamblado de TransportRecord.\n * @internal\n */\n _getResource(): Partial<ILogResourceRef> | undefined;\n /**\n * Retorna el store actual de AsyncLocalStorage, si ALS está activo en el\n * call stack corriente.\n * @internal\n */\n _getAlsStore(): Record<string, unknown> | undefined;\n}\n\n// AsyncLocalStorage type (Node 14+, undefined in browser)\ntype ALS = {\n run<R>(store: Record<string, unknown>, fn: () => R): R;\n getStore(): Record<string, unknown> | undefined;\n};\ndeclare const AsyncLocalStorage: new () => ALS;\n\n// Feature-detect AsyncLocalStorage\nconst hasALS = typeof AsyncLocalStorage !== 'undefined';\nconst alsInstance: ALS | undefined = hasALS ? new AsyncLocalStorage() : undefined;\n\n/**\n * Factory que crea una instancia de {@link LogContext}.\n *\n * @param options - Configuración (ver {@link ILogContextOptions})\n * @returns Una instancia de LogContext lista para usar\n *\n * @example\n * const logContext = createLogContext({\n * childLoggerFactory: (cfg) => new Logger(cfg),\n * initialContext: { service: 'orders-api' },\n * initialResource: { serviceName: 'orders-api', environment: 'prod' }\n * });\n *\n * // Child inmutable con contexto persistente\n * const requestLog = logContext.child({ requestId: 'abc-123' });\n *\n * // Scope transitorio vía ALS (Node; en browser sin ALS es no-op)\n * logContext.withContext({ traceId: 't-9' }, () => {\n * requestLog.info('procesando orden');\n * });\n */\nexport function createLogContext(options: ILogContextOptions): LogContext {\n let context: Record<string, unknown> = { ...options.initialContext };\n let resource: Partial<ILogResourceRef> | undefined = options.initialResource\n ? { ...options.initialResource }\n : undefined;\n\n // Use provided ALS instance or fall back to module-level (browser fallback)\n const als = options.alsInstance ?? alsInstance;\n\n return {\n getContext(): ContextSnapshot {\n return { ...context };\n },\n\n withContext<R>(bindings: Record<string, unknown>, fn?: () => R): R | undefined {\n // No-op without AsyncLocalStorage (browser) — warn once\n if (!als) {\n if (fn) return fn();\n return undefined;\n }\n // No fn: backwards-compat no-op setter shim\n if (!fn) return undefined;\n // Run fn within AsyncLocalStorage scope\n const merged = { ...context, ...bindings };\n return als.run(merged, fn);\n },\n\n async withContextAsync<R>(bindings: Record<string, unknown>, fn: () => Promise<R>): Promise<R> {\n if (!als) return fn();\n const merged = { ...context, ...bindings };\n return als.run(merged, fn);\n },\n\n clearContext(): typeof this {\n context = {};\n return this;\n },\n\n setResource(res: Partial<ILogResourceRef>): typeof this {\n resource = { ...resource, ...res };\n return this;\n },\n\n child(_extra: Record<string, unknown>): ChildLoggerShape {\n // Creates the child logger via factory. Captures the current\n // _getBaseContextRecord() snapshot (parent context WITHOUT ALS) at\n // child-creation time. ALS is transient and should not be baked into\n // the child's _parentContextRecord — it is applied fresh at dispatch time.\n // Note: _extra (bindings) are stored by Logger.child() as _bindings.\n const snapshot = this._getBaseContextRecord();\n const childLogger = options.childLoggerFactory({}) as ChildLoggerShape;\n // Store on childLogger for Logger.child() to pick up\n (childLogger as unknown as Record<string, unknown>)['__parentSnapshot'] = snapshot;\n return childLogger;\n },\n\n _getContextRecord(): Record<string, unknown> {\n // Returns the full merged context for dispatch purposes.\n // Base (parent snapshot + own context) plus ALS overlay if active.\n const base = this._getBaseContextRecord();\n const alsContext = als?.getStore();\n if (alsContext && Object.keys(alsContext).length > 0) {\n return { ...base, ...alsContext };\n }\n return base;\n },\n\n _getBaseContextRecord(): Record<string, unknown> {\n // Returns parent context chain + own context (NO ALS overlay).\n // ALS is applied by _getContextRecord() as a live overlay.\n let base: Record<string, unknown> = {};\n const parentRecord = options.getParentContextRecord?.() ?? null;\n if (parentRecord && Object.keys(parentRecord).length > 0) {\n base = parentRecord;\n } else if (Object.keys(context).length > 0) {\n base = context;\n }\n if (Object.keys(context).length > 0) {\n base = { ...base, ...context };\n }\n return base;\n },\n\n _getResource(): Partial<ILogResourceRef> | undefined {\n return resource;\n },\n\n _getAlsStore(): Record<string, unknown> | undefined {\n return als?.getStore();\n }\n };\n}\n"],"mappings":";AA+OA,MAAM,cADS,OAAO,sBAAsB,cACE,IAAI,kBAAkB,IAAI,KAAA;;;;;;;;;;;;;;;;;;;;;;AAuBxE,SAAgB,iBAAiB,SAAyC;CACtE,IAAI,UAAmC,EAAE,GAAG,QAAQ,eAAe;CACnE,IAAI,WAAiD,QAAQ,kBACvD,EAAE,GAAG,QAAQ,gBAAgB,IAC7B,KAAA;CAGN,MAAM,MAAM,QAAQ,eAAe;CAEnC,OAAO;EACH,aAA8B;GAC1B,OAAO,EAAE,GAAG,QAAQ;EACxB;EAEA,YAAe,UAAmC,IAA6B;GAE3E,IAAI,CAAC,KAAK;IACN,IAAI,IAAI,OAAO,GAAG;IAClB;GACJ;GAEA,IAAI,CAAC,IAAI,OAAO,KAAA;GAEhB,MAAM,SAAS;IAAE,GAAG;IAAS,GAAG;GAAS;GACzC,OAAO,IAAI,IAAI,QAAQ,EAAE;EAC7B;EAEA,MAAM,iBAAoB,UAAmC,IAAkC;GAC3F,IAAI,CAAC,KAAK,OAAO,GAAG;GACpB,MAAM,SAAS;IAAE,GAAG;IAAS,GAAG;GAAS;GACzC,OAAO,IAAI,IAAI,QAAQ,EAAE;EAC7B;EAEA,eAA4B;GACxB,UAAU,CAAC;GACX,OAAO;EACX;EAEA,YAAY,KAA4C;GACpD,WAAW;IAAE,GAAG;IAAU,GAAG;GAAI;GACjC,OAAO;EACX;EAEA,MAAM,QAAmD;GAMrD,MAAM,WAAW,KAAK,sBAAsB;GAC5C,MAAM,cAAc,QAAQ,mBAAmB,CAAC,CAAC;GAEjD,YAAoD,sBAAsB;GAC1E,OAAO;EACX;EAEA,oBAA6C;GAGzC,MAAM,OAAO,KAAK,sBAAsB;GACxC,MAAM,aAAa,KAAK,SAAS;GACjC,IAAI,cAAc,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,GAC/C,OAAO;IAAE,GAAG;IAAM,GAAG;GAAW;GAEpC,OAAO;EACX;EAEA,wBAAiD;GAG7C,IAAI,OAAgC,CAAC;GACrC,MAAM,eAAe,QAAQ,yBAAyB,KAAK;GAC3D,IAAI,gBAAgB,OAAO,KAAK,YAAY,CAAC,CAAC,SAAS,GACnD,OAAO;QACJ,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,GACrC,OAAO;GAEX,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,GAC9B,OAAO;IAAE,GAAG;IAAM,GAAG;GAAQ;GAEjC,OAAO;EACX;EAEA,eAAqD;GACjD,OAAO;EACX;EAEA,eAAoD;GAChD,OAAO,KAAK,SAAS;EACzB;CACJ;AACJ"}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
//#region src/context/LogContext.ts
|
|
2
|
-
const alsInstance = typeof AsyncLocalStorage !== "undefined" ? new AsyncLocalStorage() : void 0;
|
|
3
|
-
/**
|
|
4
|
-
* Factory que crea una instancia de {@link LogContext}.
|
|
5
|
-
*
|
|
6
|
-
* @param options - Configuración (ver {@link ILogContextOptions})
|
|
7
|
-
* @returns Una instancia de LogContext lista para usar
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* const logContext = createLogContext({
|
|
11
|
-
* childLoggerFactory: (cfg) => new Logger(cfg),
|
|
12
|
-
* initialContext: { service: 'orders-api' },
|
|
13
|
-
* initialResource: { serviceName: 'orders-api', environment: 'prod' }
|
|
14
|
-
* });
|
|
15
|
-
*
|
|
16
|
-
* // Child inmutable con contexto persistente
|
|
17
|
-
* const requestLog = logContext.child({ requestId: 'abc-123' });
|
|
18
|
-
*
|
|
19
|
-
* // Scope transitorio vía ALS (Node; en browser sin ALS es no-op)
|
|
20
|
-
* logContext.withContext({ traceId: 't-9' }, () => {
|
|
21
|
-
* requestLog.info('procesando orden');
|
|
22
|
-
* });
|
|
23
|
-
*/
|
|
24
|
-
function createLogContext(options) {
|
|
25
|
-
let context = { ...options.initialContext };
|
|
26
|
-
let resource = options.initialResource ? { ...options.initialResource } : void 0;
|
|
27
|
-
const als = options.alsInstance ?? alsInstance;
|
|
28
|
-
return {
|
|
29
|
-
getContext() {
|
|
30
|
-
return { ...context };
|
|
31
|
-
},
|
|
32
|
-
withContext(bindings, fn) {
|
|
33
|
-
if (!als) {
|
|
34
|
-
if (fn) return fn();
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (!fn) return void 0;
|
|
38
|
-
const merged = {
|
|
39
|
-
...context,
|
|
40
|
-
...bindings
|
|
41
|
-
};
|
|
42
|
-
return als.run(merged, fn);
|
|
43
|
-
},
|
|
44
|
-
async withContextAsync(bindings, fn) {
|
|
45
|
-
if (!als) return fn();
|
|
46
|
-
const merged = {
|
|
47
|
-
...context,
|
|
48
|
-
...bindings
|
|
49
|
-
};
|
|
50
|
-
return als.run(merged, fn);
|
|
51
|
-
},
|
|
52
|
-
clearContext() {
|
|
53
|
-
context = {};
|
|
54
|
-
return this;
|
|
55
|
-
},
|
|
56
|
-
setResource(res) {
|
|
57
|
-
resource = {
|
|
58
|
-
...resource,
|
|
59
|
-
...res
|
|
60
|
-
};
|
|
61
|
-
return this;
|
|
62
|
-
},
|
|
63
|
-
child(_extra) {
|
|
64
|
-
const snapshot = this._getBaseContextRecord();
|
|
65
|
-
const childLogger = options.childLoggerFactory({});
|
|
66
|
-
childLogger["__parentSnapshot"] = snapshot;
|
|
67
|
-
return childLogger;
|
|
68
|
-
},
|
|
69
|
-
_getContextRecord() {
|
|
70
|
-
const base = this._getBaseContextRecord();
|
|
71
|
-
const alsContext = als?.getStore();
|
|
72
|
-
if (alsContext && Object.keys(alsContext).length > 0) return {
|
|
73
|
-
...base,
|
|
74
|
-
...alsContext
|
|
75
|
-
};
|
|
76
|
-
return base;
|
|
77
|
-
},
|
|
78
|
-
_getBaseContextRecord() {
|
|
79
|
-
let base = {};
|
|
80
|
-
const parentRecord = options.getParentContextRecord?.() ?? null;
|
|
81
|
-
if (parentRecord && Object.keys(parentRecord).length > 0) base = parentRecord;
|
|
82
|
-
else if (Object.keys(context).length > 0) base = context;
|
|
83
|
-
if (Object.keys(context).length > 0) base = {
|
|
84
|
-
...base,
|
|
85
|
-
...context
|
|
86
|
-
};
|
|
87
|
-
return base;
|
|
88
|
-
},
|
|
89
|
-
_getResource() {
|
|
90
|
-
return resource;
|
|
91
|
-
},
|
|
92
|
-
_getAlsStore() {
|
|
93
|
-
return als?.getStore();
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
//#endregion
|
|
98
|
-
export { createLogContext as t };
|
|
99
|
-
|
|
100
|
-
//# sourceMappingURL=LogContext-DjlITOzZ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LogContext-DjlITOzZ.js","names":[],"sources":["../../src/context/LogContext.ts"],"sourcesContent":["/**\n * @fileoverview Bridge de LogContext — gestión de MDC (Mapped Diagnostic Context).\n *\n * Encapsula el contexto estructurado por logger, la creación de child loggers y\n * el merge de resource OTel en cada record emitido.\n *\n * Modelo de API:\n * - `withContext(bindings, fn?)` — si se pasa `fn`, lo ejecuta dentro de un\n * scope de AsyncLocalStorage mergeando `bindings`. Sin `fn`: no-op (shim de\n * backwards compat para la vieja forma de setter).\n * - `withContextAsync(bindings, fn)` — variante async callback.\n * - `child(bindings)` — inmutable (patrón canónico de MDC).\n * - Feature-detect de AsyncLocalStorage; en browser sin ALS es no-op.\n */\n\nimport type { ILogResourceRef } from '../types/index.js';\nimport type { LoggerConfig } from '../types/index.js';\n\n/**\n * Snapshot del contexto bound. Lo retorna {@link LogContext.getContext}.\n *\n * Es `Readonly` para marcar contractually que el objeto devuelto es una shallow\n * copy: mutarlo no afecta a los records que emitan futuras llamadas de log.\n */\nexport type ContextSnapshot = Readonly<Record<string, unknown>>;\n\n/**\n * Tipo de la factory function para crear instancias child de Logger.\n *\n * Se inyecta en LogContext para que `child()` pueda instanciar nuevos loggers\n * sin introducir un import circular entre `Logger.ts` y `LogContext.ts`.\n * Retorna `unknown` — la clase Logger concreta la maneja el caller, y la\n * instancia devuelta tiene su campo `context` escrito por LogContext tras la\n * creación.\n */\nexport type ChildLoggerFactory = (config: Partial<LoggerConfig>) => unknown;\n\n/**\n * Shape mínima de una instancia de Logger que LogContext necesita ver.\n *\n * Evita dependencias circulares entre LogContext y Logger. El campo\n * `_parentContextRecord` lo setea el Logger padre después de que\n * `LogContext.child()` retorna, estableciendo la cadena de contextos.\n */\nexport interface ChildLoggerShape {\n _parentContextRecord?: Record<string, unknown>;\n /** Campo legacy — ya no es la fuente canónica del contexto. */\n context?: Record<string, unknown>;\n}\n\n/**\n * Options que se pasan a {@link createLogContext}.\n */\nexport interface ILogContextOptions {\n /** Pares key-value iniciales del contexto. */\n initialContext?: Record<string, unknown>;\n /** Factory para crear instancias child de logger. */\n childLoggerFactory: ChildLoggerFactory;\n /** Resource OTel inicial a mergear en cada record emitido. */\n initialResource?: Partial<ILogResourceRef>;\n /**\n * Retorna el record de contexto mergeado del logger padre en el momento\n * de creación del child. Lo usa `_getContextRecord()` para construir la\n * cadena de contextos.\n * @internal\n */\n getParentContextRecord?: () => Record<string, unknown>;\n /**\n * Instancia de AsyncLocalStorage a usar para el scoping de `withContext`.\n * @internal\n */\n alsInstance?: ALS;\n}\n\n/**\n * Contrato que retorna {@link createLogContext}.\n *\n * Fachada de MDC (Mapped Diagnostic Context) por logger. Combina tres fuentes\n * de contexto:\n * - **base inmutable** vía `child()` (snapshot capturado al crear el child),\n * - **scope transitorio** vía `withContext()` / `withContextAsync()` sobre\n * AsyncLocalStorage,\n * - **resource OTel** mergeado en cada record.\n *\n * En entornos browser sin `AsyncLocalStorage`, las variantes `withContext*`\n * degradan a no-op: ejecutan `fn` sin scoping (o lo skipan si no hay `fn`).\n * `child()` sigue operativo en browser porque no depende de ALS.\n */\nexport interface LogContext {\n /**\n * Snapshot actual del contexto bound.\n *\n * @returns Copia inmutable (shallow) del contexto; mutarla no afecta a los\n * records que emitan futuras llamadas de log.\n *\n * @example\n * const ctx = logContext.getContext();\n * console.log(ctx.requestId); // 'abc-123'\n */\n getContext(): ContextSnapshot;\n\n /**\n * Ejecuta `fn` dentro de un scope de AsyncLocalStorage donde `bindings`\n * se mergean al contexto para todas las llamadas de log dentro de `fn`.\n *\n * Si no se pasa `fn` (la vieja forma de setter), es no-op por backwards\n * compatibility. Para binding persistente prefiere `child()`; para\n * callbacks async usa `withContextAsync()`.\n *\n * **Browser fallback**: sin ALS, ejecuta `fn` directamente sin scoping\n * (los bindings NO se mergean). Si tampoco hay `fn`, retorna `undefined`.\n *\n * @param bindings - Pares key-value a attachar durante la ejecución de `fn`\n * @param fn - Función sincrónica opcional a ejecutar bajo el scope ALS\n * @returns El valor de retorno de `fn`, o `undefined` si no se pasa `fn`\n *\n * @example\n * logContext.withContext({ requestId: 'abc-123' }, () => {\n * logger.info('procesando'); // el record lleva requestId=abc-123\n * });\n * // fuera de fn: requestId ya no está presente en próximos logs\n *\n * @see {@link LogContext.withContextAsync} para callbacks async\n * @see {@link LogContext.child} para binding persistente inmutable (sin ALS)\n */\n withContext<R>(bindings: Record<string, unknown>, fn?: () => R): R | undefined;\n\n /**\n * Variante async de {@link withContext}. Ejecuta `fn` dentro de un scope\n * de AsyncLocalStorage para que los bindings queden disponibles a todas las\n * llamadas de log async dentro de `fn` (incluso tras `await`).\n *\n * **Browser fallback**: sin ALS, ejecuta `fn` directamente sin scoping.\n *\n * @param bindings - Pares key-value a attachar durante la ejecución de `fn`\n * @param fn - Función async a ejecutar bajo el scope ALS\n * @returns El Promise retornado por `fn`\n *\n * @example\n * await logContext.withContextAsync({ traceId }, async () => {\n * const user = await fetchUser();\n * logger.info('user cargado', { id: user.id });\n * // el record lleva el traceId aunque el log ocurra tras un await\n * });\n */\n withContextAsync<R>(bindings: Record<string, unknown>, fn: () => Promise<R>): Promise<R>;\n\n /**\n * Droppea todas las keys del contexto bound. Tras esta llamada, los\n * records emitidos ya no llevan `attributes` hasta que\n * {@link withContext} o {@link child} restablezcan uno.\n *\n * @returns La misma instancia de LogContext, ahora sin contexto\n *\n * @example\n * logContext.clearContext();\n * logger.info('limpio'); // sin attributes\n */\n clearContext(): this;\n\n /**\n * Actualiza el resource OTel por defecto (service.name, version,\n * deployment.environment, ...).\n *\n * Se persiste en el campo `resource` de cada record emitido, salvo que el\n * propio record lo overridee.\n *\n * @param resource - Resource OTel parcial a mergear con el actual\n * @returns La misma instancia de LogContext, para encadenar calls\n *\n * @example\n * logContext.setResource({ serviceName: 'api-gateway', environment: 'prod' });\n */\n setResource(resource: Partial<ILogResourceRef>): this;\n\n /**\n * Devuelve una copia inmutable de este logger con el contexto extra bound.\n *\n * Las llamadas futuras sobre el child emiten con el contexto mergeado, sin\n * mutar al padre — patrón canónico de MDC.\n *\n * A diferencia de {@link withContext}, **no involucra AsyncLocalStorage**:\n * el binding es persistente y queda capturado en el snapshot del child al\n * crearse. Por eso `child()` es operativo también en browser sin ALS.\n *\n * Los bindings transitorios de ALS activos en el momento de `child()` NO\n * se bakean en el child — solo se captura el contexto base. ALS se aplica\n * fresco en cada dispatch vía `_getContextRecord()`.\n *\n * @param extra - Pares key-value a attachar (requestId, userId, ...)\n * @returns Un nuevo Logger con el contexto mergeado\n *\n * @example\n * const requestLog = logContext.child({ requestId: 'abc-123' });\n * requestLog.info('inicio'); // siempre lleva requestId=abc-123\n * requestLog.info('fin');\n * // el logger padre no se ve afectado por estos bindings\n *\n * @see {@link LogContext.withContext} para scoping transitorio (ALS)\n */\n child(extra: Record<string, unknown>): ChildLoggerShape;\n\n /**\n * Record de contexto interno. Expuesto para el ensamblado de TransportRecord\n * (base + overlay ALS si hay store activo).\n * @internal\n */\n _getContextRecord(): Record<string, unknown>;\n /**\n * Retorna el contexto base SIN el overlay de ALS.\n *\n * Lo usa `Logger.child()` para capturar el snapshot del contexto padre al\n * crear un child logger, garantizando que el binding ALS transitorio no\n * se bakeé en el child.\n * @internal\n */\n _getBaseContextRecord(): Record<string, unknown>;\n /**\n * Record de resource interno. Expuesto para el ensamblado de TransportRecord.\n * @internal\n */\n _getResource(): Partial<ILogResourceRef> | undefined;\n /**\n * Retorna el store actual de AsyncLocalStorage, si ALS está activo en el\n * call stack corriente.\n * @internal\n */\n _getAlsStore(): Record<string, unknown> | undefined;\n}\n\n// AsyncLocalStorage type (Node 14+, undefined in browser)\ntype ALS = {\n run<R>(store: Record<string, unknown>, fn: () => R): R;\n getStore(): Record<string, unknown> | undefined;\n};\ndeclare const AsyncLocalStorage: new () => ALS;\n\n// Feature-detect AsyncLocalStorage\nconst hasALS = typeof AsyncLocalStorage !== 'undefined';\nconst alsInstance: ALS | undefined = hasALS ? new AsyncLocalStorage() : undefined;\n\n/**\n * Factory que crea una instancia de {@link LogContext}.\n *\n * @param options - Configuración (ver {@link ILogContextOptions})\n * @returns Una instancia de LogContext lista para usar\n *\n * @example\n * const logContext = createLogContext({\n * childLoggerFactory: (cfg) => new Logger(cfg),\n * initialContext: { service: 'orders-api' },\n * initialResource: { serviceName: 'orders-api', environment: 'prod' }\n * });\n *\n * // Child inmutable con contexto persistente\n * const requestLog = logContext.child({ requestId: 'abc-123' });\n *\n * // Scope transitorio vía ALS (Node; en browser sin ALS es no-op)\n * logContext.withContext({ traceId: 't-9' }, () => {\n * requestLog.info('procesando orden');\n * });\n */\nexport function createLogContext(options: ILogContextOptions): LogContext {\n let context: Record<string, unknown> = { ...options.initialContext };\n let resource: Partial<ILogResourceRef> | undefined = options.initialResource\n ? { ...options.initialResource }\n : undefined;\n\n // Use provided ALS instance or fall back to module-level (browser fallback)\n const als = options.alsInstance ?? alsInstance;\n\n return {\n getContext(): ContextSnapshot {\n return { ...context };\n },\n\n withContext<R>(bindings: Record<string, unknown>, fn?: () => R): R | undefined {\n // No-op without AsyncLocalStorage (browser) — warn once\n if (!als) {\n if (fn) return fn();\n return undefined;\n }\n // No fn: backwards-compat no-op setter shim\n if (!fn) return undefined;\n // Run fn within AsyncLocalStorage scope\n const merged = { ...context, ...bindings };\n return als.run(merged, fn);\n },\n\n async withContextAsync<R>(bindings: Record<string, unknown>, fn: () => Promise<R>): Promise<R> {\n if (!als) return fn();\n const merged = { ...context, ...bindings };\n return als.run(merged, fn);\n },\n\n clearContext(): typeof this {\n context = {};\n return this;\n },\n\n setResource(res: Partial<ILogResourceRef>): typeof this {\n resource = { ...resource, ...res };\n return this;\n },\n\n child(_extra: Record<string, unknown>): ChildLoggerShape {\n // Creates the child logger via factory. Captures the current\n // _getBaseContextRecord() snapshot (parent context WITHOUT ALS) at\n // child-creation time. ALS is transient and should not be baked into\n // the child's _parentContextRecord — it is applied fresh at dispatch time.\n // Note: _extra (bindings) are stored by Logger.child() as _bindings.\n const snapshot = this._getBaseContextRecord();\n const childLogger = options.childLoggerFactory({}) as ChildLoggerShape;\n // Store on childLogger for Logger.child() to pick up\n (childLogger as unknown as Record<string, unknown>)['__parentSnapshot'] = snapshot;\n return childLogger;\n },\n\n _getContextRecord(): Record<string, unknown> {\n // Returns the full merged context for dispatch purposes.\n // Base (parent snapshot + own context) plus ALS overlay if active.\n const base = this._getBaseContextRecord();\n const alsContext = als?.getStore();\n if (alsContext && Object.keys(alsContext).length > 0) {\n return { ...base, ...alsContext };\n }\n return base;\n },\n\n _getBaseContextRecord(): Record<string, unknown> {\n // Returns parent context chain + own context (NO ALS overlay).\n // ALS is applied by _getContextRecord() as a live overlay.\n let base: Record<string, unknown> = {};\n const parentRecord = options.getParentContextRecord?.() ?? null;\n if (parentRecord && Object.keys(parentRecord).length > 0) {\n base = parentRecord;\n } else if (Object.keys(context).length > 0) {\n base = context;\n }\n if (Object.keys(context).length > 0) {\n base = { ...base, ...context };\n }\n return base;\n },\n\n _getResource(): Partial<ILogResourceRef> | undefined {\n return resource;\n },\n\n _getAlsStore(): Record<string, unknown> | undefined {\n return als?.getStore();\n }\n };\n}\n"],"mappings":";AA+OA,MAAM,cADS,OAAO,sBAAsB,cACE,IAAI,kBAAkB,IAAI,KAAA;;;;;;;;;;;;;;;;;;;;;;AAuBxE,SAAgB,iBAAiB,SAAyC;CACtE,IAAI,UAAmC,EAAE,GAAG,QAAQ,eAAe;CACnE,IAAI,WAAiD,QAAQ,kBACvD,EAAE,GAAG,QAAQ,gBAAgB,IAC7B,KAAA;CAGN,MAAM,MAAM,QAAQ,eAAe;CAEnC,OAAO;EACH,aAA8B;GAC1B,OAAO,EAAE,GAAG,QAAQ;EACxB;EAEA,YAAe,UAAmC,IAA6B;GAE3E,IAAI,CAAC,KAAK;IACN,IAAI,IAAI,OAAO,GAAG;IAClB;GACJ;GAEA,IAAI,CAAC,IAAI,OAAO,KAAA;GAEhB,MAAM,SAAS;IAAE,GAAG;IAAS,GAAG;GAAS;GACzC,OAAO,IAAI,IAAI,QAAQ,EAAE;EAC7B;EAEA,MAAM,iBAAoB,UAAmC,IAAkC;GAC3F,IAAI,CAAC,KAAK,OAAO,GAAG;GACpB,MAAM,SAAS;IAAE,GAAG;IAAS,GAAG;GAAS;GACzC,OAAO,IAAI,IAAI,QAAQ,EAAE;EAC7B;EAEA,eAA4B;GACxB,UAAU,CAAC;GACX,OAAO;EACX;EAEA,YAAY,KAA4C;GACpD,WAAW;IAAE,GAAG;IAAU,GAAG;GAAI;GACjC,OAAO;EACX;EAEA,MAAM,QAAmD;GAMrD,MAAM,WAAW,KAAK,sBAAsB;GAC5C,MAAM,cAAc,QAAQ,mBAAmB,CAAC,CAAC;GAEjD,YAAoD,sBAAsB;GAC1E,OAAO;EACX;EAEA,oBAA6C;GAGzC,MAAM,OAAO,KAAK,sBAAsB;GACxC,MAAM,aAAa,KAAK,SAAS;GACjC,IAAI,cAAc,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,GAC/C,OAAO;IAAE,GAAG;IAAM,GAAG;GAAW;GAEpC,OAAO;EACX;EAEA,wBAAiD;GAG7C,IAAI,OAAgC,CAAC;GACrC,MAAM,eAAe,QAAQ,yBAAyB,KAAK;GAC3D,IAAI,gBAAgB,OAAO,KAAK,YAAY,CAAC,CAAC,SAAS,GACnD,OAAO;QACJ,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,GACrC,OAAO;GAEX,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,GAC9B,OAAO;IAAE,GAAG;IAAM,GAAG;GAAQ;GAEjC,OAAO;EACX;EAEA,eAAqD;GACjD,OAAO;EACX;EAEA,eAAoD;GAChD,OAAO,KAAK,SAAS;EACzB;CACJ;AACJ"}
|