@mks2508/better-logger 0.18.2-alpha.2 → 0.18.3

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.
Files changed (43) hide show
  1. package/dist/Logger.d.ts +113 -3
  2. package/dist/Logger.d.ts.map +1 -1
  3. package/dist/ScopedLogger.d.ts +38 -1
  4. package/dist/ScopedLogger.d.ts.map +1 -1
  5. package/dist/chunks/LogContext-DZasm_P5.cjs +141 -0
  6. package/dist/chunks/LogContext-DZasm_P5.cjs.map +1 -0
  7. package/dist/chunks/LogContext-Dyzs61XG.js +136 -0
  8. package/dist/chunks/LogContext-Dyzs61XG.js.map +1 -0
  9. package/dist/chunks/transports-BZ-Mc2IT.cjs +1450 -0
  10. package/dist/chunks/transports-BZ-Mc2IT.cjs.map +1 -0
  11. package/dist/chunks/transports-DvaLAeGJ.js +1403 -0
  12. package/dist/chunks/transports-DvaLAeGJ.js.map +1 -0
  13. package/dist/context/LogContext.d.ts +2 -4
  14. package/dist/context/LogContext.d.ts.map +1 -1
  15. package/dist/context.cjs +1 -1
  16. package/dist/context.js +1 -1
  17. package/dist/index.cjs +325 -4
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.ts +5 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +323 -5
  22. package/dist/index.js.map +1 -1
  23. package/dist/transports/OtlpTraceTransport.d.ts +118 -0
  24. package/dist/transports/OtlpTraceTransport.d.ts.map +1 -0
  25. package/dist/transports/OtlpTransport.d.ts +27 -1
  26. package/dist/transports/OtlpTransport.d.ts.map +1 -1
  27. package/dist/transports/SpanRuntime.d.ts +73 -0
  28. package/dist/transports/SpanRuntime.d.ts.map +1 -0
  29. package/dist/transports/TransportManager.d.ts +18 -11
  30. package/dist/transports/TransportManager.d.ts.map +1 -1
  31. package/dist/transports/index.d.ts +1 -0
  32. package/dist/transports/index.d.ts.map +1 -1
  33. package/dist/transports-module.d.ts +3 -1
  34. package/dist/transports-module.d.ts.map +1 -1
  35. package/dist/transports.cjs +2 -1
  36. package/dist/transports.js +2 -2
  37. package/dist/types/index.d.ts +1 -1
  38. package/dist/types/index.d.ts.map +1 -1
  39. package/dist/types/transports.d.ts +121 -2
  40. package/dist/types/transports.d.ts.map +1 -1
  41. package/dist/utils/asyncLocalStorage.d.ts +33 -0
  42. package/dist/utils/asyncLocalStorage.d.ts.map +1 -0
  43. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -3,12 +3,12 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_core = require("./chunks/core-CqS_UBzJ.cjs");
6
- const require_transports = require("./chunks/transports-yK6CL0Ml.cjs");
6
+ const require_transports = require("./chunks/transports-BZ-Mc2IT.cjs");
7
7
  const require_utils = require("./chunks/utils-W_cxqriN.cjs");
8
8
  const require_styling = require("./chunks/styling-Cel2wPRy.cjs");
9
9
  const require_environment_detector = require("./chunks/environment-detector-D-tHkKWA.cjs");
10
10
  const require_spinner = require("./chunks/spinner-BHyYEXsM.cjs");
11
- const require_LogContext = require("./chunks/LogContext-BaMXleWj.cjs");
11
+ const require_LogContext = require("./chunks/LogContext-DZasm_P5.cjs");
12
12
  const require_HookBridge = require("./chunks/HookBridge-C-AvXmPD.cjs");
13
13
  const require_SerializerBridge = require("./chunks/SerializerBridge-C4a9Z37F.cjs");
14
14
  const require_server_fallback = require("./chunks/server-fallback-CaCPjWby.cjs");
@@ -293,6 +293,34 @@ var ScopedLogger = class {
293
293
  this.parent.logWithBindings(this.getBindings(), "trace", ...args);
294
294
  }
295
295
  /**
296
+ * Emite un point-span con el scope de este logger como
297
+ * `SpanRecord.scope`. Ver {@link Logger.event}.
298
+ *
299
+ * @param {string} name - Nombre del evento.
300
+ * @param {SpanAttributes} [attributes] - Attributes del span.
301
+ *
302
+ * @example
303
+ * logger.scope('BUILD').event('step.done', { step: 'compile' });
304
+ */
305
+ event(name, attributes) {
306
+ this.parent._emitSpanEvent(name, attributes, this.getScopePrefix());
307
+ }
308
+ span(name, fnOrAttributes, maybeFn) {
309
+ const { attributes, fn } = resolveSpanArgs(fnOrAttributes, maybeFn);
310
+ return this.parent._runSpan(name, attributes, this.getScopePrefix(), fn);
311
+ }
312
+ /**
313
+ * Inicia un span externally-ended etiquetado con el scope de este
314
+ * logger. Ver {@link Logger.startSpan}.
315
+ *
316
+ * @param {string} name - Nombre de la operación.
317
+ * @param {SpanAttributes} [attributes] - Attributes iniciales.
318
+ * @returns {Span} Handle con `set`/`end`/`fail`.
319
+ */
320
+ startSpan(name, attributes) {
321
+ return this.parent._startSpan(name, attributes, this.getScopePrefix());
322
+ }
323
+ /**
296
324
  * Delegación a {@link Logger.step}: dibuja una barra de progreso discreta
297
325
  * `current/total` para este scope.
298
326
  *
@@ -1537,6 +1565,164 @@ function createTransportBridge() {
1537
1565
  };
1538
1566
  }
1539
1567
  //#endregion
1568
+ //#region src/transports/SpanRuntime.ts
1569
+ /** ALS singleton del módulo — compartido por todas las instancias de Logger.
1570
+ *
1571
+ * Resolución por capas (global → `process.getBuiltinModule` → `require`,
1572
+ * undefined en browser) delegada al helper compartido
1573
+ * {@link resolveAsyncLocalStorage} — ver su doc para el detalle de capas.
1574
+ * `context/LogContext.ts` usa el mismo helper para su ALS de MDC. */
1575
+ const activeSpanStore = require_LogContext.resolveAsyncLocalStorage();
1576
+ /**
1577
+ * Span activo en el call stack corriente (dentro de `span(fn)`), si lo hay.
1578
+ * Lo consumen la correlación log→span (`TransportRecord.traceId/spanId`) y la
1579
+ * resolución de `parentSpanId` en spans hijos.
1580
+ */
1581
+ function getActiveSpan() {
1582
+ return activeSpanStore?.getStore();
1583
+ }
1584
+ /**
1585
+ * Ejecuta `fn` con `record` como span activo. Sin ALS disponible (browser),
1586
+ * ejecuta `fn` directamente — sin correlación pero funcional.
1587
+ */
1588
+ function runWithActiveSpan(record, fn) {
1589
+ return activeSpanStore ? activeSpanStore.run(record, fn) : fn();
1590
+ }
1591
+ /** Nanosegundos Unix actuales como string decimal (formato OTLP). */
1592
+ function nowUnixNano() {
1593
+ return String(BigInt(Date.now()) * 1000000n);
1594
+ }
1595
+ /**
1596
+ * Hex aleatorio de `bytes` bytes. Usa `crypto.getRandomValues`; sin
1597
+ * `crypto` (runtimes exóticos) degrada a `Math.random` — suficiente para ids
1598
+ * de correlación, no para secrets.
1599
+ */
1600
+ function randomHex(bytes) {
1601
+ const cryptoObj = globalThis.crypto;
1602
+ if (cryptoObj?.getRandomValues) {
1603
+ const buf = new Uint8Array(bytes);
1604
+ cryptoObj.getRandomValues(buf);
1605
+ let out = "";
1606
+ for (const b of buf) out += b.toString(16).padStart(2, "0");
1607
+ return out;
1608
+ }
1609
+ let out = "";
1610
+ for (let i = 0; i < bytes * 2; i++) out += Math.floor(Math.random() * 16).toString(16);
1611
+ return out;
1612
+ }
1613
+ /** Trace id W3C: 16 bytes → 32 chars hex. */
1614
+ function generateTraceId() {
1615
+ return randomHex(16);
1616
+ }
1617
+ /** Span id W3C: 8 bytes → 16 chars hex. */
1618
+ function generateSpanId() {
1619
+ return randomHex(8);
1620
+ }
1621
+ /**
1622
+ * Registry de spans abiertos (leak-safety). Cubre tanto los de `startSpan()`
1623
+ * (externally-ended, el caso del handoff) como los de `span(fn)` en curso:
1624
+ * si un flush llega mid-flight, es mejor exportar el span incompleto que
1625
+ * tirarlo. El doble-`end()` es no-op, así que el cierre natural posterior no
1626
+ * duplica el export.
1627
+ */
1628
+ const openSpans = /* @__PURE__ */ new Map();
1629
+ /**
1630
+ * Crea el record de span y su {@link Span} handle. El handle registra el
1631
+ * span en el registry de abiertos; `onEnd` se invoca una única vez, con el
1632
+ * record final (duración real o `incomplete: true`), para su export al
1633
+ * pipeline de transports.
1634
+ *
1635
+ * `end()` solo fija `endTimeUnixNano` si no se fijó antes — así los
1636
+ * point-spans (`event()`) pueden fijar `end = start` exacto.
1637
+ *
1638
+ * @param name - Nombre de la operación.
1639
+ * @param attributes - Attributes iniciales (copia propia del record).
1640
+ * @param scope - Scope del logger emisor.
1641
+ * @param onEnd - Callback de export; recibe el record cerrado.
1642
+ * @returns El record (para el ALS store) y el handle del span.
1643
+ */
1644
+ function createSpan(name, attributes, scope, onEnd) {
1645
+ const parent = getActiveSpan();
1646
+ const record = {
1647
+ kind: "span",
1648
+ traceId: parent?.traceId ?? generateTraceId(),
1649
+ spanId: generateSpanId(),
1650
+ parentSpanId: parent?.spanId,
1651
+ name,
1652
+ spanKind: 1,
1653
+ startTimeUnixNano: nowUnixNano(),
1654
+ endTimeUnixNano: "0",
1655
+ attributes: { ...attributes ?? {} },
1656
+ scope
1657
+ };
1658
+ let ended = false;
1659
+ const finish = () => {
1660
+ if (ended) return;
1661
+ ended = true;
1662
+ openSpans.delete(record);
1663
+ onEnd(record);
1664
+ };
1665
+ openSpans.set(record, {
1666
+ record,
1667
+ end: finish,
1668
+ forceEnd: () => {
1669
+ if (ended) return;
1670
+ record.incomplete = true;
1671
+ if (record.endTimeUnixNano === "0") record.endTimeUnixNano = nowUnixNano();
1672
+ finish();
1673
+ }
1674
+ });
1675
+ const handle = {
1676
+ traceId: record.traceId,
1677
+ spanId: record.spanId,
1678
+ set(key, value) {
1679
+ record.attributes[key] = value;
1680
+ return handle;
1681
+ },
1682
+ end(attributes) {
1683
+ if (attributes) Object.assign(record.attributes, attributes);
1684
+ if (ended) return;
1685
+ if (record.endTimeUnixNano === "0") record.endTimeUnixNano = nowUnixNano();
1686
+ finish();
1687
+ },
1688
+ fail(err) {
1689
+ if (ended) return;
1690
+ record.status = {
1691
+ code: 2,
1692
+ message: err instanceof Error ? err.message : String(err)
1693
+ };
1694
+ this.end();
1695
+ }
1696
+ };
1697
+ return {
1698
+ record,
1699
+ span: handle
1700
+ };
1701
+ }
1702
+ /**
1703
+ * Emite un point-span (`event()`): start = end exactamente, sin registro en
1704
+ * abiertos tras el cierre — no hay leak posible. El `parentSpanId` se resuelve
1705
+ * contra el span activo si existe, pero NO fija contexto ALS.
1706
+ */
1707
+ function emitEventSpan(name, attributes, scope, onEnd) {
1708
+ const { record, span } = createSpan(name, attributes, scope, onEnd);
1709
+ record.endTimeUnixNano = record.startTimeUnixNano;
1710
+ span.end();
1711
+ }
1712
+ /**
1713
+ * Fuerza el cierre de todos los spans abiertos (`incomplete: true`) y los
1714
+ * exporta vía el `onEnd` de cada uno. Lo invocan `flushTransports()` /
1715
+ * `closeTransports()` / `cleanup()` ANTES del flush real — un span abierto
1716
+ * nunca se tira en shutdown.
1717
+ *
1718
+ * @returns Los records forzados a cerrar (para diagnóstico/tests).
1719
+ */
1720
+ function forceCloseOpenSpans() {
1721
+ const snapshot = [...openSpans.values()];
1722
+ for (const entry of snapshot) entry.forceEnd();
1723
+ return snapshot.map((entry) => entry.record);
1724
+ }
1725
+ //#endregion
1540
1726
  //#region src/playground/TerminalBridge.ts
1541
1727
  /**
1542
1728
  * Crea un {@link TerminalBridge} usando un getter para evitar referencias
@@ -2012,6 +2198,7 @@ var Logger = class Logger {
2012
2198
  } catch {}
2013
2199
  this.themeChangeListener = null;
2014
2200
  }
2201
+ forceCloseOpenSpans();
2015
2202
  await this.transportBridge.closeTransports();
2016
2203
  this.handlers.length = 0;
2017
2204
  this.timers.clear();
@@ -2434,21 +2621,27 @@ var Logger = class Logger {
2434
2621
  return this.transportBridge.removeTransport(id);
2435
2622
  }
2436
2623
  /**
2437
- * Fuerza el flush de todos los transports
2624
+ * Fuerza el flush de todos los transports. Antes de flushear, cierra los
2625
+ * spans aún abiertos (`incomplete: true`) y los encola para export — un
2626
+ * span abierto nunca se tira en un flush.
2438
2627
  *
2439
2628
  * @returns Promise que resuelve cuando todos los buffers están vaciados
2440
2629
  *
2441
2630
  */
2442
2631
  async flushTransports() {
2632
+ forceCloseOpenSpans();
2443
2633
  await this.transportBridge.flushTransports();
2444
2634
  }
2445
2635
  /**
2446
- * Cierra todos los transports
2636
+ * Cierra todos los transports. Como en {@link flushTransports}, primero
2637
+ * fuerza el cierre de los spans abiertos para que viajen en el flush
2638
+ * final del close.
2447
2639
  *
2448
2640
  * @returns Promise que resuelve cuando todos están cerrados
2449
2641
  *
2450
2642
  */
2451
2643
  async closeTransports() {
2644
+ forceCloseOpenSpans();
2452
2645
  await this.transportBridge.closeTransports();
2453
2646
  }
2454
2647
  /**
@@ -2550,6 +2743,11 @@ var Logger = class Logger {
2550
2743
  resource: this.logContext._getResource() ? { ...this.logContext._getResource() } : void 0,
2551
2744
  ...extra
2552
2745
  };
2746
+ const activeSpan = getActiveSpan();
2747
+ if (activeSpan && !record.traceId) {
2748
+ record.traceId = activeSpan.traceId;
2749
+ record.spanId = activeSpan.spanId;
2750
+ }
2553
2751
  this.transportBridge.writeRecord(record);
2554
2752
  }
2555
2753
  /**
@@ -2806,6 +3004,104 @@ var Logger = class Logger {
2806
3004
  return this.log("critical", ...args);
2807
3005
  }
2808
3006
  /**
3007
+ * Emite un **point-span**: un span con `start = end` para completion
3008
+ * signals y eventos puntuales (una operación instantánea, un hito).
3009
+ * No registra contexto activo ni puede leakear — se exporta de inmediato.
3010
+ *
3011
+ * El nombre `event` (y no `trace`) es deliberado: `trace` es un log level
3012
+ * (el -1) y está reservado.
3013
+ *
3014
+ * @param {string} name - Nombre del evento (`job.completed`, `cache.flushed`, ...).
3015
+ * @param {SpanAttributes} [attributes] - Attributes del span.
3016
+ *
3017
+ * @example
3018
+ * logger.event('build.finished', { status: 'ok', durationMs: 4200 });
3019
+ *
3020
+ * @see {@link span} para intervalos con work-block
3021
+ * @see {@link startSpan} para intervalos externally-ended
3022
+ */
3023
+ event(name, attributes) {
3024
+ emitEventSpan(name, attributes, this._spanScopeName(), (record) => {
3025
+ this.transportBridge.writeRecord(record);
3026
+ });
3027
+ }
3028
+ span(name, fnOrAttributes, maybeFn) {
3029
+ const { attributes, fn } = resolveSpanArgs(fnOrAttributes, maybeFn);
3030
+ return this._runSpan(name, attributes, this._spanScopeName(), fn);
3031
+ }
3032
+ /**
3033
+ * Inicia un span de intervalo **externally-ended**: devuelve el handle y
3034
+ * NO fija contexto ALS (el cierre ocurre fuera del bloque léxico — p.ej.
3035
+ * un `cli.spawn` que se cierra desde un poll o callback externo).
3036
+ *
3037
+ * Si el span no se cierra antes de `flushTransports()` / shutdown, el
3038
+ * flush lo fuerza a cerrar con `incomplete: true` y lo exporta — nunca
3039
+ * se tira.
3040
+ *
3041
+ * @param {string} name - Nombre de la operación.
3042
+ * @param {SpanAttributes} [attributes] - Attributes iniciales.
3043
+ * @returns {Span} Handle con `set`/`end`/`fail` y los ids del span.
3044
+ *
3045
+ * @example
3046
+ * const s = logger.startSpan('spawn.build', { cmd: 'make' });
3047
+ * proc.on('exit', code => {
3048
+ * if (code === 0) s.end({ exitCode: code });
3049
+ * else s.fail(new Error(`exit ${code}`));
3050
+ * });
3051
+ *
3052
+ * @see {@link span} para el caso con work-block
3053
+ */
3054
+ startSpan(name, attributes) {
3055
+ return this._startSpan(name, attributes, this._spanScopeName());
3056
+ }
3057
+ /**
3058
+ * Scope por defecto de los spans emitidos por este logger (el
3059
+ * `globalPrefix`, o `'root'`).
3060
+ * @internal
3061
+ */
3062
+ _spanScopeName() {
3063
+ return this.config.globalPrefix || "root";
3064
+ }
3065
+ /**
3066
+ * Emite un point-span con scope explícito. Lo consume
3067
+ * {@link ScopedLogger.event}, que pasa su scope compuesto.
3068
+ * @internal
3069
+ */
3070
+ _emitSpanEvent(name, attributes, scope) {
3071
+ emitEventSpan(name, attributes, scope, (record) => {
3072
+ this.transportBridge.writeRecord(record);
3073
+ });
3074
+ }
3075
+ /**
3076
+ * Crea un span externally-ended con scope explícito. Lo consume
3077
+ * {@link ScopedLogger.startSpan}.
3078
+ * @internal
3079
+ */
3080
+ _startSpan(name, attributes, scope) {
3081
+ const { span } = createSpan(name, attributes, scope, (record) => {
3082
+ this.transportBridge.writeRecord(record);
3083
+ });
3084
+ return span;
3085
+ }
3086
+ /**
3087
+ * Corre `fn` dentro de un span activo en el ALS con scope explícito.
3088
+ * Lo consume {@link ScopedLogger.span}.
3089
+ * @internal
3090
+ */
3091
+ async _runSpan(name, attributes, scope, fn) {
3092
+ const { record, span } = createSpan(name, attributes, scope, (r) => {
3093
+ this.transportBridge.writeRecord(r);
3094
+ });
3095
+ try {
3096
+ const result = await runWithActiveSpan(record, () => fn(span));
3097
+ span.end();
3098
+ return result;
3099
+ } catch (error) {
3100
+ span.fail(error);
3101
+ throw error;
3102
+ }
3103
+ }
3104
+ /**
2809
3105
  * Muestra datos en formato de tabla. Pasa por la pipeline completa
2810
3106
  * (outputMode-respecting writeOutput + transports + hooks).
2811
3107
  *
@@ -3270,6 +3566,23 @@ function toAttributeValue(value) {
3270
3566
  return;
3271
3567
  }
3272
3568
  }
3569
+ /**
3570
+ * Resuelve los argumentos del overload `span(name, fn?)` /
3571
+ * `span(name, attributes, fn)` a `{ attributes, fn }`.
3572
+ *
3573
+ * @internal Compartido con `ScopedLogger.span`; no es API pública.
3574
+ */
3575
+ function resolveSpanArgs(fnOrAttributes, maybeFn) {
3576
+ if (typeof fnOrAttributes === "function") return {
3577
+ attributes: void 0,
3578
+ fn: fnOrAttributes
3579
+ };
3580
+ if (typeof maybeFn === "function") return {
3581
+ attributes: fnOrAttributes,
3582
+ fn: maybeFn
3583
+ };
3584
+ throw new TypeError("span(name, …): se requiere una función fn");
3585
+ }
3273
3586
  //#endregion
3274
3587
  //#region src/index.ts
3275
3588
  /**
@@ -3302,6 +3615,11 @@ const group = (label, collapsed) => getLogger().group(label, collapsed);
3302
3615
  const groupEnd = () => getLogger().groupEnd();
3303
3616
  const time = (label) => getLogger().time(label);
3304
3617
  const timeEnd = (label) => getLogger().timeEnd(label);
3618
+ const event = (name, attributes) => getLogger().event(name, attributes);
3619
+ const startSpan = (name, attributes) => getLogger().startSpan(name, attributes);
3620
+ function span(name, fnOrAttributes, maybeFn) {
3621
+ return getLogger().span(name, fnOrAttributes, maybeFn);
3622
+ }
3305
3623
  const setGlobalPrefix = (prefix) => getLogger().setGlobalPrefix(prefix);
3306
3624
  const scope = (name) => getLogger().scope(name);
3307
3625
  const component = (name) => getLogger().component(name);
@@ -3360,6 +3678,7 @@ exports.critical = critical;
3360
3678
  exports.debug = debug;
3361
3679
  exports.default = src_default;
3362
3680
  exports.error = error;
3681
+ exports.event = event;
3363
3682
  exports.flushTransports = flushTransports;
3364
3683
  exports.formatLogLevelANSI = require_utils.formatLogLevelANSI;
3365
3684
  exports.formatSuccessANSI = require_utils.formatSuccessANSI;
@@ -3390,6 +3709,8 @@ exports.setGlobalPrefix = setGlobalPrefix;
3390
3709
  exports.setTheme = setTheme;
3391
3710
  exports.setVerbosity = setVerbosity;
3392
3711
  exports.showBanner = showBanner;
3712
+ exports.span = span;
3713
+ exports.startSpan = startSpan;
3393
3714
  exports.success = success;
3394
3715
  exports.supportsANSI = require_environment_detector.supportsANSI;
3395
3716
  exports.table = table;