@webpieces/winston 0.3.314 → 0.3.317
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/package.json +3 -2
- package/src/WinstonConsoleFactory.d.ts +1 -5
- package/src/WinstonConsoleFactory.js +2 -5
- package/src/WinstonConsoleFactory.js.map +1 -1
- package/src/WinstonFactoryBase.js +1 -1
- package/src/WinstonFactoryBase.js.map +1 -1
- package/src/WinstonFactoryOptions.d.ts +5 -13
- package/src/WinstonFactoryOptions.js +5 -9
- package/src/WinstonFactoryOptions.js.map +1 -1
- package/src/WinstonGcpFactory.d.ts +4 -8
- package/src/WinstonGcpFactory.js +5 -8
- package/src/WinstonGcpFactory.js.map +1 -1
- package/src/format.d.ts +13 -10
- package/src/format.js +30 -19
- package/src/format.js.map +1 -1
- package/src/index.d.ts +5 -7
- package/src/index.js +5 -7
- package/src/index.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/winston",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.317",
|
|
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,7 +23,8 @@
|
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@webpieces/core-util": "0.3.
|
|
26
|
+
"@webpieces/core-util": "0.3.317",
|
|
27
|
+
"@webpieces/core-context": "0.3.317",
|
|
27
28
|
"winston": "3.11.0",
|
|
28
29
|
"logform": "2.7.0",
|
|
29
30
|
"safe-stable-stringify": "2.5.0"
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ContextReader } from '@webpieces/core-util';
|
|
2
1
|
import { WinstonFactoryBase } from './WinstonFactoryBase';
|
|
3
2
|
import { WinstonFactoryOptions } from './WinstonFactoryOptions';
|
|
4
3
|
/**
|
|
@@ -6,10 +5,7 @@ import { WinstonFactoryOptions } from './WinstonFactoryOptions';
|
|
|
6
5
|
* pretty console output with the registered context keys as a bracketed prefix,
|
|
7
6
|
* for human reading — same enrichment as the GCP backend, different rendering.
|
|
8
7
|
* Matches the tested onetablet/monorepo-nx1 local logger.
|
|
9
|
-
*
|
|
10
|
-
* @param reader the environment's ContextReader (on a node server, a
|
|
11
|
-
* `RequestContextReader` from @webpieces/core-context).
|
|
12
8
|
*/
|
|
13
9
|
export declare class WinstonConsoleFactory extends WinstonFactoryBase {
|
|
14
|
-
constructor(
|
|
10
|
+
constructor(opts?: WinstonFactoryOptions);
|
|
15
11
|
}
|
|
@@ -10,13 +10,10 @@ const format_1 = require("./format");
|
|
|
10
10
|
* pretty console output with the registered context keys as a bracketed prefix,
|
|
11
11
|
* for human reading — same enrichment as the GCP backend, different rendering.
|
|
12
12
|
* Matches the tested onetablet/monorepo-nx1 local logger.
|
|
13
|
-
*
|
|
14
|
-
* @param reader the environment's ContextReader (on a node server, a
|
|
15
|
-
* `RequestContextReader` from @webpieces/core-context).
|
|
16
13
|
*/
|
|
17
14
|
class WinstonConsoleFactory extends WinstonFactoryBase_1.WinstonFactoryBase {
|
|
18
|
-
constructor(
|
|
19
|
-
super(winston_1.format.combine((0, format_1.bigIntSafeFormat)(), (0, format_1.injectContextFormat)(
|
|
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);
|
|
20
17
|
}
|
|
21
18
|
}
|
|
22
19
|
exports.WinstonConsoleFactory = WinstonConsoleFactory;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WinstonConsoleFactory.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonConsoleFactory.ts"],"names":[],"mappings":";;;AAAA,qCAAiC;
|
|
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"]}
|
|
@@ -14,8 +14,8 @@ class WinstonFactoryBase {
|
|
|
14
14
|
base;
|
|
15
15
|
loggers = new Map();
|
|
16
16
|
constructor(finalFormat, opts) {
|
|
17
|
+
// No level set — we do NOT filter; that is winston's job (defaults to 'info').
|
|
17
18
|
this.base = (0, winston_1.createLogger)({
|
|
18
|
-
level: WinstonLogger_1.LEVEL_TO_WINSTON[opts.level],
|
|
19
19
|
format: finalFormat,
|
|
20
20
|
defaultMeta: opts.svcGitHash ? { svcGitHash: opts.svcGitHash } : undefined,
|
|
21
21
|
transports: [new winston_1.transports.Console()],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WinstonFactoryBase.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonFactoryBase.ts"],"names":[],"mappings":";;;AAAA,qCAAmD;AAInD,
|
|
1
|
+
{"version":3,"file":"WinstonFactoryBase.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonFactoryBase.ts"],"names":[],"mappings":";;;AAAA,qCAAmD;AAInD,mDAAgD;AAGhD;;;;;;GAMG;AACH,MAAsB,kBAAkB;IACnB,IAAI,CAAc;IAClB,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAErD,YAAsB,WAAmB,EAAE,IAA2B;QAClE,+EAA+E;QAC/E,IAAI,CAAC,IAAI,GAAG,IAAA,sBAAY,EAAC;YACrB,MAAM,EAAE,WAAW;YACnB,WAAW,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,SAAS;YAC1E,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;AAvBD,gDAuBC","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 { 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 */\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 // No level set — we do NOT filter; that is winston's job (defaults to 'info').\n this.base = createLogger({\n format: finalFormat,\n defaultMeta: opts.svcGitHash ? { svcGitHash: opts.svcGitHash } : undefined,\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,16 +1,13 @@
|
|
|
1
|
-
import type { LogLevel } from '@webpieces/core-util';
|
|
2
1
|
/**
|
|
3
2
|
* WinstonFactoryOptions - tuning for the winston LoggerFactory backends.
|
|
4
3
|
*
|
|
5
|
-
* Data-only structure → a class, per CLAUDE.md.
|
|
6
|
-
*
|
|
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.
|
|
7
9
|
*/
|
|
8
10
|
export declare class WinstonFactoryOptions {
|
|
9
|
-
/**
|
|
10
|
-
* Minimum webpieces level to emit. Defaults to 'info' (matching the
|
|
11
|
-
* tested monorepo-nx logger); pass 'trace'/'debug' to see finer lines.
|
|
12
|
-
*/
|
|
13
|
-
readonly level: LogLevel;
|
|
14
11
|
/**
|
|
15
12
|
* The running service's git commit SHA. When set, every line carries
|
|
16
13
|
* `jsonPayload.svcGitHash=<sha>` (winston defaultMeta) so operators can
|
|
@@ -18,11 +15,6 @@ export declare class WinstonFactoryOptions {
|
|
|
18
15
|
*/
|
|
19
16
|
readonly svcGitHash?: string | undefined;
|
|
20
17
|
constructor(
|
|
21
|
-
/**
|
|
22
|
-
* Minimum webpieces level to emit. Defaults to 'info' (matching the
|
|
23
|
-
* tested monorepo-nx logger); pass 'trace'/'debug' to see finer lines.
|
|
24
|
-
*/
|
|
25
|
-
level?: LogLevel,
|
|
26
18
|
/**
|
|
27
19
|
* The running service's git commit SHA. When set, every line carries
|
|
28
20
|
* `jsonPayload.svcGitHash=<sha>` (winston defaultMeta) so operators can
|
|
@@ -4,25 +4,21 @@ exports.WinstonFactoryOptions = void 0;
|
|
|
4
4
|
/**
|
|
5
5
|
* WinstonFactoryOptions - tuning for the winston LoggerFactory backends.
|
|
6
6
|
*
|
|
7
|
-
* Data-only structure → a class, per CLAUDE.md.
|
|
8
|
-
*
|
|
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.
|
|
9
12
|
*/
|
|
10
13
|
class WinstonFactoryOptions {
|
|
11
|
-
level;
|
|
12
14
|
svcGitHash;
|
|
13
15
|
constructor(
|
|
14
|
-
/**
|
|
15
|
-
* Minimum webpieces level to emit. Defaults to 'info' (matching the
|
|
16
|
-
* tested monorepo-nx logger); pass 'trace'/'debug' to see finer lines.
|
|
17
|
-
*/
|
|
18
|
-
level = 'info',
|
|
19
16
|
/**
|
|
20
17
|
* The running service's git commit SHA. When set, every line carries
|
|
21
18
|
* `jsonPayload.svcGitHash=<sha>` (winston defaultMeta) so operators can
|
|
22
19
|
* filter Cloud Logging by deployment.
|
|
23
20
|
*/
|
|
24
21
|
svcGitHash) {
|
|
25
|
-
this.level = level;
|
|
26
22
|
this.svcGitHash = svcGitHash;
|
|
27
23
|
}
|
|
28
24
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WinstonFactoryOptions.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonFactoryOptions.ts"],"names":[],"mappings":";;;
|
|
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"]}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ContextReader } from '@webpieces/core-util';
|
|
2
1
|
import { WinstonFactoryBase } from './WinstonFactoryBase';
|
|
3
2
|
import { WinstonFactoryOptions } from './WinstonFactoryOptions';
|
|
4
3
|
/**
|
|
@@ -6,13 +5,10 @@ import { WinstonFactoryOptions } from './WinstonFactoryOptions';
|
|
|
6
5
|
* Cloud Run / GKE logging agent natively parses it — `severity` + `message` lift
|
|
7
6
|
* onto the LogEntry and every registered context key lands at top-level
|
|
8
7
|
* jsonPayload.<name> (requestId, tenantId, …), filterable directly. There is NO
|
|
9
|
-
* @google-cloud transport — correlation rides the webpieces context
|
|
10
|
-
* the tested-in-GCP
|
|
11
|
-
*
|
|
12
|
-
* @param reader the environment's ContextReader — on a node server pass
|
|
13
|
-
* `new RequestContextReader()` from @webpieces/core-context. Keeping it a
|
|
14
|
-
* constructor arg is why this package depends only on @webpieces/core-util.
|
|
8
|
+
* @google-cloud transport — correlation rides the webpieces context, read
|
|
9
|
+
* DIRECTLY from RequestContext on each line. This matches the tested-in-GCP
|
|
10
|
+
* onetablet/monorepo-nx1 core logger exactly.
|
|
15
11
|
*/
|
|
16
12
|
export declare class WinstonGcpFactory extends WinstonFactoryBase {
|
|
17
|
-
constructor(
|
|
13
|
+
constructor(opts?: WinstonFactoryOptions);
|
|
18
14
|
}
|
package/src/WinstonGcpFactory.js
CHANGED
|
@@ -10,16 +10,13 @@ const format_1 = require("./format");
|
|
|
10
10
|
* Cloud Run / GKE logging agent natively parses it — `severity` + `message` lift
|
|
11
11
|
* onto the LogEntry and every registered context key lands at top-level
|
|
12
12
|
* jsonPayload.<name> (requestId, tenantId, …), filterable directly. There is NO
|
|
13
|
-
* @google-cloud transport — correlation rides the webpieces context
|
|
14
|
-
* the tested-in-GCP
|
|
15
|
-
*
|
|
16
|
-
* @param reader the environment's ContextReader — on a node server pass
|
|
17
|
-
* `new RequestContextReader()` from @webpieces/core-context. Keeping it a
|
|
18
|
-
* constructor arg is why this package depends only on @webpieces/core-util.
|
|
13
|
+
* @google-cloud transport — correlation rides the webpieces context, read
|
|
14
|
+
* DIRECTLY from RequestContext on each line. This matches the tested-in-GCP
|
|
15
|
+
* onetablet/monorepo-nx1 core logger exactly.
|
|
19
16
|
*/
|
|
20
17
|
class WinstonGcpFactory extends WinstonFactoryBase_1.WinstonFactoryBase {
|
|
21
|
-
constructor(
|
|
22
|
-
super(winston_1.format.combine((0, format_1.bigIntSafeFormat)(), (0, format_1.injectContextFormat)(
|
|
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);
|
|
23
20
|
}
|
|
24
21
|
}
|
|
25
22
|
exports.WinstonGcpFactory = WinstonGcpFactory;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WinstonGcpFactory.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonGcpFactory.ts"],"names":[],"mappings":";;;AAAA,qCAAiC;
|
|
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"]}
|
package/src/format.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Format } from 'logform';
|
|
2
|
-
import type { ContextReader } from '@webpieces/core-util';
|
|
3
2
|
export declare const LEVEL_TO_SEVERITY: Record<string, string>;
|
|
4
3
|
/**
|
|
5
4
|
* Round-trip the record through safe-stable-stringify so circular references
|
|
@@ -11,17 +10,21 @@ export declare const LEVEL_TO_SEVERITY: Record<string, string>;
|
|
|
11
10
|
*/
|
|
12
11
|
export declare function bigIntSafeFormat(): Format;
|
|
13
12
|
/**
|
|
14
|
-
* Inject every logged HeaderRegistry
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* masked
|
|
18
|
-
* record win on conflict. Runs on EVERY
|
|
19
|
-
* handleExceptions/handleRejections lines that
|
|
13
|
+
* Inject every logged HeaderRegistry key present in the active RequestContext frame
|
|
14
|
+
* into the record under its `name` (→ top-level jsonPayload.<name> in GCP, filterable
|
|
15
|
+
* as jsonPayload.requestId, jsonPayload.tenantId, …). Values are read DIRECTLY from
|
|
16
|
+
* RequestContext, secured keys masked via {@link ContextKey.maskIfSecured} — no
|
|
17
|
+
* ContextReader. Caller-supplied fields on the record win on conflict. Runs on EVERY
|
|
18
|
+
* winston call, including winston's own handleExceptions/handleRejections lines that
|
|
19
|
+
* bypass the WinstonLogger wrapper.
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* This mirrors the (duplicated, on purpose) inline logic in BunyanLogger: it must run
|
|
22
|
+
* ONLY when a winston backend is installed, never for the plain ConsoleLogger. A log
|
|
23
|
+
* line with no active RequestContext = a missing request-wrapping server filter; we
|
|
24
|
+
* report that once (closure latch, via console.error so there is no re-entrancy back
|
|
25
|
+
* into winston and no dependency on LogManager).
|
|
23
26
|
*/
|
|
24
|
-
export declare function injectContextFormat(
|
|
27
|
+
export declare function injectContextFormat(): Format;
|
|
25
28
|
/**
|
|
26
29
|
* Map the winston level onto a top-level `severity` field that the Cloud Logging
|
|
27
30
|
* agent lifts onto the LogEntry.
|
package/src/format.js
CHANGED
|
@@ -21,6 +21,7 @@ exports.localPrettyFormat = localPrettyFormat;
|
|
|
21
21
|
const winston_1 = require("winston");
|
|
22
22
|
const safe_stable_stringify_1 = require("safe-stable-stringify");
|
|
23
23
|
const core_util_1 = require("@webpieces/core-util");
|
|
24
|
+
const core_context_1 = require("@webpieces/core-context");
|
|
24
25
|
// winston level → GCP Cloud Logging severity. The Cloud Run / GKE logging agent
|
|
25
26
|
// recognises top-level `severity` in stdout JSON; without this map it falls back
|
|
26
27
|
// to "DEFAULT" which is unfilterable. webpieces `trace` maps onto winston `silly`
|
|
@@ -54,29 +55,39 @@ function bigIntSafeFormat() {
|
|
|
54
55
|
})();
|
|
55
56
|
}
|
|
56
57
|
/**
|
|
57
|
-
* Inject every logged HeaderRegistry
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* masked
|
|
61
|
-
* record win on conflict. Runs on EVERY
|
|
62
|
-
* handleExceptions/handleRejections lines that
|
|
58
|
+
* Inject every logged HeaderRegistry key present in the active RequestContext frame
|
|
59
|
+
* into the record under its `name` (→ top-level jsonPayload.<name> in GCP, filterable
|
|
60
|
+
* as jsonPayload.requestId, jsonPayload.tenantId, …). Values are read DIRECTLY from
|
|
61
|
+
* RequestContext, secured keys masked via {@link ContextKey.maskIfSecured} — no
|
|
62
|
+
* ContextReader. Caller-supplied fields on the record win on conflict. Runs on EVERY
|
|
63
|
+
* winston call, including winston's own handleExceptions/handleRejections lines that
|
|
64
|
+
* bypass the WinstonLogger wrapper.
|
|
63
65
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
+
* This mirrors the (duplicated, on purpose) inline logic in BunyanLogger: it must run
|
|
67
|
+
* ONLY when a winston backend is installed, never for the plain ConsoleLogger. A log
|
|
68
|
+
* line with no active RequestContext = a missing request-wrapping server filter; we
|
|
69
|
+
* report that once (closure latch, via console.error so there is no re-entrancy back
|
|
70
|
+
* into winston and no dependency on LogManager).
|
|
66
71
|
*/
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
let
|
|
72
|
+
// webpieces-disable no-function-outside-class -- winston format(fn) factory; whole file is winston Format factories
|
|
73
|
+
function injectContextFormat() {
|
|
74
|
+
let reportedMissingContext = false;
|
|
70
75
|
return (0, winston_1.format)((info) => {
|
|
71
|
-
if (
|
|
72
|
-
|
|
76
|
+
if (core_context_1.RequestContext.isActive()) {
|
|
77
|
+
// ONE loop, in HeaderRegistry.buildLogFields. Caller-supplied fields win on conflict.
|
|
78
|
+
core_context_1.RequestContext.buildLogFields().forEach((value, name) => {
|
|
79
|
+
if (info[name] === undefined) {
|
|
80
|
+
info[name] = value;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else if (!reportedMissingContext) {
|
|
85
|
+
reportedMissingContext = true;
|
|
86
|
+
// This IS a logging backend; direct stderr for the framework-misconfig warning.
|
|
87
|
+
console.error('Log emitted OUTSIDE RequestContext.run(...) — every request must be wrapped in ' +
|
|
88
|
+
'RequestContext.run() by a server filter. That filter appears to be missing: ' +
|
|
89
|
+
'correlation fields (requestId, tenant, ...) will be absent from logs. Reported once.');
|
|
73
90
|
}
|
|
74
|
-
const logMap = headerMethods.buildSecureMapForLogs(loggedKeys, reader);
|
|
75
|
-
logMap.forEach((value, name) => {
|
|
76
|
-
if (info[name] === undefined) {
|
|
77
|
-
info[name] = value;
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
91
|
return info;
|
|
81
92
|
})();
|
|
82
93
|
}
|
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 { HeaderMethods, HeaderRegistry } from '@webpieces/core-util';\nimport type { ContextKey, ContextReader } from '@webpieces/core-util';\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 context key present in the current context\n * frame into the record under its `name` (→ top-level jsonPayload.<name> in GCP,\n * filterable as jsonPayload.requestId, jsonPayload.tenantId, …). Secured keys are\n * masked by HeaderMethods.buildSecureMapForLogs. Caller-supplied fields on the\n * record win on conflict. Runs on EVERY winston call, including winston's own\n * handleExceptions/handleRejections lines that bypass the WinstonLogger wrapper.\n *\n * The registry is read LAZILY (first log call) because a LoggerFactory is\n * constructed by the app BEFORE setupRuntime calls HeaderRegistry.configure.\n */\nexport function injectContextFormat(reader: ContextReader): Format {\n const headerMethods = new HeaderMethods();\n let loggedKeys: ContextKey[] | undefined;\n return format((info: TransformableInfo) => {\n if (!loggedKeys) {\n loggedKeys = HeaderRegistry.get().getLoggedKeys();\n }\n const logMap = headerMethods.buildSecureMapForLogs(loggedKeys, reader);\n logMap.forEach((value: string, name: string) => {\n if (info[name] === undefined) {\n info[name] = value;\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 const prefixBits: string[] = [];\n for (const name of contextNames) {\n const value = info[name];\n if (value != null) {\n prefixBits.push(`${name}=${String(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) || contextNames.has(key)) {\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.buildLogFields. Caller-supplied fields win on conflict.\n RequestContext.buildLogFields().forEach((value: string, 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 const prefixBits: string[] = [];\n for (const name of contextNames) {\n const value = info[name];\n if (value != null) {\n prefixBits.push(`${name}=${String(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) || contextNames.has(key)) {\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
|
@@ -6,18 +6,16 @@
|
|
|
6
6
|
*
|
|
7
7
|
* ```ts
|
|
8
8
|
* import { WinstonGcpFactory, WinstonConsoleFactory } from '@webpieces/winston';
|
|
9
|
-
* import { RequestContextReader } from '@webpieces/core-context';
|
|
10
9
|
*
|
|
11
|
-
* const reader = new RequestContextReader();
|
|
12
10
|
* const loggerFactory = process.env.K_SERVICE
|
|
13
|
-
* ? new WinstonGcpFactory(
|
|
14
|
-
* : new WinstonConsoleFactory(
|
|
11
|
+
* ? new WinstonGcpFactory() // Cloud Run → stdout JSON
|
|
12
|
+
* : new WinstonConsoleFactory(); // local → pretty console
|
|
15
13
|
* // hand to setupRuntime(new RuntimeSetupOptions(loggerFactory, ...))
|
|
16
14
|
* ```
|
|
17
15
|
*
|
|
18
|
-
* Both backends auto-enrich every line with the logged context keys
|
|
19
|
-
*
|
|
20
|
-
*
|
|
16
|
+
* Both backends auto-enrich every line with the logged context keys, read
|
|
17
|
+
* DIRECTLY from the active RequestContext (@webpieces/core-context) on each line —
|
|
18
|
+
* no ContextReader is threaded in.
|
|
21
19
|
*
|
|
22
20
|
* @packageDocumentation
|
|
23
21
|
*/
|
package/src/index.js
CHANGED
|
@@ -9,18 +9,16 @@ exports.localPrettyFormat = exports.severityFormat = exports.injectContextFormat
|
|
|
9
9
|
*
|
|
10
10
|
* ```ts
|
|
11
11
|
* import { WinstonGcpFactory, WinstonConsoleFactory } from '@webpieces/winston';
|
|
12
|
-
* import { RequestContextReader } from '@webpieces/core-context';
|
|
13
12
|
*
|
|
14
|
-
* const reader = new RequestContextReader();
|
|
15
13
|
* const loggerFactory = process.env.K_SERVICE
|
|
16
|
-
* ? new WinstonGcpFactory(
|
|
17
|
-
* : new WinstonConsoleFactory(
|
|
14
|
+
* ? new WinstonGcpFactory() // Cloud Run → stdout JSON
|
|
15
|
+
* : new WinstonConsoleFactory(); // local → pretty console
|
|
18
16
|
* // hand to setupRuntime(new RuntimeSetupOptions(loggerFactory, ...))
|
|
19
17
|
* ```
|
|
20
18
|
*
|
|
21
|
-
* Both backends auto-enrich every line with the logged context keys
|
|
22
|
-
*
|
|
23
|
-
*
|
|
19
|
+
* Both backends auto-enrich every line with the logged context keys, read
|
|
20
|
+
* DIRECTLY from the active RequestContext (@webpieces/core-context) on each line —
|
|
21
|
+
* no ContextReader is threaded in.
|
|
24
22
|
*
|
|
25
23
|
* @packageDocumentation
|
|
26
24
|
*/
|
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
|
|
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"]}
|