@webpieces/winston 0.4.395 → 0.4.397

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/README.md CHANGED
@@ -16,26 +16,37 @@ Two factories, both auto-enriching every line with the logged context keys regis
16
16
  ## Usage
17
17
 
18
18
  ```ts
19
- import { LogManager, HeaderRegistry } from '@webpieces/core-util';
19
+ import { ServiceInfo } from '@webpieces/core-util';
20
20
  import { WinstonGcpFactory, WinstonConsoleFactory } from '@webpieces/winston';
21
- import { RequestContextReader } from '@webpieces/core-context';
22
21
 
23
- const reader = new RequestContextReader();
22
+ // FIRST: identify this service. Both factories read name+version in their CONSTRUCTOR, so this
23
+ // must come before you build one — a forgotten call throws at startup rather than shipping logs
24
+ // that cannot say which build emitted them.
25
+ ServiceInfo.setInfo('my-service', '2.1.0');
26
+
24
27
  const loggerFactory = process.env.K_SERVICE
25
- ? new WinstonGcpFactory(reader)
26
- : new WinstonConsoleFactory(reader);
28
+ ? new WinstonGcpFactory()
29
+ : new WinstonConsoleFactory();
27
30
 
28
31
  // Typically you pass loggerFactory to setupRuntime(new RuntimeSetupOptions(loggerFactory, ...)),
29
32
  // which calls HeaderRegistry.configure(...) then LogManager.setFactory(loggerFactory) for you.
30
33
  ```
31
34
 
32
- The `ContextReader` is a **constructor argument** (the node `RequestContextReader` lives in
33
- `@webpieces/core-context`) so this package depends only on `@webpieces/core-util` not on
34
- any node context package.
35
+ Both factories read the magic context **directly** from `RequestContext` on each line, so
36
+ nothing is threaded in: there is no `ContextReader` constructor argument.
35
37
 
36
38
  ## Options
37
39
 
38
- `new WinstonGcpFactory(reader, new WinstonFactoryOptions(level, svcGitHash))`:
39
-
40
- - `level` minimum webpieces level to emit (default `'info'`). `trace` maps to winston `silly`.
41
- - `svcGitHash` when set, every line carries `jsonPayload.svcGitHash` (deployment filter).
40
+ There are none both factories take no arguments.
41
+
42
+ - **Service name + version** from `ServiceInfo.setInfo(...)` (see above), NOT factory options.
43
+ Every line carries `svcName` and `version`. They live in `@webpieces/core-util` because they
44
+ are facts about the SERVICE, not about winston: the bunyan backend reads the same values, and
45
+ `requestIdSource` reads the name (it records which service minted a request-id).
46
+ - **`version` is opaque** — a git SHA, a semver tag, a CI build number, whatever identifies your
47
+ build. webpieces neither parses nor derives it; your app decides where it comes from.
48
+ - **Local rendering** — neither `svcName` nor `version` renders in `WinstonConsoleFactory` output.
49
+ They earn their keep in GCP (filtering across many services and deploys); locally each service
50
+ logs to its own place and you can check git yourself, so on every line they are just noise.
51
+ - **Level** — there is deliberately no knob. webpieces does not filter by level; winston filters
52
+ at its own default.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/winston",
3
- "version": "0.4.395",
3
+ "version": "0.4.397",
4
4
  "description": "Node-only winston LoggerFactory backends for webpieces: Console (local pretty) + GCP (Cloud Run stdout JSON), auto-enriched with HeaderRegistry context keys",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -23,8 +23,8 @@
23
23
  "access": "public"
24
24
  },
25
25
  "dependencies": {
26
- "@webpieces/core-util": "0.4.395",
27
- "@webpieces/core-context": "0.4.395",
26
+ "@webpieces/core-util": "0.4.397",
27
+ "@webpieces/core-context": "0.4.397",
28
28
  "winston": "3.11.0",
29
29
  "logform": "2.7.0",
30
30
  "safe-stable-stringify": "2.5.0"
@@ -0,0 +1,48 @@
1
+ import { transports } from 'winston';
2
+ import type { TransformableInfo } from 'logform';
3
+ /**
4
+ * ChunkingConsoleTransport - a winston Console transport that SPLITS an oversized record into
5
+ * several complete records instead of letting GCP silently drop it.
6
+ *
7
+ * WHY A TRANSPORT, not the WinstonLogger wrapper — two reasons, both decisive:
8
+ *
9
+ * 1. COVERAGE. WinstonFactoryBase sets `handleExceptions: true` / `handleRejections: true`, and
10
+ * those lines are emitted by winston itself, bypassing WinstonLogger entirely. An uncaught
11
+ * exception carrying a huge stack trace is exactly the log you cannot afford to lose, so the
12
+ * guard has to sit below the wrapper.
13
+ * 2. EXACT MEASUREMENT. A transport runs AFTER the format chain, so `info[MESSAGE]` is the fully
14
+ * rendered line — envelope and all (severity, svcName, requestId, tenantId, the `api` tag). We
15
+ * measure the real thing rather than estimating the caller's contribution and hoping.
16
+ *
17
+ * WHY IT RE-SERIALIZES rather than slicing `info[MESSAGE]`: a fragment of a JSON line is not valid
18
+ * JSON, so the logging agent would file each piece as an unparsed `textPayload` and every structured
19
+ * field would be lost. Each emitted piece must be a COMPLETE, parseable record — so we chunk the
20
+ * oversized FIELDS and rebuild N records, each tagged with a shared {@link LogChunkInfo}.
21
+ *
22
+ * GCP-ONLY: wired in by WinstonGcpFactory. WinstonConsoleFactory keeps a plain Console transport —
23
+ * a dev terminal has no size limit and splitting there would only hurt readability.
24
+ *
25
+ * The common case is untouched: a record within budget goes straight to `super.log` and is
26
+ * byte-identical to what it was before this class existed.
27
+ */
28
+ export declare class ChunkingConsoleTransport extends transports.Console {
29
+ private readonly budgetBytes;
30
+ constructor(budgetBytes?: number);
31
+ log(info: TransformableInfo, callback: () => void): void;
32
+ /**
33
+ * Hand one finished record to the real Console transport. winston types `log` as optional on the
34
+ * base, so we resolve it once here rather than sprinkling `?.` at the call sites — and if it were
35
+ * ever truly absent we still fire the callback, because swallowing it would hang the logger.
36
+ */
37
+ private writeThrough;
38
+ /** Split the oversized record's fields and emit one complete record per piece. */
39
+ private logChunked;
40
+ /**
41
+ * Rebuild one complete record: every original field, with `message`/`errStack` replaced by this
42
+ * piece and a `logChunk` tag added, re-serialized exactly the way format.json() would (same
43
+ * safe-stable-stringify, so circular refs stay "[Circular]").
44
+ */
45
+ private buildRecord;
46
+ /** The fully-rendered line the format chain produced (what a transport writes). */
47
+ private rendered;
48
+ }
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ChunkingConsoleTransport = void 0;
4
+ const winston_1 = require("winston");
5
+ const safe_stable_stringify_1 = require("safe-stable-stringify");
6
+ const core_util_1 = require("@webpieces/core-util");
7
+ // winston's own symbol (not a DI token): it holds the FINAL rendered line — what format.json()
8
+ // produced and what a transport actually writes — as distinct from the `message` string PROPERTY,
9
+ // which is the caller's text. We need both: the symbol to measure, the property to re-chunk.
10
+ // webpieces-disable no-symbol-di-tokens -- winston's documented internal record key (Symbol.for('message')), not a DI token
11
+ const MESSAGE = Symbol.for('message');
12
+ /**
13
+ * ChunkingConsoleTransport - a winston Console transport that SPLITS an oversized record into
14
+ * several complete records instead of letting GCP silently drop it.
15
+ *
16
+ * WHY A TRANSPORT, not the WinstonLogger wrapper — two reasons, both decisive:
17
+ *
18
+ * 1. COVERAGE. WinstonFactoryBase sets `handleExceptions: true` / `handleRejections: true`, and
19
+ * those lines are emitted by winston itself, bypassing WinstonLogger entirely. An uncaught
20
+ * exception carrying a huge stack trace is exactly the log you cannot afford to lose, so the
21
+ * guard has to sit below the wrapper.
22
+ * 2. EXACT MEASUREMENT. A transport runs AFTER the format chain, so `info[MESSAGE]` is the fully
23
+ * rendered line — envelope and all (severity, svcName, requestId, tenantId, the `api` tag). We
24
+ * measure the real thing rather than estimating the caller's contribution and hoping.
25
+ *
26
+ * WHY IT RE-SERIALIZES rather than slicing `info[MESSAGE]`: a fragment of a JSON line is not valid
27
+ * JSON, so the logging agent would file each piece as an unparsed `textPayload` and every structured
28
+ * field would be lost. Each emitted piece must be a COMPLETE, parseable record — so we chunk the
29
+ * oversized FIELDS and rebuild N records, each tagged with a shared {@link LogChunkInfo}.
30
+ *
31
+ * GCP-ONLY: wired in by WinstonGcpFactory. WinstonConsoleFactory keeps a plain Console transport —
32
+ * a dev terminal has no size limit and splitting there would only hurt readability.
33
+ *
34
+ * The common case is untouched: a record within budget goes straight to `super.log` and is
35
+ * byte-identical to what it was before this class existed.
36
+ */
37
+ class ChunkingConsoleTransport extends winston_1.transports.Console {
38
+ budgetBytes;
39
+ constructor(budgetBytes = core_util_1.GCP_LOG_BUDGET_BYTES) {
40
+ super();
41
+ this.budgetBytes = budgetBytes;
42
+ }
43
+ log(info, callback) {
44
+ const rendered = this.rendered(info);
45
+ if (core_util_1.LogChunker.byteLength(rendered) <= this.budgetBytes) {
46
+ // The overwhelmingly common path — unchanged behaviour, no extra work beyond one measure.
47
+ this.writeThrough(info, callback);
48
+ return;
49
+ }
50
+ this.logChunked(info, rendered, callback);
51
+ }
52
+ /**
53
+ * Hand one finished record to the real Console transport. winston types `log` as optional on the
54
+ * base, so we resolve it once here rather than sprinkling `?.` at the call sites — and if it were
55
+ * ever truly absent we still fire the callback, because swallowing it would hang the logger.
56
+ */
57
+ writeThrough(info, callback) {
58
+ const parentLog = super.log;
59
+ if (!parentLog) {
60
+ callback();
61
+ return;
62
+ }
63
+ parentLog.call(this, info, callback);
64
+ }
65
+ /** Split the oversized record's fields and emit one complete record per piece. */
66
+ logChunked(info, rendered, callback) {
67
+ const message = typeof info.message === 'string' ? info.message : String(info.message ?? '');
68
+ const stack = typeof info['errStack'] === 'string' ? info['errStack'] : undefined;
69
+ const budgets = core_util_1.LogChunker.chunkBudgets(core_util_1.LogChunker.byteLength(rendered), this.budgetBytes, message, stack ?? '');
70
+ const messageChunks = core_util_1.LogChunker.chunk(message, budgets.firstBudget);
71
+ const stackChunks = core_util_1.LogChunker.chunk(stack ?? '', budgets.secondBudget);
72
+ const uid = core_util_1.LogChunker.newUid();
73
+ const total = Math.max(messageChunks.length, stackChunks.length);
74
+ for (let index = 0; index < total; index++) {
75
+ const piece = this.buildRecord(info, messageChunks[index] ?? '', stack === undefined ? undefined : (stackChunks[index] ?? ''), new core_util_1.LogChunkInfo(uid, index, total));
76
+ // Only the LAST piece completes the write; winston expects exactly one callback per log().
77
+ this.writeThrough(piece, index === total - 1 ? callback : () => undefined);
78
+ }
79
+ }
80
+ /**
81
+ * Rebuild one complete record: every original field, with `message`/`errStack` replaced by this
82
+ * piece and a `logChunk` tag added, re-serialized exactly the way format.json() would (same
83
+ * safe-stable-stringify, so circular refs stay "[Circular]").
84
+ */
85
+ buildRecord(info, messageChunk, stackChunk, chunkInfo) {
86
+ const fields = {};
87
+ // Object.keys skips winston's symbol keys, so this is exactly the set format.json() serializes.
88
+ for (const key of Object.keys(info)) {
89
+ fields[key] = info[key];
90
+ }
91
+ fields['message'] = messageChunk;
92
+ if (stackChunk !== undefined) {
93
+ fields['errStack'] = stackChunk;
94
+ }
95
+ fields['logChunk'] = chunkInfo;
96
+ // Object.assign copies own enumerable SYMBOL keys too, so winston's LEVEL symbol (which the
97
+ // Console transport reads) survives; then we overwrite the rendered line with this piece's.
98
+ const piece = Object.assign({}, info, fields);
99
+ piece[MESSAGE] = (0, safe_stable_stringify_1.stringify)(fields) ?? '';
100
+ return piece;
101
+ }
102
+ /** The fully-rendered line the format chain produced (what a transport writes). */
103
+ rendered(info) {
104
+ const message = info[MESSAGE];
105
+ return typeof message === 'string' ? message : String(message ?? '');
106
+ }
107
+ }
108
+ exports.ChunkingConsoleTransport = ChunkingConsoleTransport;
109
+ //# sourceMappingURL=ChunkingConsoleTransport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChunkingConsoleTransport.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/ChunkingConsoleTransport.ts"],"names":[],"mappings":"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","sourcesContent":["import { transports } from 'winston';\nimport type { TransformableInfo } from 'logform';\nimport { stringify as safeStringify } from 'safe-stable-stringify';\nimport { GCP_LOG_BUDGET_BYTES, LogChunker, LogChunkInfo } from '@webpieces/core-util';\n\n// winston's own symbol (not a DI token): it holds the FINAL rendered line — what format.json()\n// produced and what a transport actually writes — as distinct from the `message` string PROPERTY,\n// which is the caller's text. We need both: the symbol to measure, the property to re-chunk.\n// webpieces-disable no-symbol-di-tokens -- winston's documented internal record key (Symbol.for('message')), not a DI token\nconst MESSAGE = Symbol.for('message');\n\n// JSON-shaped value (the widest thing a winston record field can hold), used instead of\n// `any`/`unknown` which the code rules disallow. Mirrors the JsonValue in format.ts.\ntype JsonValue = string | number | boolean | bigint | object | null | undefined;\n\n/**\n * ChunkingConsoleTransport - a winston Console transport that SPLITS an oversized record into\n * several complete records instead of letting GCP silently drop it.\n *\n * WHY A TRANSPORT, not the WinstonLogger wrapper — two reasons, both decisive:\n *\n * 1. COVERAGE. WinstonFactoryBase sets `handleExceptions: true` / `handleRejections: true`, and\n * those lines are emitted by winston itself, bypassing WinstonLogger entirely. An uncaught\n * exception carrying a huge stack trace is exactly the log you cannot afford to lose, so the\n * guard has to sit below the wrapper.\n * 2. EXACT MEASUREMENT. A transport runs AFTER the format chain, so `info[MESSAGE]` is the fully\n * rendered line — envelope and all (severity, svcName, requestId, tenantId, the `api` tag). We\n * measure the real thing rather than estimating the caller's contribution and hoping.\n *\n * WHY IT RE-SERIALIZES rather than slicing `info[MESSAGE]`: a fragment of a JSON line is not valid\n * JSON, so the logging agent would file each piece as an unparsed `textPayload` and every structured\n * field would be lost. Each emitted piece must be a COMPLETE, parseable record — so we chunk the\n * oversized FIELDS and rebuild N records, each tagged with a shared {@link LogChunkInfo}.\n *\n * GCP-ONLY: wired in by WinstonGcpFactory. WinstonConsoleFactory keeps a plain Console transport —\n * a dev terminal has no size limit and splitting there would only hurt readability.\n *\n * The common case is untouched: a record within budget goes straight to `super.log` and is\n * byte-identical to what it was before this class existed.\n */\nexport class ChunkingConsoleTransport extends transports.Console {\n private readonly budgetBytes: number;\n\n constructor(budgetBytes: number = GCP_LOG_BUDGET_BYTES) {\n super();\n this.budgetBytes = budgetBytes;\n }\n\n override log(info: TransformableInfo, callback: () => void): void {\n const rendered = this.rendered(info);\n if (LogChunker.byteLength(rendered) <= this.budgetBytes) {\n // The overwhelmingly common path — unchanged behaviour, no extra work beyond one measure.\n this.writeThrough(info, callback);\n return;\n }\n this.logChunked(info, rendered, callback);\n }\n\n /**\n * Hand one finished record to the real Console transport. winston types `log` as optional on the\n * base, so we resolve it once here rather than sprinkling `?.` at the call sites — and if it were\n * ever truly absent we still fire the callback, because swallowing it would hang the logger.\n */\n private writeThrough(info: TransformableInfo, callback: () => void): void {\n const parentLog = super.log;\n if (!parentLog) {\n callback();\n return;\n }\n parentLog.call(this, info, callback);\n }\n\n /** Split the oversized record's fields and emit one complete record per piece. */\n private logChunked(info: TransformableInfo, rendered: string, callback: () => void): void {\n const message = typeof info.message === 'string' ? info.message : String(info.message ?? '');\n const stack = typeof info['errStack'] === 'string' ? info['errStack'] : undefined;\n\n const budgets = LogChunker.chunkBudgets(\n LogChunker.byteLength(rendered), this.budgetBytes, message, stack ?? '',\n );\n const messageChunks = LogChunker.chunk(message, budgets.firstBudget);\n const stackChunks = LogChunker.chunk(stack ?? '', budgets.secondBudget);\n\n const uid = LogChunker.newUid();\n const total = Math.max(messageChunks.length, stackChunks.length);\n for (let index = 0; index < total; index++) {\n const piece = this.buildRecord(\n info,\n messageChunks[index] ?? '',\n stack === undefined ? undefined : (stackChunks[index] ?? ''),\n new LogChunkInfo(uid, index, total),\n );\n // Only the LAST piece completes the write; winston expects exactly one callback per log().\n this.writeThrough(piece, index === total - 1 ? callback : (): void => undefined);\n }\n }\n\n /**\n * Rebuild one complete record: every original field, with `message`/`errStack` replaced by this\n * piece and a `logChunk` tag added, re-serialized exactly the way format.json() would (same\n * safe-stable-stringify, so circular refs stay \"[Circular]\").\n */\n private buildRecord(\n info: TransformableInfo,\n messageChunk: string,\n stackChunk: string | undefined,\n chunkInfo: LogChunkInfo,\n ): TransformableInfo {\n const fields: Record<string, JsonValue> = {};\n // Object.keys skips winston's symbol keys, so this is exactly the set format.json() serializes.\n for (const key of Object.keys(info)) {\n fields[key] = (info as Record<string, JsonValue>)[key];\n }\n fields['message'] = messageChunk;\n if (stackChunk !== undefined) {\n fields['errStack'] = stackChunk;\n }\n fields['logChunk'] = chunkInfo;\n\n // Object.assign copies own enumerable SYMBOL keys too, so winston's LEVEL symbol (which the\n // Console transport reads) survives; then we overwrite the rendered line with this piece's.\n const piece = Object.assign({}, info, fields) as TransformableInfo;\n (piece as Record<symbol, JsonValue>)[MESSAGE] = safeStringify(fields) ?? '';\n return piece;\n }\n\n /** The fully-rendered line the format chain produced (what a transport writes). */\n private rendered(info: TransformableInfo): string {\n const message = (info as Record<symbol, JsonValue>)[MESSAGE];\n return typeof message === 'string' ? message : String(message ?? '');\n }\n}\n"]}
@@ -1,11 +1,15 @@
1
1
  import { WinstonFactoryBase } from './WinstonFactoryBase';
2
- import { WinstonFactoryOptions } from './WinstonFactoryOptions';
3
2
  /**
4
3
  * WinstonConsoleFactory - the LOCAL developer backend. Colorized single-line
5
4
  * pretty console output with the registered context keys as a bracketed prefix,
6
5
  * for human reading — same enrichment as the GCP backend, different rendering.
7
6
  * Matches the tested onetablet/monorepo-nx1 local logger.
7
+ *
8
+ * The service name + version come from {@link ServiceInfo} (this constructor reads them), but
9
+ * neither RENDERS locally: you already know which service you are running and can check git
10
+ * yourself, so they would be noise on every line. They still ship to GCP via the sibling
11
+ * {@link WinstonGcpFactory}. See LOCAL_STRUCTURAL_KEYS in ./format.
8
12
  */
9
13
  export declare class WinstonConsoleFactory extends WinstonFactoryBase {
10
- constructor(opts?: WinstonFactoryOptions);
14
+ constructor();
11
15
  }
@@ -3,17 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WinstonConsoleFactory = void 0;
4
4
  const winston_1 = require("winston");
5
5
  const WinstonFactoryBase_1 = require("./WinstonFactoryBase");
6
- const WinstonFactoryOptions_1 = require("./WinstonFactoryOptions");
7
6
  const format_1 = require("./format");
8
7
  /**
9
8
  * WinstonConsoleFactory - the LOCAL developer backend. Colorized single-line
10
9
  * pretty console output with the registered context keys as a bracketed prefix,
11
10
  * for human reading — same enrichment as the GCP backend, different rendering.
12
11
  * Matches the tested onetablet/monorepo-nx1 local logger.
12
+ *
13
+ * The service name + version come from {@link ServiceInfo} (this constructor reads them), but
14
+ * neither RENDERS locally: you already know which service you are running and can check git
15
+ * yourself, so they would be noise on every line. They still ship to GCP via the sibling
16
+ * {@link WinstonGcpFactory}. See LOCAL_STRUCTURAL_KEYS in ./format.
13
17
  */
14
18
  class WinstonConsoleFactory extends WinstonFactoryBase_1.WinstonFactoryBase {
15
- constructor(opts = new WinstonFactoryOptions_1.WinstonFactoryOptions()) {
16
- super(winston_1.format.combine((0, format_1.bigIntSafeFormat)(), (0, format_1.injectContextFormat)(), (0, format_1.severityFormat)(), winston_1.format.colorize(), (0, format_1.localPrettyFormat)()), opts);
19
+ constructor() {
20
+ super(winston_1.format.combine((0, format_1.bigIntSafeFormat)(), (0, format_1.injectContextFormat)(), (0, format_1.severityFormat)(), winston_1.format.colorize(), (0, format_1.localPrettyFormat)()));
17
21
  }
18
22
  }
19
23
  exports.WinstonConsoleFactory = WinstonConsoleFactory;
@@ -1 +1 @@
1
- {"version":3,"file":"WinstonConsoleFactory.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonConsoleFactory.ts"],"names":[],"mappings":";;;AAAA,qCAAiC;AACjC,6DAA0D;AAC1D,mEAAgE;AAChE,qCAAoG;AAEpG;;;;;GAKG;AACH,MAAa,qBAAsB,SAAQ,uCAAkB;IACzD,YAAY,OAA8B,IAAI,6CAAqB,EAAE;QACjE,KAAK,CACD,gBAAM,CAAC,OAAO,CACV,IAAA,yBAAgB,GAAE,EAClB,IAAA,4BAAmB,GAAE,EACrB,IAAA,uBAAc,GAAE,EAChB,gBAAM,CAAC,QAAQ,EAAE,EACjB,IAAA,0BAAiB,GAAE,CACtB,EACD,IAAI,CACP,CAAC;IACN,CAAC;CACJ;AAbD,sDAaC","sourcesContent":["import { format } from 'winston';\nimport { WinstonFactoryBase } from './WinstonFactoryBase';\nimport { WinstonFactoryOptions } from './WinstonFactoryOptions';\nimport { bigIntSafeFormat, injectContextFormat, localPrettyFormat, severityFormat } from './format';\n\n/**\n * WinstonConsoleFactory - the LOCAL developer backend. Colorized single-line\n * pretty console output with the registered context keys as a bracketed prefix,\n * for human reading — same enrichment as the GCP backend, different rendering.\n * Matches the tested onetablet/monorepo-nx1 local logger.\n */\nexport class WinstonConsoleFactory extends WinstonFactoryBase {\n constructor(opts: WinstonFactoryOptions = new WinstonFactoryOptions()) {\n super(\n format.combine(\n bigIntSafeFormat(),\n injectContextFormat(),\n severityFormat(),\n format.colorize(),\n localPrettyFormat(),\n ),\n opts,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"WinstonConsoleFactory.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonConsoleFactory.ts"],"names":[],"mappings":";;;AAAA,qCAAiC;AACjC,6DAA0D;AAC1D,qCAAoG;AAEpG;;;;;;;;;;GAUG;AACH,MAAa,qBAAsB,SAAQ,uCAAkB;IACzD;QACI,KAAK,CACD,gBAAM,CAAC,OAAO,CACV,IAAA,yBAAgB,GAAE,EAClB,IAAA,4BAAmB,GAAE,EACrB,IAAA,uBAAc,GAAE,EAChB,gBAAM,CAAC,QAAQ,EAAE,EACjB,IAAA,0BAAiB,GAAE,CACtB,CACJ,CAAC;IACN,CAAC;CACJ;AAZD,sDAYC","sourcesContent":["import { format } from 'winston';\nimport { WinstonFactoryBase } from './WinstonFactoryBase';\nimport { bigIntSafeFormat, injectContextFormat, localPrettyFormat, severityFormat } from './format';\n\n/**\n * WinstonConsoleFactory - the LOCAL developer backend. Colorized single-line\n * pretty console output with the registered context keys as a bracketed prefix,\n * for human reading — same enrichment as the GCP backend, different rendering.\n * Matches the tested onetablet/monorepo-nx1 local logger.\n *\n * The service name + version come from {@link ServiceInfo} (this constructor reads them), but\n * neither RENDERS locally: you already know which service you are running and can check git\n * yourself, so they would be noise on every line. They still ship to GCP via the sibling\n * {@link WinstonGcpFactory}. See LOCAL_STRUCTURAL_KEYS in ./format.\n */\nexport class WinstonConsoleFactory extends WinstonFactoryBase {\n constructor() {\n super(\n format.combine(\n bigIntSafeFormat(),\n injectContextFormat(),\n severityFormat(),\n format.colorize(),\n localPrettyFormat(),\n ),\n );\n }\n}\n"]}
@@ -1,6 +1,6 @@
1
+ import type Transport from 'winston-transport';
1
2
  import type { Format } from 'logform';
2
3
  import type { Logger, LoggerFactory } from '@webpieces/core-util';
3
- import { WinstonFactoryOptions } from './WinstonFactoryOptions';
4
4
  /**
5
5
  * WinstonFactoryBase - shared plumbing for the winston {@link LoggerFactory}
6
6
  * backends. Builds ONE underlying winston logger (a single `Console` transport,
@@ -8,15 +8,22 @@ import { WinstonFactoryOptions } from './WinstonFactoryOptions';
8
8
  * out a cached {@link WinstonLogger} per name (each a winston child carrying
9
9
  * `loggerName`). Subclasses differ only in the format stack they pass up.
10
10
  *
11
- * Every line carries `svcName` from {@link ServiceInfo}. Winston, unlike bunyan, has
12
- * NO mandatory logger name, so this backend used to emit no service name at all — a
13
- * winston service was distinguishable only by GCP's own resource labels. Now both
14
- * backends read the SAME {@link ServiceInfo}, so naming is a property of the service
15
- * rather than of the logging library the app happened to pick.
11
+ * Every line carries `svcName` + `version` from {@link ServiceInfo}. Neither used to be a property
12
+ * of the SERVICE: winston has no mandatory logger name (so this backend emitted none — a winston
13
+ * service was distinguishable only by GCP's own resource labels), and the version lived here as an
14
+ * optional `svcGitHash` factory option that bunyan had no counterpart for. Both now come from the
15
+ * ONE {@link ServiceInfo}, so the fields on your logs no longer depend on which logging library the
16
+ * app happened to pick. `version` is opaque — whatever string the app used to identify its build.
16
17
  */
17
18
  export declare abstract class WinstonFactoryBase implements LoggerFactory {
18
19
  private readonly base;
19
20
  private readonly loggers;
20
- protected constructor(finalFormat: Format, opts: WinstonFactoryOptions);
21
+ /**
22
+ * @param transport - the sink to write through. Defaults to a plain Console; the GCP subclass
23
+ * passes a {@link ChunkingConsoleTransport} instead, because only there does a per-entry size
24
+ * limit exist. Taking the whole transport (rather than a size knob) keeps the size limit a fact
25
+ * about the SINK, which is where it actually lives — a dev terminal has no such limit.
26
+ */
27
+ protected constructor(finalFormat: Format, transport?: Transport);
21
28
  getLogger(name: string): Logger;
22
29
  }
@@ -11,27 +11,34 @@ const WinstonLogger_1 = require("./WinstonLogger");
11
11
  * out a cached {@link WinstonLogger} per name (each a winston child carrying
12
12
  * `loggerName`). Subclasses differ only in the format stack they pass up.
13
13
  *
14
- * Every line carries `svcName` from {@link ServiceInfo}. Winston, unlike bunyan, has
15
- * NO mandatory logger name, so this backend used to emit no service name at all — a
16
- * winston service was distinguishable only by GCP's own resource labels. Now both
17
- * backends read the SAME {@link ServiceInfo}, so naming is a property of the service
18
- * rather than of the logging library the app happened to pick.
14
+ * Every line carries `svcName` + `version` from {@link ServiceInfo}. Neither used to be a property
15
+ * of the SERVICE: winston has no mandatory logger name (so this backend emitted none — a winston
16
+ * service was distinguishable only by GCP's own resource labels), and the version lived here as an
17
+ * optional `svcGitHash` factory option that bunyan had no counterpart for. Both now come from the
18
+ * ONE {@link ServiceInfo}, so the fields on your logs no longer depend on which logging library the
19
+ * app happened to pick. `version` is opaque — whatever string the app used to identify its build.
19
20
  */
20
21
  class WinstonFactoryBase {
21
22
  base;
22
23
  loggers = new Map();
23
- constructor(finalFormat, opts) {
24
- // Read at STARTUP (this ctor runs while booting), so a forgotten ServiceInfo.setName(...)
25
- // fails the deploy rather than shipping unnamed logs.
26
- const defaultMeta = { svcName: core_util_1.ServiceInfo.getName() };
27
- if (opts.svcGitHash) {
28
- defaultMeta['svcGitHash'] = opts.svcGitHash;
29
- }
24
+ /**
25
+ * @param transport - the sink to write through. Defaults to a plain Console; the GCP subclass
26
+ * passes a {@link ChunkingConsoleTransport} instead, because only there does a per-entry size
27
+ * limit exist. Taking the whole transport (rather than a size knob) keeps the size limit a fact
28
+ * about the SINK, which is where it actually lives — a dev terminal has no such limit.
29
+ */
30
+ constructor(finalFormat, transport) {
31
+ // Read at STARTUP (this ctor runs while booting), so a forgotten ServiceInfo.setInfo(...)
32
+ // fails the deploy rather than shipping logs that cannot say which build emitted them.
33
+ const defaultMeta = {
34
+ svcName: core_util_1.ServiceInfo.getName(),
35
+ version: core_util_1.ServiceInfo.getVersion(),
36
+ };
30
37
  // No level set — we do NOT filter; that is winston's job (defaults to 'info').
31
38
  this.base = (0, winston_1.createLogger)({
32
39
  format: finalFormat,
33
40
  defaultMeta: defaultMeta,
34
- transports: [new winston_1.transports.Console()],
41
+ transports: [transport ?? new winston_1.transports.Console()],
35
42
  handleExceptions: true,
36
43
  handleRejections: true,
37
44
  });
@@ -1 +1 @@
1
- {"version":3,"file":"WinstonFactoryBase.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonFactoryBase.ts"],"names":[],"mappings":";;;AAAA,qCAAmD;AAInD,oDAAmD;AACnD,mDAAgD;AAGhD;;;;;;;;;;;;GAYG;AACH,MAAsB,kBAAkB;IACnB,IAAI,CAAc;IAClB,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAErD,YAAsB,WAAmB,EAAE,IAA2B;QAClE,0FAA0F;QAC1F,sDAAsD;QACtD,MAAM,WAAW,GAA2B,EAAE,OAAO,EAAE,uBAAW,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/E,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,WAAW,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC;QAChD,CAAC;QAED,+EAA+E;QAC/E,IAAI,CAAC,IAAI,GAAG,IAAA,sBAAY,EAAC;YACrB,MAAM,EAAE,WAAW;YACnB,WAAW,EAAE,WAAW;YACxB,UAAU,EAAE,CAAC,IAAI,oBAAU,CAAC,OAAO,EAAE,CAAC;YACtC,gBAAgB,EAAE,IAAI;YACtB,gBAAgB,EAAE,IAAI;SACzB,CAAC,CAAC;IACP,CAAC;IAED,SAAS,CAAC,IAAY;QAClB,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,MAAM,GAAG,IAAI,6BAAa,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAClE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ;AA9BD,gDA8BC","sourcesContent":["import { createLogger, transports } from 'winston';\nimport type { Logger as WinstonBase } from 'winston';\nimport type { Format } from 'logform';\nimport type { Logger, LoggerFactory } from '@webpieces/core-util';\nimport { ServiceInfo } from '@webpieces/core-util';\nimport { WinstonLogger } from './WinstonLogger';\nimport { WinstonFactoryOptions } from './WinstonFactoryOptions';\n\n/**\n * WinstonFactoryBase - shared plumbing for the winston {@link LoggerFactory}\n * backends. Builds ONE underlying winston logger (a single `Console` transport,\n * handleExceptions/Rejections on) with the caller-chosen format stack, then hands\n * out a cached {@link WinstonLogger} per name (each a winston child carrying\n * `loggerName`). Subclasses differ only in the format stack they pass up.\n *\n * Every line carries `svcName` from {@link ServiceInfo}. Winston, unlike bunyan, has\n * NO mandatory logger name, so this backend used to emit no service name at all — a\n * winston service was distinguishable only by GCP's own resource labels. Now both\n * backends read the SAME {@link ServiceInfo}, so naming is a property of the service\n * rather than of the logging library the app happened to pick.\n */\nexport abstract class WinstonFactoryBase implements LoggerFactory {\n private readonly base: WinstonBase;\n private readonly loggers = new Map<string, Logger>();\n\n protected constructor(finalFormat: Format, opts: WinstonFactoryOptions) {\n // Read at STARTUP (this ctor runs while booting), so a forgotten ServiceInfo.setName(...)\n // fails the deploy rather than shipping unnamed logs.\n const defaultMeta: Record<string, string> = { svcName: ServiceInfo.getName() };\n if (opts.svcGitHash) {\n defaultMeta['svcGitHash'] = opts.svcGitHash;\n }\n\n // No level set — we do NOT filter; that is winston's job (defaults to 'info').\n this.base = createLogger({\n format: finalFormat,\n defaultMeta: defaultMeta,\n transports: [new transports.Console()],\n handleExceptions: true,\n handleRejections: true,\n });\n }\n\n getLogger(name: string): Logger {\n let logger = this.loggers.get(name);\n if (!logger) {\n logger = new WinstonLogger(this.base.child({ loggerName: name }));\n this.loggers.set(name, logger);\n }\n return logger;\n }\n}\n"]}
1
+ {"version":3,"file":"WinstonFactoryBase.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonFactoryBase.ts"],"names":[],"mappings":";;;AAAA,qCAAmD;AAKnD,oDAAmD;AACnD,mDAAgD;AAEhD;;;;;;;;;;;;;GAaG;AACH,MAAsB,kBAAkB;IACnB,IAAI,CAAc;IAClB,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAErD;;;;;OAKG;IACH,YAAsB,WAAmB,EAAE,SAAqB;QAC5D,0FAA0F;QAC1F,uFAAuF;QACvF,MAAM,WAAW,GAA2B;YACxC,OAAO,EAAE,uBAAW,CAAC,OAAO,EAAE;YAC9B,OAAO,EAAE,uBAAW,CAAC,UAAU,EAAE;SACpC,CAAC;QAEF,+EAA+E;QAC/E,IAAI,CAAC,IAAI,GAAG,IAAA,sBAAY,EAAC;YACrB,MAAM,EAAE,WAAW;YACnB,WAAW,EAAE,WAAW;YACxB,UAAU,EAAE,CAAC,SAAS,IAAI,IAAI,oBAAU,CAAC,OAAO,EAAE,CAAC;YACnD,gBAAgB,EAAE,IAAI;YACtB,gBAAgB,EAAE,IAAI;SACzB,CAAC,CAAC;IACP,CAAC;IAED,SAAS,CAAC,IAAY;QAClB,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,MAAM,GAAG,IAAI,6BAAa,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAClE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ;AApCD,gDAoCC","sourcesContent":["import { createLogger, transports } from 'winston';\nimport type { Logger as WinstonBase } from 'winston';\nimport type Transport from 'winston-transport';\nimport type { Format } from 'logform';\nimport type { Logger, LoggerFactory } from '@webpieces/core-util';\nimport { ServiceInfo } from '@webpieces/core-util';\nimport { WinstonLogger } from './WinstonLogger';\n\n/**\n * WinstonFactoryBase - shared plumbing for the winston {@link LoggerFactory}\n * backends. Builds ONE underlying winston logger (a single `Console` transport,\n * handleExceptions/Rejections on) with the caller-chosen format stack, then hands\n * out a cached {@link WinstonLogger} per name (each a winston child carrying\n * `loggerName`). Subclasses differ only in the format stack they pass up.\n *\n * Every line carries `svcName` + `version` from {@link ServiceInfo}. Neither used to be a property\n * of the SERVICE: winston has no mandatory logger name (so this backend emitted none — a winston\n * service was distinguishable only by GCP's own resource labels), and the version lived here as an\n * optional `svcGitHash` factory option that bunyan had no counterpart for. Both now come from the\n * ONE {@link ServiceInfo}, so the fields on your logs no longer depend on which logging library the\n * app happened to pick. `version` is opaque — whatever string the app used to identify its build.\n */\nexport abstract class WinstonFactoryBase implements LoggerFactory {\n private readonly base: WinstonBase;\n private readonly loggers = new Map<string, Logger>();\n\n /**\n * @param transport - the sink to write through. Defaults to a plain Console; the GCP subclass\n * passes a {@link ChunkingConsoleTransport} instead, because only there does a per-entry size\n * limit exist. Taking the whole transport (rather than a size knob) keeps the size limit a fact\n * about the SINK, which is where it actually lives — a dev terminal has no such limit.\n */\n protected constructor(finalFormat: Format, transport?: Transport) {\n // Read at STARTUP (this ctor runs while booting), so a forgotten ServiceInfo.setInfo(...)\n // fails the deploy rather than shipping logs that cannot say which build emitted them.\n const defaultMeta: Record<string, string> = {\n svcName: ServiceInfo.getName(),\n version: ServiceInfo.getVersion(),\n };\n\n // No level set — we do NOT filter; that is winston's job (defaults to 'info').\n this.base = createLogger({\n format: finalFormat,\n defaultMeta: defaultMeta,\n transports: [transport ?? new transports.Console()],\n handleExceptions: true,\n handleRejections: true,\n });\n }\n\n getLogger(name: string): Logger {\n let logger = this.loggers.get(name);\n if (!logger) {\n logger = new WinstonLogger(this.base.child({ loggerName: name }));\n this.loggers.set(name, logger);\n }\n return logger;\n }\n}\n"]}
@@ -1,5 +1,4 @@
1
1
  import { WinstonFactoryBase } from './WinstonFactoryBase';
2
- import { WinstonFactoryOptions } from './WinstonFactoryOptions';
3
2
  /**
4
3
  * WinstonGcpFactory - the GCP/Cloud Run backend. Emits flat JSON to stdout; the
5
4
  * Cloud Run / GKE logging agent natively parses it — `severity` + `message` lift
@@ -8,7 +7,16 @@ import { WinstonFactoryOptions } from './WinstonFactoryOptions';
8
7
  * @google-cloud transport — correlation rides the webpieces context, read
9
8
  * DIRECTLY from RequestContext on each line. This matches the tested-in-GCP
10
9
  * onetablet/monorepo-nx1 core logger exactly.
10
+ *
11
+ * The service name + version come from {@link ServiceInfo}, which startup must have populated
12
+ * (this constructor reads them); they are NOT factory options.
13
+ *
14
+ * Writes through a {@link ChunkingConsoleTransport} rather than a plain Console, because THIS is the
15
+ * backend with a size limit: Cloud Logging caps a LogEntry at 256 KiB and — critically — an
16
+ * oversized jsonPayload entry is DROPPED, not truncated, with no error raised anywhere. A big
17
+ * response body or stack trace would simply never appear. The transport splits such a record into
18
+ * several complete, parseable records sharing a `jsonPayload.logChunk.uid`.
11
19
  */
12
20
  export declare class WinstonGcpFactory extends WinstonFactoryBase {
13
- constructor(opts?: WinstonFactoryOptions);
21
+ constructor();
14
22
  }
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WinstonGcpFactory = void 0;
4
4
  const winston_1 = require("winston");
5
5
  const WinstonFactoryBase_1 = require("./WinstonFactoryBase");
6
- const WinstonFactoryOptions_1 = require("./WinstonFactoryOptions");
6
+ const ChunkingConsoleTransport_1 = require("./ChunkingConsoleTransport");
7
7
  const format_1 = require("./format");
8
8
  /**
9
9
  * WinstonGcpFactory - the GCP/Cloud Run backend. Emits flat JSON to stdout; the
@@ -13,10 +13,19 @@ const format_1 = require("./format");
13
13
  * @google-cloud transport — correlation rides the webpieces context, read
14
14
  * DIRECTLY from RequestContext on each line. This matches the tested-in-GCP
15
15
  * onetablet/monorepo-nx1 core logger exactly.
16
+ *
17
+ * The service name + version come from {@link ServiceInfo}, which startup must have populated
18
+ * (this constructor reads them); they are NOT factory options.
19
+ *
20
+ * Writes through a {@link ChunkingConsoleTransport} rather than a plain Console, because THIS is the
21
+ * backend with a size limit: Cloud Logging caps a LogEntry at 256 KiB and — critically — an
22
+ * oversized jsonPayload entry is DROPPED, not truncated, with no error raised anywhere. A big
23
+ * response body or stack trace would simply never appear. The transport splits such a record into
24
+ * several complete, parseable records sharing a `jsonPayload.logChunk.uid`.
16
25
  */
17
26
  class WinstonGcpFactory extends WinstonFactoryBase_1.WinstonFactoryBase {
18
- constructor(opts = new WinstonFactoryOptions_1.WinstonFactoryOptions()) {
19
- super(winston_1.format.combine((0, format_1.bigIntSafeFormat)(), (0, format_1.injectContextFormat)(), (0, format_1.severityFormat)(), winston_1.format.json()), opts);
27
+ constructor() {
28
+ super(winston_1.format.combine((0, format_1.bigIntSafeFormat)(), (0, format_1.injectContextFormat)(), (0, format_1.severityFormat)(), winston_1.format.json()), new ChunkingConsoleTransport_1.ChunkingConsoleTransport());
20
29
  }
21
30
  }
22
31
  exports.WinstonGcpFactory = WinstonGcpFactory;
@@ -1 +1 @@
1
- {"version":3,"file":"WinstonGcpFactory.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonGcpFactory.ts"],"names":[],"mappings":";;;AAAA,qCAAiC;AACjC,6DAA0D;AAC1D,mEAAgE;AAChE,qCAAiF;AAEjF;;;;;;;;GAQG;AACH,MAAa,iBAAkB,SAAQ,uCAAkB;IACrD,YAAY,OAA8B,IAAI,6CAAqB,EAAE;QACjE,KAAK,CACD,gBAAM,CAAC,OAAO,CACV,IAAA,yBAAgB,GAAE,EAClB,IAAA,4BAAmB,GAAE,EACrB,IAAA,uBAAc,GAAE,EAChB,gBAAM,CAAC,IAAI,EAAE,CAChB,EACD,IAAI,CACP,CAAC;IACN,CAAC;CACJ;AAZD,8CAYC","sourcesContent":["import { format } from 'winston';\nimport { WinstonFactoryBase } from './WinstonFactoryBase';\nimport { WinstonFactoryOptions } from './WinstonFactoryOptions';\nimport { bigIntSafeFormat, injectContextFormat, severityFormat } from './format';\n\n/**\n * WinstonGcpFactory - the GCP/Cloud Run backend. Emits flat JSON to stdout; the\n * Cloud Run / GKE logging agent natively parses it — `severity` + `message` lift\n * onto the LogEntry and every registered context key lands at top-level\n * jsonPayload.<name> (requestId, tenantId, …), filterable directly. There is NO\n * @google-cloud transport — correlation rides the webpieces context, read\n * DIRECTLY from RequestContext on each line. This matches the tested-in-GCP\n * onetablet/monorepo-nx1 core logger exactly.\n */\nexport class WinstonGcpFactory extends WinstonFactoryBase {\n constructor(opts: WinstonFactoryOptions = new WinstonFactoryOptions()) {\n super(\n format.combine(\n bigIntSafeFormat(),\n injectContextFormat(),\n severityFormat(),\n format.json(),\n ),\n opts,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"WinstonGcpFactory.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonGcpFactory.ts"],"names":[],"mappings":";;;AAAA,qCAAiC;AACjC,6DAA0D;AAC1D,yEAAsE;AACtE,qCAAiF;AAEjF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAa,iBAAkB,SAAQ,uCAAkB;IACrD;QACI,KAAK,CACD,gBAAM,CAAC,OAAO,CACV,IAAA,yBAAgB,GAAE,EAClB,IAAA,4BAAmB,GAAE,EACrB,IAAA,uBAAc,GAAE,EAChB,gBAAM,CAAC,IAAI,EAAE,CAChB,EACD,IAAI,mDAAwB,EAAE,CACjC,CAAC;IACN,CAAC;CACJ;AAZD,8CAYC","sourcesContent":["import { format } from 'winston';\nimport { WinstonFactoryBase } from './WinstonFactoryBase';\nimport { ChunkingConsoleTransport } from './ChunkingConsoleTransport';\nimport { bigIntSafeFormat, injectContextFormat, severityFormat } from './format';\n\n/**\n * WinstonGcpFactory - the GCP/Cloud Run backend. Emits flat JSON to stdout; the\n * Cloud Run / GKE logging agent natively parses it — `severity` + `message` lift\n * onto the LogEntry and every registered context key lands at top-level\n * jsonPayload.<name> (requestId, tenantId, …), filterable directly. There is NO\n * @google-cloud transport — correlation rides the webpieces context, read\n * DIRECTLY from RequestContext on each line. This matches the tested-in-GCP\n * onetablet/monorepo-nx1 core logger exactly.\n *\n * The service name + version come from {@link ServiceInfo}, which startup must have populated\n * (this constructor reads them); they are NOT factory options.\n *\n * Writes through a {@link ChunkingConsoleTransport} rather than a plain Console, because THIS is the\n * backend with a size limit: Cloud Logging caps a LogEntry at 256 KiB and — critically — an\n * oversized jsonPayload entry is DROPPED, not truncated, with no error raised anywhere. A big\n * response body or stack trace would simply never appear. The transport splits such a record into\n * several complete, parseable records sharing a `jsonPayload.logChunk.uid`.\n */\nexport class WinstonGcpFactory extends WinstonFactoryBase {\n constructor() {\n super(\n format.combine(\n bigIntSafeFormat(),\n injectContextFormat(),\n severityFormat(),\n format.json(),\n ),\n new ChunkingConsoleTransport(),\n );\n }\n}\n"]}
package/src/format.js CHANGED
@@ -106,7 +106,19 @@ function severityFormat() {
106
106
  }
107
107
  // Fields that are rendered specially (or not at all) by the local pretty format,
108
108
  // so they must not leak into the trailing "extra" JSON blob.
109
- const LOCAL_STRUCTURAL_KEYS = new Set(['level', 'message', 'severity', 'svcGitHash', 'loggerName']);
109
+ //
110
+ // `svcName` + `version` (the ServiceInfo defaultMeta fields) are here to be rendered NOT AT ALL:
111
+ // they earn their keep in GCP, where you filter across many services and deploys, but locally each
112
+ // service logs to its own place and you can check git yourself — so on every single line they are
113
+ // pure noise. GCP still gets both (this set only affects localPrettyFormat).
114
+ const LOCAL_STRUCTURAL_KEYS = new Set([
115
+ 'level',
116
+ 'message',
117
+ 'severity',
118
+ 'svcName',
119
+ 'version',
120
+ 'loggerName',
121
+ ]);
110
122
  /**
111
123
  * Local-only human format: `[loggerName] [requestId=… tenantId=…] level: message { …extra }`.
112
124
  * The registered context keys (already injected by injectContextFormat) render as
package/src/format.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/format.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The winston format layers that turn a raw webpieces log call into a\n * Cloud-Logging-ready structured record. Ported verbatim (behaviourally) from\n * the tested-in-GCP logger at\n * onetablet/monorepo-nx1 libraries/core-context/src/logger/format.ts, with the\n * one webpieces adaptation: context is read from the webpieces HeaderRegistry +\n * a ContextReader (rather than a hard-coded PLATFORM_HEADERS enum), so the exact\n * set of logged fields is whatever the app registered.\n *\n * Correlation rides the webpieces magic context (AsyncLocalStorage on the\n * server, via the ContextReader passed in) — NOT OpenTelemetry / trace-agent, so\n * nothing here imports a tracing agent.\n */\nimport { format } from 'winston';\nimport type { Format, TransformableInfo } from 'logform';\nimport { stringify as safeStringify } from 'safe-stable-stringify';\nimport { HeaderRegistry } from '@webpieces/core-util';\nimport type { ContextKey } from '@webpieces/core-util';\nimport { RequestContext } from '@webpieces/core-context';\n\n// JSON-shaped value (the widest thing a log field / replacer value can hold),\n// used instead of `any`/`unknown` which the code rules disallow.\ntype JsonValue = string | number | boolean | bigint | object | null | undefined;\n\n// winston level → GCP Cloud Logging severity. The Cloud Run / GKE logging agent\n// recognises top-level `severity` in stdout JSON; without this map it falls back\n// to \"DEFAULT\" which is unfilterable. webpieces `trace` maps onto winston `silly`\n// (see WinstonLogger), so both land at DEBUG severity.\nexport const LEVEL_TO_SEVERITY: Record<string, string> = {\n silly: 'DEBUG',\n verbose: 'DEBUG',\n debug: 'DEBUG',\n info: 'INFO',\n warn: 'WARNING',\n error: 'ERROR',\n};\n\n/**\n * Round-trip the record through safe-stable-stringify so circular references\n * (HTTP client/response cycles, request/response objects, framework execution\n * contexts) become \"[Circular]\" instead of crashing the log emit, and bigints\n * serialize as strings (JSON.stringify can't, and the bare safe-stringify output\n * wouldn't round-trip through JSON.parse). Symbol keys winston relies on are\n * untouched (JSON ignores them), so Object.assign only rewrites string fields.\n */\nexport function bigIntSafeFormat(): Format {\n return format((info: TransformableInfo) => {\n return Object.assign(\n info,\n JSON.parse(\n // webpieces-disable no-any-unknown -- safe-stable-stringify's Replacer types the value as unknown\n safeStringify(info, (_key: string, value: unknown) => {\n if (typeof value === 'bigint') {\n return value.toString();\n }\n return value;\n })!,\n ),\n );\n })();\n}\n\n/**\n * Inject every logged HeaderRegistry key present in the active RequestContext frame\n * into the record under its `name` (→ top-level jsonPayload.<name> in GCP, filterable\n * as jsonPayload.requestId, jsonPayload.tenantId, …). Values are read DIRECTLY from\n * RequestContext, secured keys masked via {@link ContextKey.maskIfSecured} — no\n * ContextReader. Caller-supplied fields on the record win on conflict. Runs on EVERY\n * winston call, including winston's own handleExceptions/handleRejections lines that\n * bypass the WinstonLogger wrapper.\n *\n * This mirrors the (duplicated, on purpose) inline logic in BunyanLogger: it must run\n * ONLY when a winston backend is installed, never for the plain ConsoleLogger. A log\n * line with no active RequestContext = a missing request-wrapping server filter; we\n * report that once (closure latch, via console.error so there is no re-entrancy back\n * into winston and no dependency on LogManager).\n */\n// webpieces-disable no-function-outside-class -- winston format(fn) factory; whole file is winston Format factories\nexport function injectContextFormat(): Format {\n let reportedMissingContext = false;\n return format((info: TransformableInfo) => {\n if (RequestContext.isActive()) {\n // ONE loop, in HeaderRegistry.buildStructuredLogFields. Values may be OBJECTS (the `api`\n // tag), so an object-valued key nests into jsonPayload.<name> (winston JSON-serializes the\n // whole record) rather than being dropped by the string-only buildLogFields. Caller-supplied\n // fields win on conflict.\n RequestContext.buildStructuredLogFields().forEach((value: string | object, name: string) => {\n if (info[name] === undefined) {\n info[name] = value;\n }\n });\n } else if (!reportedMissingContext) {\n reportedMissingContext = true;\n // This IS a logging backend; direct stderr for the framework-misconfig warning.\n console.error(\n 'Log emitted OUTSIDE RequestContext.run(...) — every request must be wrapped in ' +\n 'RequestContext.run() by a server filter. That filter appears to be missing: ' +\n 'correlation fields (requestId, tenant, ...) will be absent from logs. Reported once.',\n );\n }\n return info;\n })();\n}\n\n/**\n * Map the winston level onto a top-level `severity` field that the Cloud Logging\n * agent lifts onto the LogEntry.\n */\nexport function severityFormat(): Format {\n return format((info: TransformableInfo) => {\n info['severity'] = LEVEL_TO_SEVERITY[info.level] || info.level.toUpperCase();\n return info;\n })();\n}\n\n// Fields that are rendered specially (or not at all) by the local pretty format,\n// so they must not leak into the trailing \"extra\" JSON blob.\nconst LOCAL_STRUCTURAL_KEYS = new Set<string>(['level', 'message', 'severity', 'svcGitHash', 'loggerName']);\n\n/**\n * Local-only human format: `[loggerName] [requestId=… tenantId=…] level: message { …extra }`.\n * The registered context keys (already injected by injectContextFormat) render as\n * a bracketed prefix; anything else the caller attached renders as trailing JSON.\n * The set of context-key names is read lazily from the registry (first line).\n */\nexport function localPrettyFormat(): Format {\n let contextNames: Set<string> | undefined;\n return format.printf((info: TransformableInfo) => {\n if (!contextNames) {\n contextNames = new Set(HeaderRegistry.get().getLoggedKeys().map((k: ContextKey) => k.name));\n }\n // Only STRING context values render in the bracket prefix. An object-valued key (the `api`\n // tag) would stringify to \"[object Object]\" here, so it is left out of the prefix and instead\n // falls into the trailing JSON blob below (readable), while GCP still gets it nested.\n const prefixBits: string[] = [];\n for (const name of contextNames) {\n const value = info[name];\n if (value != null && typeof value === 'string') {\n prefixBits.push(`${name}=${value}`);\n }\n }\n const prefix = prefixBits.length ? `[${prefixBits.join(' ')}] ` : '';\n const loggerName = info['loggerName'] ? `[${String(info['loggerName'])}] ` : '';\n\n const rest: Record<string, JsonValue> = {};\n for (const key of Object.keys(info)) {\n if (LOCAL_STRUCTURAL_KEYS.has(key)) {\n continue;\n }\n // A context key already shown in the bracket prefix (string value) is skipped; an\n // object-valued context key (api) was NOT shown there, so let it render as trailing JSON.\n if (contextNames.has(key) && typeof info[key] === 'string') {\n continue;\n }\n rest[key] = info[key] as JsonValue;\n }\n const restStr = Object.keys(rest).length ? ` ${safeStringify(rest)}` : '';\n\n return `${loggerName}${prefix}${info.level}: ${info.message}${restStr}`;\n });\n}\n"]}
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/format.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The winston format layers that turn a raw webpieces log call into a\n * Cloud-Logging-ready structured record. Ported verbatim (behaviourally) from\n * the tested-in-GCP logger at\n * onetablet/monorepo-nx1 libraries/core-context/src/logger/format.ts, with the\n * one webpieces adaptation: context is read from the webpieces HeaderRegistry +\n * a ContextReader (rather than a hard-coded PLATFORM_HEADERS enum), so the exact\n * set of logged fields is whatever the app registered.\n *\n * Correlation rides the webpieces magic context (AsyncLocalStorage on the\n * server, via the ContextReader passed in) — NOT OpenTelemetry / trace-agent, so\n * nothing here imports a tracing agent.\n */\nimport { format } from 'winston';\nimport type { Format, TransformableInfo } from 'logform';\nimport { stringify as safeStringify } from 'safe-stable-stringify';\nimport { HeaderRegistry } from '@webpieces/core-util';\nimport type { ContextKey } from '@webpieces/core-util';\nimport { RequestContext } from '@webpieces/core-context';\n\n// JSON-shaped value (the widest thing a log field / replacer value can hold),\n// used instead of `any`/`unknown` which the code rules disallow.\ntype JsonValue = string | number | boolean | bigint | object | null | undefined;\n\n// winston level → GCP Cloud Logging severity. The Cloud Run / GKE logging agent\n// recognises top-level `severity` in stdout JSON; without this map it falls back\n// to \"DEFAULT\" which is unfilterable. webpieces `trace` maps onto winston `silly`\n// (see WinstonLogger), so both land at DEBUG severity.\nexport const LEVEL_TO_SEVERITY: Record<string, string> = {\n silly: 'DEBUG',\n verbose: 'DEBUG',\n debug: 'DEBUG',\n info: 'INFO',\n warn: 'WARNING',\n error: 'ERROR',\n};\n\n/**\n * Round-trip the record through safe-stable-stringify so circular references\n * (HTTP client/response cycles, request/response objects, framework execution\n * contexts) become \"[Circular]\" instead of crashing the log emit, and bigints\n * serialize as strings (JSON.stringify can't, and the bare safe-stringify output\n * wouldn't round-trip through JSON.parse). Symbol keys winston relies on are\n * untouched (JSON ignores them), so Object.assign only rewrites string fields.\n */\nexport function bigIntSafeFormat(): Format {\n return format((info: TransformableInfo) => {\n return Object.assign(\n info,\n JSON.parse(\n // webpieces-disable no-any-unknown -- safe-stable-stringify's Replacer types the value as unknown\n safeStringify(info, (_key: string, value: unknown) => {\n if (typeof value === 'bigint') {\n return value.toString();\n }\n return value;\n })!,\n ),\n );\n })();\n}\n\n/**\n * Inject every logged HeaderRegistry key present in the active RequestContext frame\n * into the record under its `name` (→ top-level jsonPayload.<name> in GCP, filterable\n * as jsonPayload.requestId, jsonPayload.tenantId, …). Values are read DIRECTLY from\n * RequestContext, secured keys masked via {@link ContextKey.maskIfSecured} — no\n * ContextReader. Caller-supplied fields on the record win on conflict. Runs on EVERY\n * winston call, including winston's own handleExceptions/handleRejections lines that\n * bypass the WinstonLogger wrapper.\n *\n * This mirrors the (duplicated, on purpose) inline logic in BunyanLogger: it must run\n * ONLY when a winston backend is installed, never for the plain ConsoleLogger. A log\n * line with no active RequestContext = a missing request-wrapping server filter; we\n * report that once (closure latch, via console.error so there is no re-entrancy back\n * into winston and no dependency on LogManager).\n */\n// webpieces-disable no-function-outside-class -- winston format(fn) factory; whole file is winston Format factories\nexport function injectContextFormat(): Format {\n let reportedMissingContext = false;\n return format((info: TransformableInfo) => {\n if (RequestContext.isActive()) {\n // ONE loop, in HeaderRegistry.buildStructuredLogFields. Values may be OBJECTS (the `api`\n // tag), so an object-valued key nests into jsonPayload.<name> (winston JSON-serializes the\n // whole record) rather than being dropped by the string-only buildLogFields. Caller-supplied\n // fields win on conflict.\n RequestContext.buildStructuredLogFields().forEach((value: string | object, name: string) => {\n if (info[name] === undefined) {\n info[name] = value;\n }\n });\n } else if (!reportedMissingContext) {\n reportedMissingContext = true;\n // This IS a logging backend; direct stderr for the framework-misconfig warning.\n console.error(\n 'Log emitted OUTSIDE RequestContext.run(...) — every request must be wrapped in ' +\n 'RequestContext.run() by a server filter. That filter appears to be missing: ' +\n 'correlation fields (requestId, tenant, ...) will be absent from logs. Reported once.',\n );\n }\n return info;\n })();\n}\n\n/**\n * Map the winston level onto a top-level `severity` field that the Cloud Logging\n * agent lifts onto the LogEntry.\n */\nexport function severityFormat(): Format {\n return format((info: TransformableInfo) => {\n info['severity'] = LEVEL_TO_SEVERITY[info.level] || info.level.toUpperCase();\n return info;\n })();\n}\n\n// Fields that are rendered specially (or not at all) by the local pretty format,\n// so they must not leak into the trailing \"extra\" JSON blob.\n//\n// `svcName` + `version` (the ServiceInfo defaultMeta fields) are here to be rendered NOT AT ALL:\n// they earn their keep in GCP, where you filter across many services and deploys, but locally each\n// service logs to its own place and you can check git yourself — so on every single line they are\n// pure noise. GCP still gets both (this set only affects localPrettyFormat).\nconst LOCAL_STRUCTURAL_KEYS = new Set<string>([\n 'level',\n 'message',\n 'severity',\n 'svcName',\n 'version',\n 'loggerName',\n]);\n\n/**\n * Local-only human format: `[loggerName] [requestId=… tenantId=…] level: message { …extra }`.\n * The registered context keys (already injected by injectContextFormat) render as\n * a bracketed prefix; anything else the caller attached renders as trailing JSON.\n * The set of context-key names is read lazily from the registry (first line).\n */\nexport function localPrettyFormat(): Format {\n let contextNames: Set<string> | undefined;\n return format.printf((info: TransformableInfo) => {\n if (!contextNames) {\n contextNames = new Set(HeaderRegistry.get().getLoggedKeys().map((k: ContextKey) => k.name));\n }\n // Only STRING context values render in the bracket prefix. An object-valued key (the `api`\n // tag) would stringify to \"[object Object]\" here, so it is left out of the prefix and instead\n // falls into the trailing JSON blob below (readable), while GCP still gets it nested.\n const prefixBits: string[] = [];\n for (const name of contextNames) {\n const value = info[name];\n if (value != null && typeof value === 'string') {\n prefixBits.push(`${name}=${value}`);\n }\n }\n const prefix = prefixBits.length ? `[${prefixBits.join(' ')}] ` : '';\n const loggerName = info['loggerName'] ? `[${String(info['loggerName'])}] ` : '';\n\n const rest: Record<string, JsonValue> = {};\n for (const key of Object.keys(info)) {\n if (LOCAL_STRUCTURAL_KEYS.has(key)) {\n continue;\n }\n // A context key already shown in the bracket prefix (string value) is skipped; an\n // object-valued context key (api) was NOT shown there, so let it render as trailing JSON.\n if (contextNames.has(key) && typeof info[key] === 'string') {\n continue;\n }\n rest[key] = info[key] as JsonValue;\n }\n const restStr = Object.keys(rest).length ? ` ${safeStringify(rest)}` : '';\n\n return `${loggerName}${prefix}${info.level}: ${info.message}${restStr}`;\n });\n}\n"]}
package/src/index.d.ts CHANGED
@@ -5,22 +5,34 @@
5
5
  * startup via `LogManager.setFactory(...)`:
6
6
  *
7
7
  * ```ts
8
+ * import { ServiceInfo } from '@webpieces/core-util';
8
9
  * import { WinstonGcpFactory, WinstonConsoleFactory } from '@webpieces/winston';
9
10
  *
11
+ * ServiceInfo.setInfo('my-service', '2.1.0'); // FIRST — the factories read it in their constructor
10
12
  * const loggerFactory = process.env.K_SERVICE
11
13
  * ? new WinstonGcpFactory() // Cloud Run → stdout JSON
12
14
  * : new WinstonConsoleFactory(); // local → pretty console
13
15
  * // hand to setupRuntime(new RuntimeSetupOptions(loggerFactory, ...))
14
16
  * ```
15
17
  *
18
+ * BREAKING (was `new WinstonFactoryOptions(svcGitHash)` passed to each factory): the version moved
19
+ * to `ServiceInfo.setInfo(name, version)` in @webpieces/core-util, because it is a fact about the
20
+ * SERVICE, not about winston — bunyan needs the same version and previously could not stamp one at
21
+ * all. It is also no longer presumed to be a git SHA: `version` is opaque, so a project deploying
22
+ * semver or CI build numbers is no longer misdescribed. Migration: delete the `WinstonFactoryOptions`
23
+ * import, call `ServiceInfo.setInfo(<name>, <the same hash>)` before building the factory, and drop
24
+ * the ctor argument. A forgotten call throws at startup. Note the field renamed `svcGitHash` →
25
+ * `version`, so GCP log filters/alerts on `jsonPayload.svcGitHash` must be updated.
26
+ *
16
27
  * Both backends auto-enrich every line with the logged context keys, read
17
28
  * DIRECTLY from the active RequestContext (@webpieces/core-context) on each line —
18
- * no ContextReader is threaded in.
29
+ * no ContextReader is threaded in. Every line also carries `svcName` + `version` from ServiceInfo,
30
+ * though neither renders in the LOCAL pretty format (you know your own service, and can check git).
19
31
  *
20
32
  * @packageDocumentation
21
33
  */
22
34
  export { WinstonGcpFactory } from './WinstonGcpFactory';
23
35
  export { WinstonConsoleFactory } from './WinstonConsoleFactory';
24
- export { WinstonFactoryOptions } from './WinstonFactoryOptions';
36
+ export { ChunkingConsoleTransport } from './ChunkingConsoleTransport';
25
37
  export { WinstonLogger, LEVEL_TO_WINSTON } from './WinstonLogger';
26
38
  export { LEVEL_TO_SEVERITY, bigIntSafeFormat, injectContextFormat, severityFormat, localPrettyFormat, } from './format';
package/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.localPrettyFormat = exports.severityFormat = exports.injectContextFormat = exports.bigIntSafeFormat = exports.LEVEL_TO_SEVERITY = exports.LEVEL_TO_WINSTON = exports.WinstonLogger = exports.WinstonFactoryOptions = exports.WinstonConsoleFactory = exports.WinstonGcpFactory = void 0;
3
+ exports.localPrettyFormat = exports.severityFormat = exports.injectContextFormat = exports.bigIntSafeFormat = exports.LEVEL_TO_SEVERITY = exports.LEVEL_TO_WINSTON = exports.WinstonLogger = exports.ChunkingConsoleTransport = exports.WinstonConsoleFactory = exports.WinstonGcpFactory = void 0;
4
4
  /**
5
5
  * @webpieces/winston
6
6
  *
@@ -8,17 +8,29 @@ exports.localPrettyFormat = exports.severityFormat = exports.injectContextFormat
8
8
  * startup via `LogManager.setFactory(...)`:
9
9
  *
10
10
  * ```ts
11
+ * import { ServiceInfo } from '@webpieces/core-util';
11
12
  * import { WinstonGcpFactory, WinstonConsoleFactory } from '@webpieces/winston';
12
13
  *
14
+ * ServiceInfo.setInfo('my-service', '2.1.0'); // FIRST — the factories read it in their constructor
13
15
  * const loggerFactory = process.env.K_SERVICE
14
16
  * ? new WinstonGcpFactory() // Cloud Run → stdout JSON
15
17
  * : new WinstonConsoleFactory(); // local → pretty console
16
18
  * // hand to setupRuntime(new RuntimeSetupOptions(loggerFactory, ...))
17
19
  * ```
18
20
  *
21
+ * BREAKING (was `new WinstonFactoryOptions(svcGitHash)` passed to each factory): the version moved
22
+ * to `ServiceInfo.setInfo(name, version)` in @webpieces/core-util, because it is a fact about the
23
+ * SERVICE, not about winston — bunyan needs the same version and previously could not stamp one at
24
+ * all. It is also no longer presumed to be a git SHA: `version` is opaque, so a project deploying
25
+ * semver or CI build numbers is no longer misdescribed. Migration: delete the `WinstonFactoryOptions`
26
+ * import, call `ServiceInfo.setInfo(<name>, <the same hash>)` before building the factory, and drop
27
+ * the ctor argument. A forgotten call throws at startup. Note the field renamed `svcGitHash` →
28
+ * `version`, so GCP log filters/alerts on `jsonPayload.svcGitHash` must be updated.
29
+ *
19
30
  * Both backends auto-enrich every line with the logged context keys, read
20
31
  * DIRECTLY from the active RequestContext (@webpieces/core-context) on each line —
21
- * no ContextReader is threaded in.
32
+ * no ContextReader is threaded in. Every line also carries `svcName` + `version` from ServiceInfo,
33
+ * though neither renders in the LOCAL pretty format (you know your own service, and can check git).
22
34
  *
23
35
  * @packageDocumentation
24
36
  */
@@ -26,8 +38,8 @@ var WinstonGcpFactory_1 = require("./WinstonGcpFactory");
26
38
  Object.defineProperty(exports, "WinstonGcpFactory", { enumerable: true, get: function () { return WinstonGcpFactory_1.WinstonGcpFactory; } });
27
39
  var WinstonConsoleFactory_1 = require("./WinstonConsoleFactory");
28
40
  Object.defineProperty(exports, "WinstonConsoleFactory", { enumerable: true, get: function () { return WinstonConsoleFactory_1.WinstonConsoleFactory; } });
29
- var WinstonFactoryOptions_1 = require("./WinstonFactoryOptions");
30
- Object.defineProperty(exports, "WinstonFactoryOptions", { enumerable: true, get: function () { return WinstonFactoryOptions_1.WinstonFactoryOptions; } });
41
+ var ChunkingConsoleTransport_1 = require("./ChunkingConsoleTransport");
42
+ Object.defineProperty(exports, "ChunkingConsoleTransport", { enumerable: true, get: function () { return ChunkingConsoleTransport_1.ChunkingConsoleTransport; } });
31
43
  var WinstonLogger_1 = require("./WinstonLogger");
32
44
  Object.defineProperty(exports, "WinstonLogger", { enumerable: true, get: function () { return WinstonLogger_1.WinstonLogger; } });
33
45
  Object.defineProperty(exports, "LEVEL_TO_WINSTON", { enumerable: true, get: function () { return WinstonLogger_1.LEVEL_TO_WINSTON; } });
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,yDAAwD;AAA/C,sHAAA,iBAAiB,OAAA;AAC1B,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAC9B,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAC9B,iDAAkE;AAAzD,8GAAA,aAAa,OAAA;AAAE,iHAAA,gBAAgB,OAAA;AACxC,mCAMkB;AALd,2GAAA,iBAAiB,OAAA;AACjB,0GAAA,gBAAgB,OAAA;AAChB,6GAAA,mBAAmB,OAAA;AACnB,wGAAA,cAAc,OAAA;AACd,2GAAA,iBAAiB,OAAA","sourcesContent":["/**\n * @webpieces/winston\n *\n * Node-only winston {@link LoggerFactory} backends for webpieces. Install one at\n * startup via `LogManager.setFactory(...)`:\n *\n * ```ts\n * import { WinstonGcpFactory, WinstonConsoleFactory } from '@webpieces/winston';\n *\n * const loggerFactory = process.env.K_SERVICE\n * ? new WinstonGcpFactory() // Cloud Run → stdout JSON\n * : new WinstonConsoleFactory(); // local → pretty console\n * // hand to setupRuntime(new RuntimeSetupOptions(loggerFactory, ...))\n * ```\n *\n * Both backends auto-enrich every line with the logged context keys, read\n * DIRECTLY from the active RequestContext (@webpieces/core-context) on each line —\n * no ContextReader is threaded in.\n *\n * @packageDocumentation\n */\nexport { WinstonGcpFactory } from './WinstonGcpFactory';\nexport { WinstonConsoleFactory } from './WinstonConsoleFactory';\nexport { WinstonFactoryOptions } from './WinstonFactoryOptions';\nexport { WinstonLogger, LEVEL_TO_WINSTON } from './WinstonLogger';\nexport {\n LEVEL_TO_SEVERITY,\n bigIntSafeFormat,\n injectContextFormat,\n severityFormat,\n localPrettyFormat,\n} from './format';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,yDAAwD;AAA/C,sHAAA,iBAAiB,OAAA;AAC1B,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAC9B,uEAAsE;AAA7D,oIAAA,wBAAwB,OAAA;AACjC,iDAAkE;AAAzD,8GAAA,aAAa,OAAA;AAAE,iHAAA,gBAAgB,OAAA;AACxC,mCAMkB;AALd,2GAAA,iBAAiB,OAAA;AACjB,0GAAA,gBAAgB,OAAA;AAChB,6GAAA,mBAAmB,OAAA;AACnB,wGAAA,cAAc,OAAA;AACd,2GAAA,iBAAiB,OAAA","sourcesContent":["/**\n * @webpieces/winston\n *\n * Node-only winston {@link LoggerFactory} backends for webpieces. Install one at\n * startup via `LogManager.setFactory(...)`:\n *\n * ```ts\n * import { ServiceInfo } from '@webpieces/core-util';\n * import { WinstonGcpFactory, WinstonConsoleFactory } from '@webpieces/winston';\n *\n * ServiceInfo.setInfo('my-service', '2.1.0'); // FIRST — the factories read it in their constructor\n * const loggerFactory = process.env.K_SERVICE\n * ? new WinstonGcpFactory() // Cloud Run → stdout JSON\n * : new WinstonConsoleFactory(); // local → pretty console\n * // hand to setupRuntime(new RuntimeSetupOptions(loggerFactory, ...))\n * ```\n *\n * BREAKING (was `new WinstonFactoryOptions(svcGitHash)` passed to each factory): the version moved\n * to `ServiceInfo.setInfo(name, version)` in @webpieces/core-util, because it is a fact about the\n * SERVICE, not about winston — bunyan needs the same version and previously could not stamp one at\n * all. It is also no longer presumed to be a git SHA: `version` is opaque, so a project deploying\n * semver or CI build numbers is no longer misdescribed. Migration: delete the `WinstonFactoryOptions`\n * import, call `ServiceInfo.setInfo(<name>, <the same hash>)` before building the factory, and drop\n * the ctor argument. A forgotten call throws at startup. Note the field renamed `svcGitHash` →\n * `version`, so GCP log filters/alerts on `jsonPayload.svcGitHash` must be updated.\n *\n * Both backends auto-enrich every line with the logged context keys, read\n * DIRECTLY from the active RequestContext (@webpieces/core-context) on each line —\n * no ContextReader is threaded in. Every line also carries `svcName` + `version` from ServiceInfo,\n * though neither renders in the LOCAL pretty format (you know your own service, and can check git).\n *\n * @packageDocumentation\n */\nexport { WinstonGcpFactory } from './WinstonGcpFactory';\nexport { WinstonConsoleFactory } from './WinstonConsoleFactory';\nexport { ChunkingConsoleTransport } from './ChunkingConsoleTransport';\nexport { WinstonLogger, LEVEL_TO_WINSTON } from './WinstonLogger';\nexport {\n LEVEL_TO_SEVERITY,\n bigIntSafeFormat,\n injectContextFormat,\n severityFormat,\n localPrettyFormat,\n} from './format';\n"]}
@@ -1,24 +0,0 @@
1
- /**
2
- * WinstonFactoryOptions - tuning for the winston LoggerFactory backends.
3
- *
4
- * Data-only structure → a class, per CLAUDE.md.
5
- *
6
- * There is deliberately NO level knob: webpieces does not filter by level — that
7
- * is winston's job (it filters at its own default). `svcGitHash` is optional, so a
8
- * bare `new WinstonGcpFactory()` still works.
9
- */
10
- export declare class WinstonFactoryOptions {
11
- /**
12
- * The running service's git commit SHA. When set, every line carries
13
- * `jsonPayload.svcGitHash=<sha>` (winston defaultMeta) so operators can
14
- * filter Cloud Logging by deployment.
15
- */
16
- readonly svcGitHash?: string | undefined;
17
- constructor(
18
- /**
19
- * The running service's git commit SHA. When set, every line carries
20
- * `jsonPayload.svcGitHash=<sha>` (winston defaultMeta) so operators can
21
- * filter Cloud Logging by deployment.
22
- */
23
- svcGitHash?: string | undefined);
24
- }
@@ -1,26 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WinstonFactoryOptions = void 0;
4
- /**
5
- * WinstonFactoryOptions - tuning for the winston LoggerFactory backends.
6
- *
7
- * Data-only structure → a class, per CLAUDE.md.
8
- *
9
- * There is deliberately NO level knob: webpieces does not filter by level — that
10
- * is winston's job (it filters at its own default). `svcGitHash` is optional, so a
11
- * bare `new WinstonGcpFactory()` still works.
12
- */
13
- class WinstonFactoryOptions {
14
- svcGitHash;
15
- constructor(
16
- /**
17
- * The running service's git commit SHA. When set, every line carries
18
- * `jsonPayload.svcGitHash=<sha>` (winston defaultMeta) so operators can
19
- * filter Cloud Logging by deployment.
20
- */
21
- svcGitHash) {
22
- this.svcGitHash = svcGitHash;
23
- }
24
- }
25
- exports.WinstonFactoryOptions = WinstonFactoryOptions;
26
- //# sourceMappingURL=WinstonFactoryOptions.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"WinstonFactoryOptions.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonFactoryOptions.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;GAQG;AACH,MAAa,qBAAqB;IAOV;IANpB;IACI;;;;OAIG;IACa,UAAmB;QAAnB,eAAU,GAAV,UAAU,CAAS;IACpC,CAAC;CACP;AATD,sDASC","sourcesContent":["/**\n * WinstonFactoryOptions - tuning for the winston LoggerFactory backends.\n *\n * Data-only structure → a class, per CLAUDE.md.\n *\n * There is deliberately NO level knob: webpieces does not filter by level — that\n * is winston's job (it filters at its own default). `svcGitHash` is optional, so a\n * bare `new WinstonGcpFactory()` still works.\n */\nexport class WinstonFactoryOptions {\n constructor(\n /**\n * The running service's git commit SHA. When set, every line carries\n * `jsonPayload.svcGitHash=<sha>` (winston defaultMeta) so operators can\n * filter Cloud Logging by deployment.\n */\n public readonly svcGitHash?: string,\n ) {}\n}\n"]}