@webpieces/bunyan 0.4.399 → 0.4.401
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/bunyan",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.401",
|
|
4
4
|
"description": "Node-only bunyan LoggerFactory backends for webpieces: Console (local pretty) + GCP (@google-cloud/logging-bunyan), 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.
|
|
27
|
-
"@webpieces/core-context": "0.4.
|
|
26
|
+
"@webpieces/core-util": "0.4.401",
|
|
27
|
+
"@webpieces/core-context": "0.4.401",
|
|
28
28
|
"bunyan": "1.8.15",
|
|
29
29
|
"@google-cloud/logging-bunyan": "5.1.0",
|
|
30
30
|
"@types/bunyan": "1.8.11"
|
|
@@ -5,21 +5,8 @@ import type { Logger as WpLogger, LoggerFactory } from '@webpieces/core-util';
|
|
|
5
5
|
* backends. Builds ONE underlying bunyan logger with the caller-chosen stream,
|
|
6
6
|
* then hands out a cached {@link BunyanLogger} per name (each a bunyan child
|
|
7
7
|
* carrying `loggerName`). Every logger reads the magic context DIRECTLY from
|
|
8
|
-
* RequestContext, so nothing is threaded through here. Subclasses
|
|
9
|
-
* the stream they pass up (GCP vs local console).
|
|
10
|
-
*
|
|
11
|
-
* The root logger's `name` is bunyan's ONE mandatory option (its constructor throws
|
|
12
|
-
* `options.name (string) is required`), and it comes from {@link ServiceInfo} — so an
|
|
13
|
-
* app names itself ONCE, in one place, for logging AND for requestIdSource. Reading it
|
|
14
|
-
* here means a forgotten `ServiceInfo.setInfo(...)` throws OUR actionable error while
|
|
15
|
-
* the process boots, rather than bunyan's opaque TypeError.
|
|
16
|
-
*
|
|
17
|
-
* `version` rides alongside as a bunyan BASE FIELD (bunyan passes unknown top-level options straight
|
|
18
|
-
* onto every record — it strips only stream/streams/level/serializers/src, and its own format
|
|
19
|
-
* version is `v`, so there is no collision). This backend previously could not stamp a version at
|
|
20
|
-
* all: it lived on winston's factory options, which made "do my logs say which build emitted them?"
|
|
21
|
-
* depend on the logging library rather than on the service. Both backends now read the ONE
|
|
22
|
-
* {@link ServiceInfo}.
|
|
8
|
+
* RequestContext (including the build `version`), so nothing is threaded through here. Subclasses
|
|
9
|
+
* differ only in the stream they pass up (GCP vs local console).
|
|
23
10
|
*
|
|
24
11
|
* Per-logger names ride as `loggerName` on each child rather than `name`, because
|
|
25
12
|
* bunyan REFUSES to let a child override `name` (`invalid options.name: child cannot
|
package/src/BunyanFactoryBase.js
CHANGED
|
@@ -3,28 +3,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BunyanFactoryBase = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const bunyan_1 = tslib_1.__importDefault(require("bunyan"));
|
|
6
|
-
const core_util_1 = require("@webpieces/core-util");
|
|
7
6
|
const BunyanLogger_1 = require("./BunyanLogger");
|
|
7
|
+
/**
|
|
8
|
+
* bunyan's ONE mandatory option is a non-empty root-logger `name` (its constructor throws
|
|
9
|
+
* `options.name (string) is required`). It is NOT the service name: the service identity is a
|
|
10
|
+
* process-global {@link ServiceInfo} fact that may not be set yet when this factory is constructed
|
|
11
|
+
* (logging must work before `setInfo`), and a bunyan root `name` is baked at `createLogger` and can
|
|
12
|
+
* never change afterwards (children throw `child cannot set logger name`). So we feed bunyan a fixed
|
|
13
|
+
* placeholder here and stamp the real build `version` per-record via {@link RequestContext.buildStructuredLogFields}
|
|
14
|
+
* (the ONE map both backends read), where it appears the moment `setInfo` runs. Locally the field is
|
|
15
|
+
* suppressed anyway (see BUNYAN_STD_FIELDS in ./streams).
|
|
16
|
+
*/
|
|
17
|
+
const BUNYAN_ROOT_NAME = 'app';
|
|
8
18
|
/**
|
|
9
19
|
* BunyanFactoryBase - shared plumbing for the bunyan {@link LoggerFactory}
|
|
10
20
|
* backends. Builds ONE underlying bunyan logger with the caller-chosen stream,
|
|
11
21
|
* then hands out a cached {@link BunyanLogger} per name (each a bunyan child
|
|
12
22
|
* carrying `loggerName`). Every logger reads the magic context DIRECTLY from
|
|
13
|
-
* RequestContext, so nothing is threaded through here. Subclasses
|
|
14
|
-
* the stream they pass up (GCP vs local console).
|
|
15
|
-
*
|
|
16
|
-
* The root logger's `name` is bunyan's ONE mandatory option (its constructor throws
|
|
17
|
-
* `options.name (string) is required`), and it comes from {@link ServiceInfo} — so an
|
|
18
|
-
* app names itself ONCE, in one place, for logging AND for requestIdSource. Reading it
|
|
19
|
-
* here means a forgotten `ServiceInfo.setInfo(...)` throws OUR actionable error while
|
|
20
|
-
* the process boots, rather than bunyan's opaque TypeError.
|
|
21
|
-
*
|
|
22
|
-
* `version` rides alongside as a bunyan BASE FIELD (bunyan passes unknown top-level options straight
|
|
23
|
-
* onto every record — it strips only stream/streams/level/serializers/src, and its own format
|
|
24
|
-
* version is `v`, so there is no collision). This backend previously could not stamp a version at
|
|
25
|
-
* all: it lived on winston's factory options, which made "do my logs say which build emitted them?"
|
|
26
|
-
* depend on the logging library rather than on the service. Both backends now read the ONE
|
|
27
|
-
* {@link ServiceInfo}.
|
|
23
|
+
* RequestContext (including the build `version`), so nothing is threaded through here. Subclasses
|
|
24
|
+
* differ only in the stream they pass up (GCP vs local console).
|
|
28
25
|
*
|
|
29
26
|
* Per-logger names ride as `loggerName` on each child rather than `name`, because
|
|
30
27
|
* bunyan REFUSES to let a child override `name` (`invalid options.name: child cannot
|
|
@@ -35,8 +32,7 @@ class BunyanFactoryBase {
|
|
|
35
32
|
loggers = new Map();
|
|
36
33
|
constructor(streams) {
|
|
37
34
|
this.base = bunyan_1.default.createLogger({
|
|
38
|
-
name:
|
|
39
|
-
version: core_util_1.ServiceInfo.getVersion(),
|
|
35
|
+
name: BUNYAN_ROOT_NAME,
|
|
40
36
|
streams,
|
|
41
37
|
});
|
|
42
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BunyanFactoryBase.js","sourceRoot":"","sources":["../../../../../packages/logging/bunyan/src/BunyanFactoryBase.ts"],"names":[],"mappings":";;;;AAAA,4DAA4B;AAE5B,
|
|
1
|
+
{"version":3,"file":"BunyanFactoryBase.js","sourceRoot":"","sources":["../../../../../packages/logging/bunyan/src/BunyanFactoryBase.ts"],"names":[],"mappings":";;;;AAAA,4DAA4B;AAE5B,iDAA8C;AAE9C;;;;;;;;;GASG;AACH,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAE/B;;;;;;;;;;;GAWG;AACH,MAAsB,iBAAiB;IAClB,IAAI,CAAS;IACb,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;IAEvD,YAAsB,OAAwB;QAC1C,IAAI,CAAC,IAAI,GAAG,gBAAM,CAAC,YAAY,CAAC;YAC5B,IAAI,EAAE,gBAAgB;YACtB,OAAO;SACV,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,2BAAY,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YACjE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ;AAnBD,8CAmBC","sourcesContent":["import Logger from 'bunyan';\nimport type { Logger as WpLogger, LoggerFactory } from '@webpieces/core-util';\nimport { BunyanLogger } from './BunyanLogger';\n\n/**\n * bunyan's ONE mandatory option is a non-empty root-logger `name` (its constructor throws\n * `options.name (string) is required`). It is NOT the service name: the service identity is a\n * process-global {@link ServiceInfo} fact that may not be set yet when this factory is constructed\n * (logging must work before `setInfo`), and a bunyan root `name` is baked at `createLogger` and can\n * never change afterwards (children throw `child cannot set logger name`). So we feed bunyan a fixed\n * placeholder here and stamp the real build `version` per-record via {@link RequestContext.buildStructuredLogFields}\n * (the ONE map both backends read), where it appears the moment `setInfo` runs. Locally the field is\n * suppressed anyway (see BUNYAN_STD_FIELDS in ./streams).\n */\nconst BUNYAN_ROOT_NAME = 'app';\n\n/**\n * BunyanFactoryBase - shared plumbing for the bunyan {@link LoggerFactory}\n * backends. Builds ONE underlying bunyan logger with the caller-chosen stream,\n * then hands out a cached {@link BunyanLogger} per name (each a bunyan child\n * carrying `loggerName`). Every logger reads the magic context DIRECTLY from\n * RequestContext (including the build `version`), so nothing is threaded through here. Subclasses\n * differ only in the stream they pass up (GCP vs local console).\n *\n * Per-logger names ride as `loggerName` on each child rather than `name`, because\n * bunyan REFUSES to let a child override `name` (`invalid options.name: child cannot\n * set logger name`).\n */\nexport abstract class BunyanFactoryBase implements LoggerFactory {\n private readonly base: Logger;\n private readonly loggers = new Map<string, WpLogger>();\n\n protected constructor(streams: Logger.Stream[]) {\n this.base = Logger.createLogger({\n name: BUNYAN_ROOT_NAME,\n streams,\n });\n }\n\n getLogger(name: string): WpLogger {\n let logger = this.loggers.get(name);\n if (!logger) {\n logger = new BunyanLogger(this.base.child({ loggerName: name }));\n this.loggers.set(name, logger);\n }\n return logger;\n }\n}\n"]}
|
package/src/BunyanLogger.d.ts
CHANGED
|
@@ -14,7 +14,6 @@ import type { Logger as WpLogger } from '@webpieces/core-util';
|
|
|
14
14
|
*/
|
|
15
15
|
export declare class BunyanLogger implements WpLogger {
|
|
16
16
|
private readonly bunyan;
|
|
17
|
-
private reportedMissingContext;
|
|
18
17
|
constructor(bunyan: Logger);
|
|
19
18
|
trace(message: string, err?: Error): void;
|
|
20
19
|
debug(message: string, err?: Error): void;
|
|
@@ -22,5 +21,4 @@ export declare class BunyanLogger implements WpLogger {
|
|
|
22
21
|
warn(message: string, err?: Error): void;
|
|
23
22
|
error(message: string, err?: Error): void;
|
|
24
23
|
private buildFields;
|
|
25
|
-
private reportMissingContextOnce;
|
|
26
24
|
}
|
package/src/BunyanLogger.js
CHANGED
|
@@ -3,11 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BunyanLogger = void 0;
|
|
4
4
|
const core_context_1 = require("@webpieces/core-context");
|
|
5
5
|
const LoggedError_1 = require("./LoggedError");
|
|
6
|
-
// A log line with no active RequestContext = a missing request-wrapping server
|
|
7
|
-
// filter. We report that once (below), never on every line.
|
|
8
|
-
const MISSING_CONTEXT_MESSAGE = 'Log emitted OUTSIDE RequestContext.run(...) — every request must be wrapped in ' +
|
|
9
|
-
'RequestContext.run() by a server filter. That filter appears to be missing: correlation ' +
|
|
10
|
-
'fields (requestId, tenant, ...) will be absent from logs until it is added. Reported once.';
|
|
11
6
|
/**
|
|
12
7
|
* The error, whole. There is deliberately NO size guard here any more.
|
|
13
8
|
*
|
|
@@ -35,9 +30,6 @@ function normalizeError(err) {
|
|
|
35
30
|
*/
|
|
36
31
|
class BunyanLogger {
|
|
37
32
|
bunyan;
|
|
38
|
-
// One-shot latch (see MISSING_CONTEXT_MESSAGE) — flips true after the first
|
|
39
|
-
// out-of-context line so we report the missing filter exactly once, not per line.
|
|
40
|
-
reportedMissingContext = false;
|
|
41
33
|
constructor(bunyan) {
|
|
42
34
|
this.bunyan = bunyan;
|
|
43
35
|
}
|
|
@@ -58,8 +50,11 @@ class BunyanLogger {
|
|
|
58
50
|
}
|
|
59
51
|
buildFields(err) {
|
|
60
52
|
const fields = {};
|
|
53
|
+
// No active RequestContext (startup, a background job, or an in-process call the caller did
|
|
54
|
+
// not wrap) simply logs with no context fields — an empty context is normal, not an error.
|
|
55
|
+
// The in-process client's ApiClientFactory.requireActiveContext() throws precisely when a
|
|
56
|
+
// request-path wrap is genuinely missing.
|
|
61
57
|
if (!core_context_1.RequestContext.isActive()) {
|
|
62
|
-
this.reportMissingContextOnce();
|
|
63
58
|
return fields;
|
|
64
59
|
}
|
|
65
60
|
// ONE loop, in HeaderRegistry.buildStructuredLogFields — the registry owns the keys and each
|
|
@@ -74,15 +69,6 @@ class BunyanLogger {
|
|
|
74
69
|
}
|
|
75
70
|
return fields;
|
|
76
71
|
}
|
|
77
|
-
// Report the missing request-wrapping filter once. Uses the RAW bunyan logger
|
|
78
|
-
// (not buildFields()), so there is no re-entrancy — just a single extra line.
|
|
79
|
-
reportMissingContextOnce() {
|
|
80
|
-
if (this.reportedMissingContext) {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
this.reportedMissingContext = true;
|
|
84
|
-
this.bunyan.error(MISSING_CONTEXT_MESSAGE);
|
|
85
|
-
}
|
|
86
72
|
}
|
|
87
73
|
exports.BunyanLogger = BunyanLogger;
|
|
88
74
|
//# sourceMappingURL=BunyanLogger.js.map
|
package/src/BunyanLogger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BunyanLogger.js","sourceRoot":"","sources":["../../../../../packages/logging/bunyan/src/BunyanLogger.ts"],"names":[],"mappings":";;;AAEA,0DAAyD;AACzD,+CAA4C;AAE5C
|
|
1
|
+
{"version":3,"file":"BunyanLogger.js","sourceRoot":"","sources":["../../../../../packages/logging/bunyan/src/BunyanLogger.ts"],"names":[],"mappings":";;;AAEA,0DAAyD;AACzD,+CAA4C;AAE5C;;;;;;;;;GASG;AACH,SAAS,cAAc,CAAC,GAAU;IAC9B,OAAO,IAAI,yBAAW,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAa,YAAY;IACQ;IAA7B,YAA6B,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAE/C,KAAK,CAAC,OAAe,EAAE,GAAW;QAC9B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAW;QAC9B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAW;QAC7B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAW;QAC7B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAW;QAC9B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;IAEO,WAAW,CAAC,GAAW;QAC3B,MAAM,MAAM,GAAkD,EAAE,CAAC;QACjE,4FAA4F;QAC5F,2FAA2F;QAC3F,0FAA0F;QAC1F,0CAA0C;QAC1C,IAAI,CAAC,6BAAc,CAAC,QAAQ,EAAE,EAAE,CAAC;YAC7B,OAAO,MAAM,CAAC;QAClB,CAAC;QAED,6FAA6F;QAC7F,iGAAiG;QACjG,8FAA8F;QAC9F,6CAA6C;QAC7C,6BAAc,CAAC,wBAAwB,EAAE,CAAC,OAAO,CAAC,CAAC,KAAsB,EAAE,IAAY,EAAE,EAAE;YACvF,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;QACzB,CAAC,CAAC,CAAC;QACH,IAAI,GAAG,EAAE,CAAC;YACN,MAAM,CAAC,KAAK,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QACxC,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ;AA7CD,oCA6CC","sourcesContent":["import type Logger from 'bunyan';\nimport type { Logger as WpLogger } from '@webpieces/core-util';\nimport { RequestContext } from '@webpieces/core-context';\nimport { LoggedError } from './LoggedError';\n\n/**\n * The error, whole. There is deliberately NO size guard here any more.\n *\n * This used to truncate an error over 100KB down to 5 stack frames and a 100-character message, to\n * keep one giant stack from blowing past Cloud Logging's per-entry limit. That traded the wrong\n * thing away: a stack trace big enough to trip the limit is precisely the one worth reading, and it\n * arrived pre-shredded. The GCP stream now SPLITS an oversized record across several complete\n * records instead (see ChunkingRawStream), so the whole stack survives and nothing is lost — which\n * makes truncating here strictly harmful.\n */\nfunction normalizeError(err: Error): LoggedError {\n return new LoggedError(err.name, err.message, err.stack);\n}\n\n/**\n * BunyanLogger - a webpieces {@link WpLogger} backed by a bunyan logger (one per\n * name, created as a bunyan child carrying `loggerName`). On every call it reads\n * the logged HeaderRegistry keys DIRECTLY from the active {@link RequestContext}\n * (secured values masked) into the bunyan fields object, and normalizes an optional\n * Error into `err: { name, message, stack }` — matching the tested trytami AppLogger.\n * The GCP stream then serializes those fields into the structured log payload.\n *\n * This context-reading is INLINED here (and duplicated in the winston logger) on\n * purpose: it must run ONLY when a bunyan/winston backend is installed, never for\n * the plain ConsoleLogger — so it does not belong on RequestContext.\n */\nexport class BunyanLogger implements WpLogger {\n constructor(private readonly bunyan: Logger) {}\n\n trace(message: string, err?: Error): void {\n this.bunyan.trace(this.buildFields(err), message);\n }\n\n debug(message: string, err?: Error): void {\n this.bunyan.debug(this.buildFields(err), message);\n }\n\n info(message: string, err?: Error): void {\n this.bunyan.info(this.buildFields(err), message);\n }\n\n warn(message: string, err?: Error): void {\n this.bunyan.warn(this.buildFields(err), message);\n }\n\n error(message: string, err?: Error): void {\n this.bunyan.error(this.buildFields(err), message);\n }\n\n private buildFields(err?: Error): Record<string, string | object | LoggedError> {\n const fields: Record<string, string | object | LoggedError> = {};\n // No active RequestContext (startup, a background job, or an in-process call the caller did\n // not wrap) simply logs with no context fields — an empty context is normal, not an error.\n // The in-process client's ApiClientFactory.requireActiveContext() throws precisely when a\n // request-path wrap is genuinely missing.\n if (!RequestContext.isActive()) {\n return fields;\n }\n\n // ONE loop, in HeaderRegistry.buildStructuredLogFields — the registry owns the keys and each\n // ContextKey masks its own value. Values may be OBJECTS (the `api` tag), so an object-valued key\n // nests into the structured payload (bunyan's GCP stream serializes fields) rather than being\n // dropped by the string-only buildLogFields.\n RequestContext.buildStructuredLogFields().forEach((value: string | object, name: string) => {\n fields[name] = value;\n });\n if (err) {\n fields['err'] = normalizeError(err);\n }\n return fields;\n }\n}\n"]}
|