@spinajs/log-common 2.0.480 → 2.0.482

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 (68) hide show
  1. package/README.md +125 -4
  2. package/lib/cjs/BatchQueue.d.ts +127 -0
  3. package/lib/cjs/BatchQueue.d.ts.map +1 -0
  4. package/lib/cjs/BatchQueue.js +126 -0
  5. package/lib/cjs/BatchQueue.js.map +1 -0
  6. package/lib/cjs/filters/filter.d.ts +27 -0
  7. package/lib/cjs/filters/filter.d.ts.map +1 -0
  8. package/lib/cjs/filters/filter.js +20 -0
  9. package/lib/cjs/filters/filter.js.map +1 -0
  10. package/lib/cjs/filters/whenRepeated.d.ts +52 -0
  11. package/lib/cjs/filters/whenRepeated.d.ts.map +1 -0
  12. package/lib/cjs/filters/whenRepeated.js +98 -0
  13. package/lib/cjs/filters/whenRepeated.js.map +1 -0
  14. package/lib/cjs/format.d.ts +20 -0
  15. package/lib/cjs/format.d.ts.map +1 -0
  16. package/lib/cjs/format.js +75 -0
  17. package/lib/cjs/format.js.map +1 -0
  18. package/lib/cjs/index.d.ts +260 -21
  19. package/lib/cjs/index.d.ts.map +1 -1
  20. package/lib/cjs/index.js +207 -33
  21. package/lib/cjs/index.js.map +1 -1
  22. package/lib/cjs/perf.d.ts +109 -0
  23. package/lib/cjs/perf.d.ts.map +1 -0
  24. package/lib/cjs/perf.js +162 -0
  25. package/lib/cjs/perf.js.map +1 -0
  26. package/lib/cjs/persistence.d.ts +19 -0
  27. package/lib/cjs/persistence.d.ts.map +1 -0
  28. package/lib/cjs/persistence.js +137 -0
  29. package/lib/cjs/persistence.js.map +1 -0
  30. package/lib/cjs/serializers.d.ts +77 -0
  31. package/lib/cjs/serializers.d.ts.map +1 -0
  32. package/lib/cjs/serializers.js +215 -0
  33. package/lib/cjs/serializers.js.map +1 -0
  34. package/lib/mjs/BatchQueue.d.ts +127 -0
  35. package/lib/mjs/BatchQueue.d.ts.map +1 -0
  36. package/lib/mjs/BatchQueue.js +122 -0
  37. package/lib/mjs/BatchQueue.js.map +1 -0
  38. package/lib/mjs/filters/filter.d.ts +27 -0
  39. package/lib/mjs/filters/filter.d.ts.map +1 -0
  40. package/lib/mjs/filters/filter.js +16 -0
  41. package/lib/mjs/filters/filter.js.map +1 -0
  42. package/lib/mjs/filters/whenRepeated.d.ts +52 -0
  43. package/lib/mjs/filters/whenRepeated.d.ts.map +1 -0
  44. package/lib/mjs/filters/whenRepeated.js +95 -0
  45. package/lib/mjs/filters/whenRepeated.js.map +1 -0
  46. package/lib/mjs/format.d.ts +20 -0
  47. package/lib/mjs/format.d.ts.map +1 -0
  48. package/lib/mjs/format.js +72 -0
  49. package/lib/mjs/format.js.map +1 -0
  50. package/lib/mjs/index.d.ts +260 -21
  51. package/lib/mjs/index.d.ts.map +1 -1
  52. package/lib/mjs/index.js +201 -10
  53. package/lib/mjs/index.js.map +1 -1
  54. package/lib/mjs/perf.d.ts +109 -0
  55. package/lib/mjs/perf.d.ts.map +1 -0
  56. package/lib/mjs/perf.js +157 -0
  57. package/lib/mjs/perf.js.map +1 -0
  58. package/lib/mjs/persistence.d.ts +19 -0
  59. package/lib/mjs/persistence.d.ts.map +1 -0
  60. package/lib/mjs/persistence.js +132 -0
  61. package/lib/mjs/persistence.js.map +1 -0
  62. package/lib/mjs/serializers.d.ts +77 -0
  63. package/lib/mjs/serializers.d.ts.map +1 -0
  64. package/lib/mjs/serializers.js +208 -0
  65. package/lib/mjs/serializers.js.map +1 -0
  66. package/lib/tsconfig.cjs.tsbuildinfo +1 -1
  67. package/lib/tsconfig.mjs.tsbuildinfo +1 -1
  68. package/package.json +8 -9
package/README.md CHANGED
@@ -1,11 +1,132 @@
1
1
  # `@spinajs/log-common`
2
2
 
3
- > TODO: description
3
+ Shared, dependency-light contracts for the SpinaJS logging stack. This package
4
+ holds no logging implementation of its own; it defines the abstract types that
5
+ `@spinajs/log`, `@spinajs/internal-logger` and the various log-source packages
6
+ implement. Depend on it when you write a **custom log target** or a **custom
7
+ `Log` implementation**, and want to avoid a circular dependency on `@spinajs/log`.
4
8
 
5
- ## Usage
9
+ ## What lives here
6
10
 
11
+ | Export | Purpose |
12
+ | --- | --- |
13
+ | `Log` (abstract) | The logger contract: `trace/debug/info/warn/error/fatal/security/success`, `child`, `write`, `addVariable`, `timeStart/timeEnd`. |
14
+ | `LogTarget<T>` (abstract) | A sink that receives `ILogEntry` objects and writes them somewhere ( console, file, HTTP, ... ). |
15
+ | `createLogMessageObject` | Builds a normalized `ILogEntry` from the many call shapes the `Log` methods accept. |
16
+ | `LogLevel` / `StrToLogLevel` / `LogLevelStrings` | Level enum and string<->enum maps. |
17
+ | `ILogEntry`, `ILogRule`, `ICommonTargetOptions`, `IFileTargetOptions`, `ILogOptions` | Config and payload shapes. |
18
+
19
+ ## The `LogTarget` contract
20
+
21
+ ```ts
22
+ abstract class LogTarget<T extends ICommonTargetOptions> extends SyncService {
23
+ public HasError: boolean; // true after a failed write, cleared on the next success
24
+ public Error: Error | null | unknown; // the last write error, or null
25
+ public Options: T; // merged options ( includes the default layout )
26
+ public abstract write(data: ILogEntry): void;
27
+ }
7
28
  ```
8
- const logCommon = require('@spinajs/log-common');
9
29
 
10
- // TODO: DEMONSTRATE API
30
+ A target receives fully-formed `ILogEntry` objects. It is responsible for:
31
+
32
+ - honouring `Options.enabled` ( skip when `false` ),
33
+ - rendering `data.Variables` through its `Options.layout`,
34
+ - setting `HasError = true` / `Error = err` when a write fails and clearing them
35
+ ( `HasError = false`, `Error = null` ) on the next successful write. Consumers
36
+ can poll these fields to detect a degraded sink.
37
+
38
+ ## `createLogMessageObject`
39
+
40
+ ```ts
41
+ createLogMessageObject(
42
+ err: Error | string,
43
+ message: string | any[],
44
+ level: LogLevel,
45
+ logger: string,
46
+ variables: any,
47
+ ...args: any[]
48
+ ): ILogEntry
11
49
  ```
50
+
51
+ It normalizes the two calling conventions the `Log` methods accept:
52
+
53
+ - **Message overload** — `err` is the format string ( or `undefined` ) and
54
+ `message` carries the format arguments, e.g. `log.info("hello %s", "world")`.
55
+ - **Error overload** — `err` is an `Error` and `message` is the human message,
56
+ e.g. `log.error(err, "could not connect")`.
57
+
58
+ Rules it applies:
59
+
60
+ - `sMsg = (err instanceof Error || !err) ? message : err` — an `err` string is
61
+ itself the message body, so a bare `log.info("hi")` works.
62
+ - `tMsg = args.length ? format(sMsg, ...args) : sMsg` — runs `util.format`-style
63
+ substitution ( see `format.ts`; `%s %d %i %f %j %o %O %%` ) only when there are
64
+ args.
65
+ - `Variables.error` is set only for the `Error` overload.
66
+ - `Variables.logger = logger ?? message` and `Variables.level` is the upper-cased
67
+ level string.
68
+
69
+ The returned entry is:
70
+
71
+ ```ts
72
+ {
73
+ Level: LogLevel,
74
+ Variables: {
75
+ error, // Error | undefined
76
+ level, // "INFO" | "ERROR" | ...
77
+ logger, // logger name
78
+ message, // formatted message
79
+ ...variables
80
+ }
81
+ }
82
+ ```
83
+
84
+ ## Layout variables
85
+
86
+ A target's `layout` is a template string resolved by `@spinajs/configuration`'s
87
+ `format(variables, layout)`. Every key on `ILogEntry.Variables` is available as
88
+ `${key}`. The always-present variables are:
89
+
90
+ | Variable | Value |
91
+ | --- | --- |
92
+ | `${datetime}` | timestamp of the entry |
93
+ | `${level}` | upper-cased level, e.g. `INFO` |
94
+ | `${message}` | the formatted message |
95
+ | `${error}` | the `Error` ( use `${error:message}` for its message ) |
96
+ | `${logger}` | the logger name |
97
+
98
+ Conditional blocks are supported, e.g. `${?error} ... ${/error}` only renders
99
+ when `error` is set. Any custom variable you attach via `log.addVariable(name, value)`
100
+ or the config `variables` array is also available as `${name}`.
101
+
102
+ The default layout ( applied to every target unless overridden ) is:
103
+
104
+ ```
105
+ ${datetime} ${level} ${message}${?error} Exception: ${error:message}${/error} (${logger})
106
+ ```
107
+
108
+ ## Writing a custom target
109
+
110
+ ```ts
111
+ import { Injectable } from "@spinajs/di";
112
+ import { format } from "@spinajs/configuration";
113
+ import { LogTarget, ILogEntry, ICommonTargetOptions } from "@spinajs/log-common";
114
+
115
+ @Injectable("MyTarget") // referenced by `type: "MyTarget"` in config
116
+ export class MyTarget extends LogTarget<ICommonTargetOptions> {
117
+ public write(data: ILogEntry): void {
118
+ if (!this.Options.enabled) return;
119
+ try {
120
+ // render with the configured layout and send it somewhere
121
+ send(format(data.Variables, this.Options.layout));
122
+ this.HasError = false;
123
+ this.Error = null;
124
+ } catch (err) {
125
+ this.HasError = true;
126
+ this.Error = err;
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ See `@spinajs/log` for the concrete targets and the rules/targets configuration.
@@ -0,0 +1,127 @@
1
+ /**
2
+ * A small, dependency-free, browser-safe buffered-batch queue.
3
+ *
4
+ * `BatchQueue<T>` owns ACCUMULATION and FLUSH TRIGGERING only; it delegates the
5
+ * actual I/O to the owner via `onFlush`. Ported from the essence of
6
+ * OpenTelemetry's BatchLogRecordProcessor.
7
+ *
8
+ * Triggers a flush when either:
9
+ * - the buffer reaches `maxBatch` items ( size trigger ), or
10
+ * - the periodic timer fires every `flushIntervalMs` ( interval trigger ).
11
+ *
12
+ * ## Failure model
13
+ *
14
+ * There are two, deliberately distinct, ways items can leave the queue:
15
+ *
16
+ * 1. **Overflow ( the ONLY silent drop ).** The buffer is hard-capped at
17
+ * `maxQueue`. When it grows past the cap the OLDEST items are dropped so
18
+ * memory stays bounded, and `onOverflow(droppedItems)` fires. This is the
19
+ * back-pressure release valve for a stuck/slow sink.
20
+ *
21
+ * 2. **Flush failure ( never a silent drop ).** `onFlush` may throw / reject.
22
+ * `BatchQueue` does NOT retry, requeue, or swallow the batch on its own —
23
+ * that is the owner's decision. The rejection PROPAGATES to any caller that
24
+ * awaits `flush()` / `forceFlush()`, so the owner can `catch` it and call
25
+ * `requeueFront(batch)` to put the batch back at the front ( never-drop
26
+ * semantics ) or drop it deliberately. The periodic timer intentionally
27
+ * ignores the rejection ( `void this.flush()` ) so a failing sink never
28
+ * kills the flush loop; the in-flight guard is always cleared in `finally`
29
+ * so the next flush can proceed regardless of outcome.
30
+ *
31
+ * ## Re-entrancy
32
+ *
33
+ * At most one `onFlush` runs at a time. If a flush is already in flight, calling
34
+ * `flush()` returns the SAME in-flight promise ( coalesced ) rather than
35
+ * starting a second `onFlush`. A flush snapshots and clears the buffer
36
+ * synchronously before awaiting the I/O, so items enqueued during a flush land
37
+ * in a fresh buffer and are picked up by the next flush.
38
+ *
39
+ * ## Runtime
40
+ *
41
+ * Uses only `setInterval` / `setTimeout` ( available in both Node and browsers ).
42
+ * The periodic timer ( and the optional per-flush timeout timer ) are `unref()`'d
43
+ * when that method exists, so they never keep a Node process alive; the `?.`
44
+ * guard makes the calls harmless in browsers.
45
+ */
46
+ export interface IBatchQueueOptions<T> {
47
+ /** Flush automatically once the buffer reaches this many items. */
48
+ maxBatch: number;
49
+ /** Hard cap on buffered items. Beyond it the OLDEST are dropped ( onOverflow fires ). */
50
+ maxQueue: number;
51
+ /** Periodic flush tick in ms. The timer is unref()'d so it never blocks process exit. */
52
+ flushIntervalMs: number;
53
+ /** Optional per-flush timeout in ms. If onFlush does not settle in time, the flush rejects. */
54
+ exportTimeoutMs?: number;
55
+ /** Does the actual I/O for a batch. May throw/reject; the owner handles retry/requeue/drop. */
56
+ onFlush: (items: T[]) => Promise<void>;
57
+ /**
58
+ * Called when the queue cap is exceeded and the oldest items are dropped.
59
+ * Receives the DROPPED ITEMS themselves ( oldest first ) so an owner can route
60
+ * exactly what was dropped to a fallback ( see LogTarget.OnDropped ). Use
61
+ * `droppedItems.length` when only the count matters.
62
+ */
63
+ onOverflow?: (droppedItems: T[]) => void;
64
+ }
65
+ export declare class BatchQueue<T> {
66
+ private options;
67
+ private buffer;
68
+ private timer;
69
+ private stopped;
70
+ /**
71
+ * The currently running flush, or null when idle. Used both as the
72
+ * re-entrancy guard ( coalesce concurrent flush() calls ) and to let
73
+ * forceFlush()/shutdown() wait for an in-flight flush before draining.
74
+ */
75
+ private inFlight;
76
+ constructor(options: IBatchQueueOptions<T>);
77
+ /** Current number of buffered items. */
78
+ get size(): number;
79
+ /**
80
+ * Add an item to the buffer.
81
+ *
82
+ * If the queue has been shut down this is a no-op that resolves immediately.
83
+ * When the buffer reaches `maxBatch` a flush is triggered and its promise is
84
+ * returned, so a caller MAY await a size-triggered flush; otherwise resolves
85
+ * immediately.
86
+ */
87
+ enqueue(item: T): Promise<void>;
88
+ /**
89
+ * Put items back at the FRONT of the buffer ( then enforce the cap ). Owners
90
+ * use this to re-queue a batch whose `onFlush` failed, without dropping it.
91
+ */
92
+ requeueFront(items: T[]): void;
93
+ /**
94
+ * Enforce the hard `maxQueue` cap by dropping the OLDEST items. Fires
95
+ * `onOverflow(droppedItems)` with the spliced-out items when anything is dropped.
96
+ */
97
+ private enforceCap;
98
+ /**
99
+ * Flush the entire buffer in a single `onFlush` call.
100
+ *
101
+ * Re-entrancy: if a flush is already in flight, that same promise is returned
102
+ * ( no second `onFlush` ). If the buffer is empty, resolves immediately.
103
+ * Otherwise the buffer is snapshotted and cleared synchronously, `onFlush`
104
+ * runs ( optionally raced against `exportTimeoutMs` ), and the in-flight guard
105
+ * is cleared in `finally` regardless of success or failure. The returned
106
+ * promise settles with `onFlush`'s outcome so an awaiting owner can catch a
107
+ * rejection and requeue.
108
+ */
109
+ flush(): Promise<void>;
110
+ /**
111
+ * Race a flush promise against a timeout that rejects after `ms`. The timeout
112
+ * timer is cleared ( and unref()'d ) so it neither leaks nor keeps the loop alive.
113
+ */
114
+ private withTimeout;
115
+ /**
116
+ * Flush any buffered items and await completion. If a flush is already in
117
+ * flight, wait for it first, then flush any remainder that accumulated. Since
118
+ * a single `flush()` drains the ENTIRE buffer, one follow-up flush suffices.
119
+ */
120
+ forceFlush(): Promise<void>;
121
+ /**
122
+ * Stop the periodic timer and perform a best-effort final flush of buffered
123
+ * items. After shutdown, `enqueue` is a no-op that resolves.
124
+ */
125
+ shutdown(): Promise<void>;
126
+ }
127
+ //# sourceMappingURL=BatchQueue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BatchQueue.d.ts","sourceRoot":"","sources":["../../src/BatchQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACnC,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,yFAAyF;IACzF,QAAQ,EAAE,MAAM,CAAC;IACjB,yFAAyF;IACzF,eAAe,EAAE,MAAM,CAAC;IACxB,+FAA+F;IAC/F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+FAA+F;IAC/F,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;CAC1C;AAED,qBAAa,UAAU,CAAC,CAAC;IAcX,OAAO,CAAC,OAAO;IAb3B,OAAO,CAAC,MAAM,CAAW;IAEzB,OAAO,CAAC,KAAK,CAAiC;IAE9C,OAAO,CAAC,OAAO,CAAS;IAExB;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAA8B;gBAE1B,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAMlD,wCAAwC;IACxC,IAAW,IAAI,IAAI,MAAM,CAExB;IAED;;;;;;;OAOG;IACI,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAetC;;;OAGG;IACI,YAAY,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI;IAKrC;;;OAGG;IACH,OAAO,CAAC,UAAU;IAQlB;;;;;;;;;;OAUG;IACI,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB7B;;;OAGG;IACH,OAAO,CAAC,WAAW;IAanB;;;;OAIG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQxC;;;OAGG;IACU,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAKvC"}
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BatchQueue = void 0;
4
+ class BatchQueue {
5
+ constructor(options) {
6
+ this.options = options;
7
+ this.buffer = [];
8
+ this.stopped = false;
9
+ /**
10
+ * The currently running flush, or null when idle. Used both as the
11
+ * re-entrancy guard ( coalesce concurrent flush() calls ) and to let
12
+ * forceFlush()/shutdown() wait for an in-flight flush before draining.
13
+ */
14
+ this.inFlight = null;
15
+ this.timer = setInterval(() => void this.flush(), this.options.flushIntervalMs);
16
+ // Do not keep the event loop / process alive just for the flush tick.
17
+ this.timer.unref?.();
18
+ }
19
+ /** Current number of buffered items. */
20
+ get size() {
21
+ return this.buffer.length;
22
+ }
23
+ /**
24
+ * Add an item to the buffer.
25
+ *
26
+ * If the queue has been shut down this is a no-op that resolves immediately.
27
+ * When the buffer reaches `maxBatch` a flush is triggered and its promise is
28
+ * returned, so a caller MAY await a size-triggered flush; otherwise resolves
29
+ * immediately.
30
+ */
31
+ enqueue(item) {
32
+ if (this.stopped) {
33
+ return Promise.resolve();
34
+ }
35
+ this.buffer.push(item);
36
+ this.enforceCap();
37
+ if (this.size >= this.options.maxBatch) {
38
+ return this.flush();
39
+ }
40
+ return Promise.resolve();
41
+ }
42
+ /**
43
+ * Put items back at the FRONT of the buffer ( then enforce the cap ). Owners
44
+ * use this to re-queue a batch whose `onFlush` failed, without dropping it.
45
+ */
46
+ requeueFront(items) {
47
+ this.buffer = [...items, ...this.buffer];
48
+ this.enforceCap();
49
+ }
50
+ /**
51
+ * Enforce the hard `maxQueue` cap by dropping the OLDEST items. Fires
52
+ * `onOverflow(droppedItems)` with the spliced-out items when anything is dropped.
53
+ */
54
+ enforceCap() {
55
+ if (this.buffer.length > this.options.maxQueue) {
56
+ const dropCount = this.buffer.length - this.options.maxQueue;
57
+ const droppedItems = this.buffer.splice(0, dropCount);
58
+ this.options.onOverflow?.(droppedItems);
59
+ }
60
+ }
61
+ /**
62
+ * Flush the entire buffer in a single `onFlush` call.
63
+ *
64
+ * Re-entrancy: if a flush is already in flight, that same promise is returned
65
+ * ( no second `onFlush` ). If the buffer is empty, resolves immediately.
66
+ * Otherwise the buffer is snapshotted and cleared synchronously, `onFlush`
67
+ * runs ( optionally raced against `exportTimeoutMs` ), and the in-flight guard
68
+ * is cleared in `finally` regardless of success or failure. The returned
69
+ * promise settles with `onFlush`'s outcome so an awaiting owner can catch a
70
+ * rejection and requeue.
71
+ */
72
+ flush() {
73
+ if (this.inFlight) {
74
+ return this.inFlight;
75
+ }
76
+ if (this.buffer.length === 0) {
77
+ return Promise.resolve();
78
+ }
79
+ const batch = this.buffer;
80
+ this.buffer = [];
81
+ const run = this.options.exportTimeoutMs !== undefined ? this.withTimeout(this.options.onFlush(batch), this.options.exportTimeoutMs) : this.options.onFlush(batch);
82
+ // Store as the guard and clear it once settled ( success OR failure ) so the
83
+ // next flush can proceed. The promise is still returned to the caller so the
84
+ // rejection propagates for owner-side handling.
85
+ this.inFlight = run.finally(() => {
86
+ this.inFlight = null;
87
+ });
88
+ return this.inFlight;
89
+ }
90
+ /**
91
+ * Race a flush promise against a timeout that rejects after `ms`. The timeout
92
+ * timer is cleared ( and unref()'d ) so it neither leaks nor keeps the loop alive.
93
+ */
94
+ withTimeout(promise, ms) {
95
+ let timeout;
96
+ const timer = new Promise((_resolve, reject) => {
97
+ timeout = setTimeout(() => reject(new Error(`BatchQueue flush timed out after ${ms}ms`)), ms);
98
+ timeout.unref?.();
99
+ });
100
+ return Promise.race([promise, timer]).finally(() => {
101
+ clearTimeout(timeout);
102
+ });
103
+ }
104
+ /**
105
+ * Flush any buffered items and await completion. If a flush is already in
106
+ * flight, wait for it first, then flush any remainder that accumulated. Since
107
+ * a single `flush()` drains the ENTIRE buffer, one follow-up flush suffices.
108
+ */
109
+ async forceFlush() {
110
+ if (this.inFlight) {
111
+ await this.inFlight;
112
+ }
113
+ await this.flush();
114
+ }
115
+ /**
116
+ * Stop the periodic timer and perform a best-effort final flush of buffered
117
+ * items. After shutdown, `enqueue` is a no-op that resolves.
118
+ */
119
+ async shutdown() {
120
+ this.stopped = true;
121
+ clearInterval(this.timer);
122
+ await this.forceFlush();
123
+ }
124
+ }
125
+ exports.BatchQueue = BatchQueue;
126
+ //# sourceMappingURL=BatchQueue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BatchQueue.js","sourceRoot":"","sources":["../../src/BatchQueue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { ILogEntry } from "../index.js";
2
+ /**
3
+ * Common shape of a configured filter entry. `type` is the DI string name the
4
+ * filter is registered under ( eg. "LevelFilter" ); all other keys are the
5
+ * filter's own free-form options and are read by the concrete filter.
6
+ */
7
+ export interface ILogFilterOptions {
8
+ type: string;
9
+ [k: string]: unknown;
10
+ }
11
+ /**
12
+ * Base class for a composable log filter. Filters are DI-registered by string
13
+ * name ( just like log targets ) via `@Injectable("<Name>")` and resolved per
14
+ * logger from `logger.filters` config. They run IN ORDER inside the logger's
15
+ * `write()`: each filter may pass the entry through ( optionally mutating it ),
16
+ * or return `null` to DROP it entirely.
17
+ *
18
+ * Kept a plain, dependency-light abstract class ( no SyncService etc. ) so the
19
+ * contract stays browser-safe and cheap to instantiate.
20
+ */
21
+ export declare abstract class LogFilter {
22
+ protected options?: ILogFilterOptions | undefined;
23
+ constructor(options?: ILogFilterOptions | undefined);
24
+ /** Return the ( possibly modified ) entry to keep, or null to DROP it. */
25
+ abstract apply(entry: ILogEntry): ILogEntry | null;
26
+ }
27
+ //# sourceMappingURL=filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../../src/filters/filter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,8BAAsB,SAAS;IACjB,SAAS,CAAC,OAAO,CAAC,EAAE,iBAAiB;gBAA3B,OAAO,CAAC,EAAE,iBAAiB,YAAA;IAEjD,0EAA0E;aAC1D,KAAK,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI;CAC1D"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LogFilter = void 0;
4
+ /**
5
+ * Base class for a composable log filter. Filters are DI-registered by string
6
+ * name ( just like log targets ) via `@Injectable("<Name>")` and resolved per
7
+ * logger from `logger.filters` config. They run IN ORDER inside the logger's
8
+ * `write()`: each filter may pass the entry through ( optionally mutating it ),
9
+ * or return `null` to DROP it entirely.
10
+ *
11
+ * Kept a plain, dependency-light abstract class ( no SyncService etc. ) so the
12
+ * contract stays browser-safe and cheap to instantiate.
13
+ */
14
+ class LogFilter {
15
+ constructor(options) {
16
+ this.options = options;
17
+ }
18
+ }
19
+ exports.LogFilter = LogFilter;
20
+ //# sourceMappingURL=filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../../../src/filters/filter.ts"],"names":[],"mappings":";;;AAeA;;;;;;;;;GASG;AACH,MAAsB,SAAS;IAC7B,YAAsB,OAA2B;QAA3B,YAAO,GAAP,OAAO,CAAoB;IAAG,CAAC;CAItD;AALD,8BAKC"}
@@ -0,0 +1,52 @@
1
+ import type { ILogEntry } from "../index.js";
2
+ import { LogFilter, ILogFilterOptions } from "./filter.js";
3
+ export interface IWhenRepeatedOptions extends Partial<ILogFilterOptions> {
4
+ /**
5
+ * Suppression window in SECONDS. Identical entries logged within this window
6
+ * ( from the first occurrence ) are collapsed into one. Default is 10.
7
+ */
8
+ timeout?: number;
9
+ /**
10
+ * Hard cap on the number of tracked keys. The internal map is self-pruning:
11
+ * expired records are dropped first, then the oldest, so memory stays bounded
12
+ * even under many distinct messages. Default is 1024.
13
+ */
14
+ maxKeys?: number;
15
+ }
16
+ interface IRepeatRecord {
17
+ count: number;
18
+ windowStart: number;
19
+ level: number;
20
+ }
21
+ /**
22
+ * Port of NLog's WhenRepeatedFilter. Collapses identical, repeated log entries
23
+ * within a timeout window into a single emitted entry, appending a ` (xN)`
24
+ * suppressed-count when logging of that key resumes. This stops a hot loop or a
25
+ * flapping error from flooding console / file / Loki and burying real signal.
26
+ *
27
+ * Known tradeoff: there is no background timer. A residual suppressed count is
28
+ * flushed on the NEXT matching entry ( window expired or higher severity ), NOT
29
+ * when the window simply elapses with no further activity. This keeps the filter
30
+ * dependency-free; the full filter pipeline in a later phase can layer a timed
31
+ * flush on top.
32
+ */
33
+ export declare class WhenRepeatedFilter extends LogFilter {
34
+ protected now: () => number;
35
+ protected timeoutMs: number;
36
+ protected maxKeys: number;
37
+ protected records: Map<string, IRepeatRecord>;
38
+ constructor(options?: IWhenRepeatedOptions, now?: () => number);
39
+ protected keyOf(entry: ILogEntry): string;
40
+ /**
41
+ * Returns the entry to EMIT ( possibly with an injected suppressed-count ) or
42
+ * `null` to SUPPRESS.
43
+ */
44
+ apply(entry: ILogEntry): ILogEntry | null;
45
+ /**
46
+ * Keeps the tracked-key map bounded. Drops expired records first, then the
47
+ * oldest by `windowStart` until at/under the cap.
48
+ */
49
+ protected prune(now: number): void;
50
+ }
51
+ export {};
52
+ //# sourceMappingURL=whenRepeated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whenRepeated.d.ts","sourceRoot":"","sources":["../../../src/filters/whenRepeated.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,WAAW,oBAAqB,SAAQ,OAAO,CAAC,iBAAiB,CAAC;IACtE;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,aAAa;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,qBAEa,kBAAmB,SAAQ,SAAS;IAKH,SAAS,CAAC,GAAG,EAAE,MAAM,MAAM;IAJvE,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAa;gBAE9C,OAAO,CAAC,EAAE,oBAAoB,EAAY,GAAG,GAAE,MAAM,MAAyB;IAM1F,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM;IAIzC;;;OAGG;IACI,KAAK,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI;IA8BhD;;;OAGG;IACH,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;CAuBnC"}
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.WhenRepeatedFilter = void 0;
13
+ const di_1 = require("@spinajs/di");
14
+ const filter_js_1 = require("./filter.js");
15
+ /**
16
+ * Port of NLog's WhenRepeatedFilter. Collapses identical, repeated log entries
17
+ * within a timeout window into a single emitted entry, appending a ` (xN)`
18
+ * suppressed-count when logging of that key resumes. This stops a hot loop or a
19
+ * flapping error from flooding console / file / Loki and burying real signal.
20
+ *
21
+ * Known tradeoff: there is no background timer. A residual suppressed count is
22
+ * flushed on the NEXT matching entry ( window expired or higher severity ), NOT
23
+ * when the window simply elapses with no further activity. This keeps the filter
24
+ * dependency-free; the full filter pipeline in a later phase can layer a timed
25
+ * flush on top.
26
+ */
27
+ let WhenRepeatedFilter = class WhenRepeatedFilter extends filter_js_1.LogFilter {
28
+ constructor(options, now = () => Date.now()) {
29
+ super(options);
30
+ this.now = now;
31
+ this.records = new Map();
32
+ this.timeoutMs = (options?.timeout ?? 10) * 1000;
33
+ this.maxKeys = options?.maxKeys ?? 1024;
34
+ }
35
+ keyOf(entry) {
36
+ return `${entry.Level}|${entry.Variables.logger}|${entry.Variables.message}`;
37
+ }
38
+ /**
39
+ * Returns the entry to EMIT ( possibly with an injected suppressed-count ) or
40
+ * `null` to SUPPRESS.
41
+ */
42
+ apply(entry) {
43
+ const key = this.keyOf(entry);
44
+ const now = this.now();
45
+ const record = this.records.get(key);
46
+ if (!record) {
47
+ // first occurrence of this key - emit and start tracking
48
+ this.records.set(key, { count: 0, windowStart: now, level: entry.Level });
49
+ this.prune(now);
50
+ return entry;
51
+ }
52
+ if (now - record.windowStart < this.timeoutMs && entry.Level <= record.level) {
53
+ // still inside the window and not a higher severity - suppress
54
+ record.count++;
55
+ return null;
56
+ }
57
+ // window expired OR a higher-severity entry arrived - emit and reset. If we
58
+ // suppressed anything, inject the count into the ( freshly built per call,
59
+ // so safe to mutate ) message string.
60
+ if (record.count > 0) {
61
+ entry.Variables.message = `${entry.Variables.message} (x${record.count})`;
62
+ }
63
+ this.records.set(key, { count: 0, windowStart: now, level: entry.Level });
64
+ this.prune(now);
65
+ return entry;
66
+ }
67
+ /**
68
+ * Keeps the tracked-key map bounded. Drops expired records first, then the
69
+ * oldest by `windowStart` until at/under the cap.
70
+ */
71
+ prune(now) {
72
+ if (this.records.size <= this.maxKeys) {
73
+ return;
74
+ }
75
+ for (const [k, r] of this.records) {
76
+ if (now - r.windowStart >= this.timeoutMs) {
77
+ this.records.delete(k);
78
+ }
79
+ }
80
+ if (this.records.size <= this.maxKeys) {
81
+ return;
82
+ }
83
+ const byAge = [...this.records.entries()].sort((a, b) => a[1].windowStart - b[1].windowStart);
84
+ for (const [k] of byAge) {
85
+ if (this.records.size <= this.maxKeys) {
86
+ break;
87
+ }
88
+ this.records.delete(k);
89
+ }
90
+ }
91
+ };
92
+ exports.WhenRepeatedFilter = WhenRepeatedFilter;
93
+ exports.WhenRepeatedFilter = WhenRepeatedFilter = __decorate([
94
+ (0, di_1.Injectable)("WhenRepeatedFilter"),
95
+ (0, di_1.NewInstance)(),
96
+ __metadata("design:paramtypes", [Object, Function])
97
+ ], WhenRepeatedFilter);
98
+ //# sourceMappingURL=whenRepeated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whenRepeated.js","sourceRoot":"","sources":["../../../src/filters/whenRepeated.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Minimal, platform-free clone of Node's `util.format`.
3
+ *
4
+ * Supports the format specifiers the framework actually uses:
5
+ * %s - String
6
+ * %d - Number ( integer )
7
+ * %i - parseInt
8
+ * %f - parseFloat
9
+ * %j - JSON
10
+ * %o - Object ( JSON.stringify )
11
+ * %O - Object ( JSON.stringify )
12
+ * %% - literal percent sign ( consumes no argument )
13
+ *
14
+ * Any arguments left over after the specifiers are consumed are appended,
15
+ * space-separated. Objects are rendered via JSON.stringify ( circular refs
16
+ * fall back to String() ). This avoids pulling in Node's `util` module so the
17
+ * logger is usable in the browser.
18
+ */
19
+ export declare function format(f?: any, ...args: any[]): string;
20
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/format.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,CA0CtD"}