@webpieces/winston 0.3.307

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 ADDED
@@ -0,0 +1,41 @@
1
+ # @webpieces/winston
2
+
3
+ Node-only [winston](https://github.com/winstonjs/winston) backends for the webpieces
4
+ pluggable logging seam (`LoggerFactory` → `Logger` from `@webpieces/core-util`).
5
+
6
+ Two factories, both auto-enriching every line with the logged context keys registered in
7
+ `HeaderRegistry`:
8
+
9
+ - **`WinstonConsoleFactory`** — local dev: colorized single-line pretty output,
10
+ `[loggerName] [requestId=… tenantId=…] level: message {…extra}`.
11
+ - **`WinstonGcpFactory`** — Cloud Run / GKE: flat JSON to **stdout**, scraped by the logging
12
+ agent. A level→`severity` map is the only GCP glue (no `@google-cloud` transport);
13
+ registered context keys land at top-level `jsonPayload.<name>`. This mirrors the
14
+ tested-in-GCP logger in `onetablet/monorepo-nx1`.
15
+
16
+ ## Usage
17
+
18
+ ```ts
19
+ import { LogManager, HeaderRegistry } from '@webpieces/core-util';
20
+ import { WinstonGcpFactory, WinstonConsoleFactory } from '@webpieces/winston';
21
+ import { RequestContextReader } from '@webpieces/core-context';
22
+
23
+ const reader = new RequestContextReader();
24
+ const loggerFactory = process.env.K_SERVICE
25
+ ? new WinstonGcpFactory(reader)
26
+ : new WinstonConsoleFactory(reader);
27
+
28
+ // Typically you pass loggerFactory to setupRuntime(new RuntimeSetupOptions(loggerFactory, ...)),
29
+ // which calls HeaderRegistry.configure(...) then LogManager.setFactory(loggerFactory) for you.
30
+ ```
31
+
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
+
36
+ ## Options
37
+
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).
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@webpieces/winston",
3
+ "version": "0.3.307",
4
+ "description": "Node-only winston LoggerFactory backends for webpieces: Console (local pretty) + GCP (Cloud Run stdout JSON), auto-enriched with HeaderRegistry context keys",
5
+ "type": "commonjs",
6
+ "main": "./src/index.js",
7
+ "types": "./src/index.d.ts",
8
+ "author": "Dean Hiller",
9
+ "license": "Apache-2.0",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/deanhiller/webpieces-ts.git",
13
+ "directory": "packages/logging/winston"
14
+ },
15
+ "keywords": [
16
+ "webpieces",
17
+ "winston",
18
+ "logging",
19
+ "gcp",
20
+ "cloud-run"
21
+ ],
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "dependencies": {
26
+ "@webpieces/core-util": "0.3.307",
27
+ "winston": "3.11.0",
28
+ "logform": "2.7.0",
29
+ "safe-stable-stringify": "2.5.0"
30
+ }
31
+ }
@@ -0,0 +1,15 @@
1
+ import type { ContextReader } from '@webpieces/core-util';
2
+ import { WinstonFactoryBase } from './WinstonFactoryBase';
3
+ import { WinstonFactoryOptions } from './WinstonFactoryOptions';
4
+ /**
5
+ * WinstonConsoleFactory - the LOCAL developer backend. Colorized single-line
6
+ * pretty console output with the registered context keys as a bracketed prefix,
7
+ * for human reading — same enrichment as the GCP backend, different rendering.
8
+ * 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
+ */
13
+ export declare class WinstonConsoleFactory extends WinstonFactoryBase {
14
+ constructor(reader: ContextReader, opts?: WinstonFactoryOptions);
15
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WinstonConsoleFactory = void 0;
4
+ const winston_1 = require("winston");
5
+ const WinstonFactoryBase_1 = require("./WinstonFactoryBase");
6
+ const WinstonFactoryOptions_1 = require("./WinstonFactoryOptions");
7
+ const format_1 = require("./format");
8
+ /**
9
+ * WinstonConsoleFactory - the LOCAL developer backend. Colorized single-line
10
+ * pretty console output with the registered context keys as a bracketed prefix,
11
+ * for human reading — same enrichment as the GCP backend, different rendering.
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
+ */
17
+ class WinstonConsoleFactory extends WinstonFactoryBase_1.WinstonFactoryBase {
18
+ constructor(reader, opts = new WinstonFactoryOptions_1.WinstonFactoryOptions()) {
19
+ super(winston_1.format.combine((0, format_1.bigIntSafeFormat)(), (0, format_1.injectContextFormat)(reader), (0, format_1.severityFormat)(), winston_1.format.colorize(), (0, format_1.localPrettyFormat)()), opts);
20
+ }
21
+ }
22
+ exports.WinstonConsoleFactory = WinstonConsoleFactory;
23
+ //# sourceMappingURL=WinstonConsoleFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WinstonConsoleFactory.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonConsoleFactory.ts"],"names":[],"mappings":";;;AAAA,qCAAiC;AAEjC,6DAA0D;AAC1D,mEAAgE;AAChE,qCAAoG;AAEpG;;;;;;;;GAQG;AACH,MAAa,qBAAsB,SAAQ,uCAAkB;IACzD,YAAY,MAAqB,EAAE,OAA8B,IAAI,6CAAqB,EAAE;QACxF,KAAK,CACD,gBAAM,CAAC,OAAO,CACV,IAAA,yBAAgB,GAAE,EAClB,IAAA,4BAAmB,EAAC,MAAM,CAAC,EAC3B,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 type { ContextReader } from '@webpieces/core-util';\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 *\n * @param reader the environment's ContextReader (on a node server, a\n * `RequestContextReader` from @webpieces/core-context).\n */\nexport class WinstonConsoleFactory extends WinstonFactoryBase {\n constructor(reader: ContextReader, opts: WinstonFactoryOptions = new WinstonFactoryOptions()) {\n super(\n format.combine(\n bigIntSafeFormat(),\n injectContextFormat(reader),\n severityFormat(),\n format.colorize(),\n localPrettyFormat(),\n ),\n opts,\n );\n }\n}\n"]}
@@ -0,0 +1,16 @@
1
+ import type { Format } from 'logform';
2
+ import type { Logger, LoggerFactory } from '@webpieces/core-util';
3
+ import { WinstonFactoryOptions } from './WinstonFactoryOptions';
4
+ /**
5
+ * WinstonFactoryBase - shared plumbing for the winston {@link LoggerFactory}
6
+ * backends. Builds ONE underlying winston logger (a single `Console` transport,
7
+ * handleExceptions/Rejections on) with the caller-chosen format stack, then hands
8
+ * out a cached {@link WinstonLogger} per name (each a winston child carrying
9
+ * `loggerName`). Subclasses differ only in the format stack they pass up.
10
+ */
11
+ export declare abstract class WinstonFactoryBase implements LoggerFactory {
12
+ private readonly base;
13
+ private readonly loggers;
14
+ protected constructor(finalFormat: Format, opts: WinstonFactoryOptions);
15
+ getLogger(name: string): Logger;
16
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WinstonFactoryBase = void 0;
4
+ const winston_1 = require("winston");
5
+ const WinstonLogger_1 = require("./WinstonLogger");
6
+ /**
7
+ * WinstonFactoryBase - shared plumbing for the winston {@link LoggerFactory}
8
+ * backends. Builds ONE underlying winston logger (a single `Console` transport,
9
+ * handleExceptions/Rejections on) with the caller-chosen format stack, then hands
10
+ * out a cached {@link WinstonLogger} per name (each a winston child carrying
11
+ * `loggerName`). Subclasses differ only in the format stack they pass up.
12
+ */
13
+ class WinstonFactoryBase {
14
+ base;
15
+ loggers = new Map();
16
+ constructor(finalFormat, opts) {
17
+ this.base = (0, winston_1.createLogger)({
18
+ level: WinstonLogger_1.LEVEL_TO_WINSTON[opts.level],
19
+ format: finalFormat,
20
+ defaultMeta: opts.svcGitHash ? { svcGitHash: opts.svcGitHash } : undefined,
21
+ transports: [new winston_1.transports.Console()],
22
+ handleExceptions: true,
23
+ handleRejections: true,
24
+ });
25
+ }
26
+ getLogger(name) {
27
+ let logger = this.loggers.get(name);
28
+ if (!logger) {
29
+ logger = new WinstonLogger_1.WinstonLogger(this.base.child({ loggerName: name }));
30
+ this.loggers.set(name, logger);
31
+ }
32
+ return logger;
33
+ }
34
+ }
35
+ exports.WinstonFactoryBase = WinstonFactoryBase;
36
+ //# sourceMappingURL=WinstonFactoryBase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WinstonFactoryBase.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonFactoryBase.ts"],"names":[],"mappings":";;;AAAA,qCAAmD;AAInD,mDAAkE;AAGlE;;;;;;GAMG;AACH,MAAsB,kBAAkB;IACnB,IAAI,CAAc;IAClB,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAErD,YAAsB,WAAmB,EAAE,IAA2B;QAClE,IAAI,CAAC,IAAI,GAAG,IAAA,sBAAY,EAAC;YACrB,KAAK,EAAE,gCAAgB,CAAC,IAAI,CAAC,KAAK,CAAC;YACnC,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 { LEVEL_TO_WINSTON, 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 this.base = createLogger({\n level: LEVEL_TO_WINSTON[opts.level],\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"]}
@@ -0,0 +1,32 @@
1
+ import type { LogLevel } from '@webpieces/core-util';
2
+ /**
3
+ * WinstonFactoryOptions - tuning for the winston LoggerFactory backends.
4
+ *
5
+ * Data-only structure → a class, per CLAUDE.md. All fields optional; a bare
6
+ * `new WinstonGcpFactory(reader)` uses the defaults (INFO threshold, no git hash).
7
+ */
8
+ 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
+ /**
15
+ * The running service's git commit SHA. When set, every line carries
16
+ * `jsonPayload.svcGitHash=<sha>` (winston defaultMeta) so operators can
17
+ * filter Cloud Logging by deployment.
18
+ */
19
+ readonly svcGitHash?: string | undefined;
20
+ 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
+ /**
27
+ * The running service's git commit SHA. When set, every line carries
28
+ * `jsonPayload.svcGitHash=<sha>` (winston defaultMeta) so operators can
29
+ * filter Cloud Logging by deployment.
30
+ */
31
+ svcGitHash?: string | undefined);
32
+ }
@@ -0,0 +1,30 @@
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. All fields optional; a bare
8
+ * `new WinstonGcpFactory(reader)` uses the defaults (INFO threshold, no git hash).
9
+ */
10
+ class WinstonFactoryOptions {
11
+ level;
12
+ svcGitHash;
13
+ 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
+ /**
20
+ * The running service's git commit SHA. When set, every line carries
21
+ * `jsonPayload.svcGitHash=<sha>` (winston defaultMeta) so operators can
22
+ * filter Cloud Logging by deployment.
23
+ */
24
+ svcGitHash) {
25
+ this.level = level;
26
+ this.svcGitHash = svcGitHash;
27
+ }
28
+ }
29
+ exports.WinstonFactoryOptions = WinstonFactoryOptions;
30
+ //# sourceMappingURL=WinstonFactoryOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WinstonFactoryOptions.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonFactoryOptions.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACH,MAAa,qBAAqB;IAMV;IAMA;IAXpB;IACI;;;OAGG;IACa,QAAkB,MAAM;IACxC;;;;OAIG;IACa,UAAmB;QANnB,UAAK,GAAL,KAAK,CAAmB;QAMxB,eAAU,GAAV,UAAU,CAAS;IACpC,CAAC;CACP;AAdD,sDAcC","sourcesContent":["import type { LogLevel } from '@webpieces/core-util';\n\n/**\n * WinstonFactoryOptions - tuning for the winston LoggerFactory backends.\n *\n * Data-only structure → a class, per CLAUDE.md. All fields optional; a bare\n * `new WinstonGcpFactory(reader)` uses the defaults (INFO threshold, no git hash).\n */\nexport class WinstonFactoryOptions {\n constructor(\n /**\n * Minimum webpieces level to emit. Defaults to 'info' (matching the\n * tested monorepo-nx logger); pass 'trace'/'debug' to see finer lines.\n */\n public readonly level: LogLevel = 'info',\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"]}
@@ -0,0 +1,18 @@
1
+ import type { ContextReader } from '@webpieces/core-util';
2
+ import { WinstonFactoryBase } from './WinstonFactoryBase';
3
+ import { WinstonFactoryOptions } from './WinstonFactoryOptions';
4
+ /**
5
+ * WinstonGcpFactory - the GCP/Cloud Run backend. Emits flat JSON to stdout; the
6
+ * Cloud Run / GKE logging agent natively parses it — `severity` + `message` lift
7
+ * onto the LogEntry and every registered context key lands at top-level
8
+ * jsonPayload.<name> (requestId, tenantId, …), filterable directly. There is NO
9
+ * @google-cloud transport — correlation rides the webpieces context. This matches
10
+ * the tested-in-GCP onetablet/monorepo-nx1 core logger exactly.
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.
15
+ */
16
+ export declare class WinstonGcpFactory extends WinstonFactoryBase {
17
+ constructor(reader: ContextReader, opts?: WinstonFactoryOptions);
18
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WinstonGcpFactory = void 0;
4
+ const winston_1 = require("winston");
5
+ const WinstonFactoryBase_1 = require("./WinstonFactoryBase");
6
+ const WinstonFactoryOptions_1 = require("./WinstonFactoryOptions");
7
+ const format_1 = require("./format");
8
+ /**
9
+ * WinstonGcpFactory - the GCP/Cloud Run backend. Emits flat JSON to stdout; the
10
+ * Cloud Run / GKE logging agent natively parses it — `severity` + `message` lift
11
+ * onto the LogEntry and every registered context key lands at top-level
12
+ * jsonPayload.<name> (requestId, tenantId, …), filterable directly. There is NO
13
+ * @google-cloud transport — correlation rides the webpieces context. This matches
14
+ * the tested-in-GCP onetablet/monorepo-nx1 core logger exactly.
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.
19
+ */
20
+ class WinstonGcpFactory extends WinstonFactoryBase_1.WinstonFactoryBase {
21
+ constructor(reader, opts = new WinstonFactoryOptions_1.WinstonFactoryOptions()) {
22
+ super(winston_1.format.combine((0, format_1.bigIntSafeFormat)(), (0, format_1.injectContextFormat)(reader), (0, format_1.severityFormat)(), winston_1.format.json()), opts);
23
+ }
24
+ }
25
+ exports.WinstonGcpFactory = WinstonGcpFactory;
26
+ //# sourceMappingURL=WinstonGcpFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WinstonGcpFactory.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonGcpFactory.ts"],"names":[],"mappings":";;;AAAA,qCAAiC;AAEjC,6DAA0D;AAC1D,mEAAgE;AAChE,qCAAiF;AAEjF;;;;;;;;;;;GAWG;AACH,MAAa,iBAAkB,SAAQ,uCAAkB;IACrD,YAAY,MAAqB,EAAE,OAA8B,IAAI,6CAAqB,EAAE;QACxF,KAAK,CACD,gBAAM,CAAC,OAAO,CACV,IAAA,yBAAgB,GAAE,EAClB,IAAA,4BAAmB,EAAC,MAAM,CAAC,EAC3B,IAAA,uBAAc,GAAE,EAChB,gBAAM,CAAC,IAAI,EAAE,CAChB,EACD,IAAI,CACP,CAAC;IACN,CAAC;CACJ;AAZD,8CAYC","sourcesContent":["import { format } from 'winston';\nimport type { ContextReader } from '@webpieces/core-util';\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. This matches\n * the tested-in-GCP onetablet/monorepo-nx1 core logger exactly.\n *\n * @param reader the environment's ContextReader — on a node server pass\n * `new RequestContextReader()` from @webpieces/core-context. Keeping it a\n * constructor arg is why this package depends only on @webpieces/core-util.\n */\nexport class WinstonGcpFactory extends WinstonFactoryBase {\n constructor(reader: ContextReader, opts: WinstonFactoryOptions = new WinstonFactoryOptions()) {\n super(\n format.combine(\n bigIntSafeFormat(),\n injectContextFormat(reader),\n severityFormat(),\n format.json(),\n ),\n opts,\n );\n }\n}\n"]}
@@ -0,0 +1,25 @@
1
+ import type { Logger, LogLevel } from '@webpieces/core-util';
2
+ import type { Logger as WinstonBase } from 'winston';
3
+ /**
4
+ * webpieces LogLevel → winston level name. webpieces `trace` has no native
5
+ * winston counterpart, so it maps onto winston `silly` (the finest npm level);
6
+ * both resolve to GCP severity DEBUG via LEVEL_TO_SEVERITY.
7
+ */
8
+ export declare const LEVEL_TO_WINSTON: Record<LogLevel, string>;
9
+ /**
10
+ * WinstonLogger - a webpieces {@link Logger} backed by a winston logger instance
11
+ * (one per name, created as a winston child carrying `loggerName`). Context
12
+ * enrichment is done by the shared winston formats (see format.ts), so this
13
+ * wrapper only maps the 5 webpieces levels and spreads an optional Error into
14
+ * `errName`/`errMessage`/`errStack` — matching the tested monorepo-nx behaviour.
15
+ */
16
+ export declare class WinstonLogger implements Logger {
17
+ private readonly winston;
18
+ constructor(winston: WinstonBase);
19
+ trace(message: string, err?: Error): void;
20
+ debug(message: string, err?: Error): void;
21
+ info(message: string, err?: Error): void;
22
+ warn(message: string, err?: Error): void;
23
+ error(message: string, err?: Error): void;
24
+ private emit;
25
+ }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WinstonLogger = exports.LEVEL_TO_WINSTON = void 0;
4
+ /**
5
+ * webpieces LogLevel → winston level name. webpieces `trace` has no native
6
+ * winston counterpart, so it maps onto winston `silly` (the finest npm level);
7
+ * both resolve to GCP severity DEBUG via LEVEL_TO_SEVERITY.
8
+ */
9
+ exports.LEVEL_TO_WINSTON = {
10
+ trace: 'silly',
11
+ debug: 'debug',
12
+ info: 'info',
13
+ warn: 'warn',
14
+ error: 'error',
15
+ };
16
+ /**
17
+ * WinstonLogger - a webpieces {@link Logger} backed by a winston logger instance
18
+ * (one per name, created as a winston child carrying `loggerName`). Context
19
+ * enrichment is done by the shared winston formats (see format.ts), so this
20
+ * wrapper only maps the 5 webpieces levels and spreads an optional Error into
21
+ * `errName`/`errMessage`/`errStack` — matching the tested monorepo-nx behaviour.
22
+ */
23
+ class WinstonLogger {
24
+ winston;
25
+ constructor(winston) {
26
+ this.winston = winston;
27
+ }
28
+ trace(message, err) {
29
+ this.emit('trace', message, err);
30
+ }
31
+ debug(message, err) {
32
+ this.emit('debug', message, err);
33
+ }
34
+ info(message, err) {
35
+ this.emit('info', message, err);
36
+ }
37
+ warn(message, err) {
38
+ this.emit('warn', message, err);
39
+ }
40
+ error(message, err) {
41
+ this.emit('error', message, err);
42
+ }
43
+ emit(level, message, err) {
44
+ const winstonLevel = exports.LEVEL_TO_WINSTON[level];
45
+ if (err) {
46
+ this.winston.log(winstonLevel, message, {
47
+ errName: err.name,
48
+ errMessage: err.message,
49
+ errStack: err.stack,
50
+ });
51
+ }
52
+ else {
53
+ this.winston.log(winstonLevel, message);
54
+ }
55
+ }
56
+ }
57
+ exports.WinstonLogger = WinstonLogger;
58
+ //# sourceMappingURL=WinstonLogger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WinstonLogger.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/WinstonLogger.ts"],"names":[],"mappings":";;;AAGA;;;;GAIG;AACU,QAAA,gBAAgB,GAA6B;IACtD,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;CACjB,CAAC;AAEF;;;;;;GAMG;AACH,MAAa,aAAa;IACO;IAA7B,YAA6B,OAAoB;QAApB,YAAO,GAAP,OAAO,CAAa;IAAG,CAAC;IAErD,KAAK,CAAC,OAAe,EAAE,GAAW;QAC9B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAW;QAC9B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAW;QAC7B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAW;QAC7B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAW;QAC9B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IACrC,CAAC;IAEO,IAAI,CAAC,KAAe,EAAE,OAAe,EAAE,GAAW;QACtD,MAAM,YAAY,GAAG,wBAAgB,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,GAAG,EAAE,CAAC;YACN,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,EAAE;gBACpC,OAAO,EAAE,GAAG,CAAC,IAAI;gBACjB,UAAU,EAAE,GAAG,CAAC,OAAO;gBACvB,QAAQ,EAAE,GAAG,CAAC,KAAK;aACtB,CAAC,CAAC;QACP,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAC5C,CAAC;IACL,CAAC;CACJ;AAnCD,sCAmCC","sourcesContent":["import type { Logger, LogLevel } from '@webpieces/core-util';\nimport type { Logger as WinstonBase } from 'winston';\n\n/**\n * webpieces LogLevel → winston level name. webpieces `trace` has no native\n * winston counterpart, so it maps onto winston `silly` (the finest npm level);\n * both resolve to GCP severity DEBUG via LEVEL_TO_SEVERITY.\n */\nexport const LEVEL_TO_WINSTON: Record<LogLevel, string> = {\n trace: 'silly',\n debug: 'debug',\n info: 'info',\n warn: 'warn',\n error: 'error',\n};\n\n/**\n * WinstonLogger - a webpieces {@link Logger} backed by a winston logger instance\n * (one per name, created as a winston child carrying `loggerName`). Context\n * enrichment is done by the shared winston formats (see format.ts), so this\n * wrapper only maps the 5 webpieces levels and spreads an optional Error into\n * `errName`/`errMessage`/`errStack` — matching the tested monorepo-nx behaviour.\n */\nexport class WinstonLogger implements Logger {\n constructor(private readonly winston: WinstonBase) {}\n\n trace(message: string, err?: Error): void {\n this.emit('trace', message, err);\n }\n\n debug(message: string, err?: Error): void {\n this.emit('debug', message, err);\n }\n\n info(message: string, err?: Error): void {\n this.emit('info', message, err);\n }\n\n warn(message: string, err?: Error): void {\n this.emit('warn', message, err);\n }\n\n error(message: string, err?: Error): void {\n this.emit('error', message, err);\n }\n\n private emit(level: LogLevel, message: string, err?: Error): void {\n const winstonLevel = LEVEL_TO_WINSTON[level];\n if (err) {\n this.winston.log(winstonLevel, message, {\n errName: err.name,\n errMessage: err.message,\n errStack: err.stack,\n });\n } else {\n this.winston.log(winstonLevel, message);\n }\n }\n}\n"]}
@@ -0,0 +1,36 @@
1
+ import type { Format } from 'logform';
2
+ import type { ContextReader } from '@webpieces/core-util';
3
+ export declare const LEVEL_TO_SEVERITY: Record<string, string>;
4
+ /**
5
+ * Round-trip the record through safe-stable-stringify so circular references
6
+ * (HTTP client/response cycles, request/response objects, framework execution
7
+ * contexts) become "[Circular]" instead of crashing the log emit, and bigints
8
+ * serialize as strings (JSON.stringify can't, and the bare safe-stringify output
9
+ * wouldn't round-trip through JSON.parse). Symbol keys winston relies on are
10
+ * untouched (JSON ignores them), so Object.assign only rewrites string fields.
11
+ */
12
+ export declare function bigIntSafeFormat(): Format;
13
+ /**
14
+ * Inject every logged HeaderRegistry context key present in the current context
15
+ * frame into the record under its `name` (→ top-level jsonPayload.<name> in GCP,
16
+ * filterable as jsonPayload.requestId, jsonPayload.tenantId, …). Secured keys are
17
+ * masked by HeaderMethods.buildSecureMapForLogs. Caller-supplied fields on the
18
+ * record win on conflict. Runs on EVERY winston call, including winston's own
19
+ * handleExceptions/handleRejections lines that bypass the WinstonLogger wrapper.
20
+ *
21
+ * The registry is read LAZILY (first log call) because a LoggerFactory is
22
+ * constructed by the app BEFORE setupRuntime calls HeaderRegistry.configure.
23
+ */
24
+ export declare function injectContextFormat(reader: ContextReader): Format;
25
+ /**
26
+ * Map the winston level onto a top-level `severity` field that the Cloud Logging
27
+ * agent lifts onto the LogEntry.
28
+ */
29
+ export declare function severityFormat(): Format;
30
+ /**
31
+ * Local-only human format: `[loggerName] [requestId=… tenantId=…] level: message { …extra }`.
32
+ * The registered context keys (already injected by injectContextFormat) render as
33
+ * a bracketed prefix; anything else the caller attached renders as trailing JSON.
34
+ * The set of context-key names is read lazily from the registry (first line).
35
+ */
36
+ export declare function localPrettyFormat(): Format;
package/src/format.js ADDED
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LEVEL_TO_SEVERITY = void 0;
4
+ exports.bigIntSafeFormat = bigIntSafeFormat;
5
+ exports.injectContextFormat = injectContextFormat;
6
+ exports.severityFormat = severityFormat;
7
+ exports.localPrettyFormat = localPrettyFormat;
8
+ /**
9
+ * The winston format layers that turn a raw webpieces log call into a
10
+ * Cloud-Logging-ready structured record. Ported verbatim (behaviourally) from
11
+ * the tested-in-GCP logger at
12
+ * onetablet/monorepo-nx1 libraries/core-context/src/logger/format.ts, with the
13
+ * one webpieces adaptation: context is read from the webpieces HeaderRegistry +
14
+ * a ContextReader (rather than a hard-coded PLATFORM_HEADERS enum), so the exact
15
+ * set of logged fields is whatever the app registered.
16
+ *
17
+ * Correlation rides the webpieces magic context (AsyncLocalStorage on the
18
+ * server, via the ContextReader passed in) — NOT OpenTelemetry / trace-agent, so
19
+ * nothing here imports a tracing agent.
20
+ */
21
+ const winston_1 = require("winston");
22
+ const safe_stable_stringify_1 = require("safe-stable-stringify");
23
+ const core_util_1 = require("@webpieces/core-util");
24
+ // winston level → GCP Cloud Logging severity. The Cloud Run / GKE logging agent
25
+ // recognises top-level `severity` in stdout JSON; without this map it falls back
26
+ // to "DEFAULT" which is unfilterable. webpieces `trace` maps onto winston `silly`
27
+ // (see WinstonLogger), so both land at DEBUG severity.
28
+ exports.LEVEL_TO_SEVERITY = {
29
+ silly: 'DEBUG',
30
+ verbose: 'DEBUG',
31
+ debug: 'DEBUG',
32
+ info: 'INFO',
33
+ warn: 'WARNING',
34
+ error: 'ERROR',
35
+ };
36
+ /**
37
+ * Round-trip the record through safe-stable-stringify so circular references
38
+ * (HTTP client/response cycles, request/response objects, framework execution
39
+ * contexts) become "[Circular]" instead of crashing the log emit, and bigints
40
+ * serialize as strings (JSON.stringify can't, and the bare safe-stringify output
41
+ * wouldn't round-trip through JSON.parse). Symbol keys winston relies on are
42
+ * untouched (JSON ignores them), so Object.assign only rewrites string fields.
43
+ */
44
+ function bigIntSafeFormat() {
45
+ return (0, winston_1.format)((info) => {
46
+ return Object.assign(info, JSON.parse(
47
+ // webpieces-disable no-any-unknown -- safe-stable-stringify's Replacer types the value as unknown
48
+ (0, safe_stable_stringify_1.stringify)(info, (_key, value) => {
49
+ if (typeof value === 'bigint') {
50
+ return value.toString();
51
+ }
52
+ return value;
53
+ })));
54
+ })();
55
+ }
56
+ /**
57
+ * Inject every logged HeaderRegistry context key present in the current context
58
+ * frame into the record under its `name` (→ top-level jsonPayload.<name> in GCP,
59
+ * filterable as jsonPayload.requestId, jsonPayload.tenantId, …). Secured keys are
60
+ * masked by HeaderMethods.buildSecureMapForLogs. Caller-supplied fields on the
61
+ * record win on conflict. Runs on EVERY winston call, including winston's own
62
+ * handleExceptions/handleRejections lines that bypass the WinstonLogger wrapper.
63
+ *
64
+ * The registry is read LAZILY (first log call) because a LoggerFactory is
65
+ * constructed by the app BEFORE setupRuntime calls HeaderRegistry.configure.
66
+ */
67
+ function injectContextFormat(reader) {
68
+ const headerMethods = new core_util_1.HeaderMethods();
69
+ let loggedKeys;
70
+ return (0, winston_1.format)((info) => {
71
+ if (!loggedKeys) {
72
+ loggedKeys = core_util_1.HeaderRegistry.get().getLoggedKeys();
73
+ }
74
+ const logMap = headerMethods.buildSecureMapForLogs(loggedKeys, reader);
75
+ logMap.forEach((value, name) => {
76
+ if (info[name] === undefined) {
77
+ info[name] = value;
78
+ }
79
+ });
80
+ return info;
81
+ })();
82
+ }
83
+ /**
84
+ * Map the winston level onto a top-level `severity` field that the Cloud Logging
85
+ * agent lifts onto the LogEntry.
86
+ */
87
+ function severityFormat() {
88
+ return (0, winston_1.format)((info) => {
89
+ info['severity'] = exports.LEVEL_TO_SEVERITY[info.level] || info.level.toUpperCase();
90
+ return info;
91
+ })();
92
+ }
93
+ // Fields that are rendered specially (or not at all) by the local pretty format,
94
+ // so they must not leak into the trailing "extra" JSON blob.
95
+ const LOCAL_STRUCTURAL_KEYS = new Set(['level', 'message', 'severity', 'svcGitHash', 'loggerName']);
96
+ /**
97
+ * Local-only human format: `[loggerName] [requestId=… tenantId=…] level: message { …extra }`.
98
+ * The registered context keys (already injected by injectContextFormat) render as
99
+ * a bracketed prefix; anything else the caller attached renders as trailing JSON.
100
+ * The set of context-key names is read lazily from the registry (first line).
101
+ */
102
+ function localPrettyFormat() {
103
+ let contextNames;
104
+ return winston_1.format.printf((info) => {
105
+ if (!contextNames) {
106
+ contextNames = new Set(core_util_1.HeaderRegistry.get().getLoggedKeys().map((k) => k.name));
107
+ }
108
+ const prefixBits = [];
109
+ for (const name of contextNames) {
110
+ const value = info[name];
111
+ if (value != null) {
112
+ prefixBits.push(`${name}=${String(value)}`);
113
+ }
114
+ }
115
+ const prefix = prefixBits.length ? `[${prefixBits.join(' ')}] ` : '';
116
+ const loggerName = info['loggerName'] ? `[${String(info['loggerName'])}] ` : '';
117
+ const rest = {};
118
+ for (const key of Object.keys(info)) {
119
+ if (LOCAL_STRUCTURAL_KEYS.has(key) || contextNames.has(key)) {
120
+ continue;
121
+ }
122
+ rest[key] = info[key];
123
+ }
124
+ const restStr = Object.keys(rest).length ? ` ${(0, safe_stable_stringify_1.stringify)(rest)}` : '';
125
+ return `${loggerName}${prefix}${info.level}: ${info.message}${restStr}`;
126
+ });
127
+ }
128
+ //# sourceMappingURL=format.js.map
@@ -0,0 +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"]}
package/src/index.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @webpieces/winston
3
+ *
4
+ * Node-only winston {@link LoggerFactory} backends for webpieces. Install one at
5
+ * startup via `LogManager.setFactory(...)`:
6
+ *
7
+ * ```ts
8
+ * import { WinstonGcpFactory, WinstonConsoleFactory } from '@webpieces/winston';
9
+ * import { RequestContextReader } from '@webpieces/core-context';
10
+ *
11
+ * const reader = new RequestContextReader();
12
+ * const loggerFactory = process.env.K_SERVICE
13
+ * ? new WinstonGcpFactory(reader) // Cloud Run → stdout JSON
14
+ * : new WinstonConsoleFactory(reader); // local → pretty console
15
+ * // hand to setupRuntime(new RuntimeSetupOptions(loggerFactory, ...))
16
+ * ```
17
+ *
18
+ * Both backends auto-enrich every line with the logged context keys from
19
+ * HeaderRegistry. This package depends only on @webpieces/core-util (+ winston);
20
+ * the node RequestContextReader is passed in, not imported.
21
+ *
22
+ * @packageDocumentation
23
+ */
24
+ export { WinstonGcpFactory } from './WinstonGcpFactory';
25
+ export { WinstonConsoleFactory } from './WinstonConsoleFactory';
26
+ export { WinstonFactoryOptions } from './WinstonFactoryOptions';
27
+ export { WinstonLogger, LEVEL_TO_WINSTON } from './WinstonLogger';
28
+ export { LEVEL_TO_SEVERITY, bigIntSafeFormat, injectContextFormat, severityFormat, localPrettyFormat, } from './format';
package/src/index.js ADDED
@@ -0,0 +1,42 @@
1
+ "use strict";
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;
4
+ /**
5
+ * @webpieces/winston
6
+ *
7
+ * Node-only winston {@link LoggerFactory} backends for webpieces. Install one at
8
+ * startup via `LogManager.setFactory(...)`:
9
+ *
10
+ * ```ts
11
+ * import { WinstonGcpFactory, WinstonConsoleFactory } from '@webpieces/winston';
12
+ * import { RequestContextReader } from '@webpieces/core-context';
13
+ *
14
+ * const reader = new RequestContextReader();
15
+ * const loggerFactory = process.env.K_SERVICE
16
+ * ? new WinstonGcpFactory(reader) // Cloud Run → stdout JSON
17
+ * : new WinstonConsoleFactory(reader); // local → pretty console
18
+ * // hand to setupRuntime(new RuntimeSetupOptions(loggerFactory, ...))
19
+ * ```
20
+ *
21
+ * Both backends auto-enrich every line with the logged context keys from
22
+ * HeaderRegistry. This package depends only on @webpieces/core-util (+ winston);
23
+ * the node RequestContextReader is passed in, not imported.
24
+ *
25
+ * @packageDocumentation
26
+ */
27
+ var WinstonGcpFactory_1 = require("./WinstonGcpFactory");
28
+ Object.defineProperty(exports, "WinstonGcpFactory", { enumerable: true, get: function () { return WinstonGcpFactory_1.WinstonGcpFactory; } });
29
+ var WinstonConsoleFactory_1 = require("./WinstonConsoleFactory");
30
+ Object.defineProperty(exports, "WinstonConsoleFactory", { enumerable: true, get: function () { return WinstonConsoleFactory_1.WinstonConsoleFactory; } });
31
+ var WinstonFactoryOptions_1 = require("./WinstonFactoryOptions");
32
+ Object.defineProperty(exports, "WinstonFactoryOptions", { enumerable: true, get: function () { return WinstonFactoryOptions_1.WinstonFactoryOptions; } });
33
+ var WinstonLogger_1 = require("./WinstonLogger");
34
+ Object.defineProperty(exports, "WinstonLogger", { enumerable: true, get: function () { return WinstonLogger_1.WinstonLogger; } });
35
+ Object.defineProperty(exports, "LEVEL_TO_WINSTON", { enumerable: true, get: function () { return WinstonLogger_1.LEVEL_TO_WINSTON; } });
36
+ var format_1 = require("./format");
37
+ Object.defineProperty(exports, "LEVEL_TO_SEVERITY", { enumerable: true, get: function () { return format_1.LEVEL_TO_SEVERITY; } });
38
+ Object.defineProperty(exports, "bigIntSafeFormat", { enumerable: true, get: function () { return format_1.bigIntSafeFormat; } });
39
+ Object.defineProperty(exports, "injectContextFormat", { enumerable: true, get: function () { return format_1.injectContextFormat; } });
40
+ Object.defineProperty(exports, "severityFormat", { enumerable: true, get: function () { return format_1.severityFormat; } });
41
+ Object.defineProperty(exports, "localPrettyFormat", { enumerable: true, get: function () { return format_1.localPrettyFormat; } });
42
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/logging/winston/src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;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 * import { RequestContextReader } from '@webpieces/core-context';\n *\n * const reader = new RequestContextReader();\n * const loggerFactory = process.env.K_SERVICE\n * ? new WinstonGcpFactory(reader) // Cloud Run → stdout JSON\n * : new WinstonConsoleFactory(reader); // local → pretty console\n * // hand to setupRuntime(new RuntimeSetupOptions(loggerFactory, ...))\n * ```\n *\n * Both backends auto-enrich every line with the logged context keys from\n * HeaderRegistry. This package depends only on @webpieces/core-util (+ winston);\n * the node RequestContextReader is passed in, not imported.\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"]}