@polygonlabs/logger 0.0.0-0 → 0.2.0
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/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +74 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +98 -0
- package/dist/logger.js.map +1 -0
- package/package.json +10 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,SAAS,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Bindings, ChildLoggerOptions, DestinationStream, Logger } from 'pino';
|
|
2
|
+
/**
|
|
3
|
+
* Duck-typed interface for Sentry error capturing. Matches the surface of
|
|
4
|
+
* @sentry/node used by logError. Pass an initialised Sentry client to
|
|
5
|
+
* createLogger to activate automatic error capture alongside pino logging.
|
|
6
|
+
*/
|
|
7
|
+
export interface SentryAdapter {
|
|
8
|
+
captureException(err: unknown): void;
|
|
9
|
+
captureMessage(msg: string, level: string): void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* pino Logger extended with logError and a narrowed child() return type.
|
|
13
|
+
*
|
|
14
|
+
* logError is a VError/WError-aware error logging helper: use it in preference
|
|
15
|
+
* to logger.error() for caught exceptions so that VError info fields are emitted
|
|
16
|
+
* as discrete structured fields and WError cause chains are fully unwound into
|
|
17
|
+
* separate log entries.
|
|
18
|
+
*
|
|
19
|
+
* child() is overridden to return AppLogger so child loggers inherit logError
|
|
20
|
+
* and the override is preserved at any depth.
|
|
21
|
+
*
|
|
22
|
+
* Inject this type throughout your service rather than importing a module-level
|
|
23
|
+
* singleton. Construct once at the entry point and pass down via constructor
|
|
24
|
+
* arguments or function parameters.
|
|
25
|
+
*/
|
|
26
|
+
export type AppLogger = Omit<Logger, 'child'> & {
|
|
27
|
+
/**
|
|
28
|
+
* Log an error at `error` level. Mirrors pino's merge-object signature with
|
|
29
|
+
* `err` as a required key:
|
|
30
|
+
*
|
|
31
|
+
* logger.logError({ err })
|
|
32
|
+
* logger.logError({ err, requestId, userId }, 'optional message override')
|
|
33
|
+
*
|
|
34
|
+
* VError `info` fields from the full cause chain are spread into the log entry
|
|
35
|
+
* alongside any call-site context. Call-site context wins on key collision.
|
|
36
|
+
* WError wrappers are skipped — only the cause is logged.
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* Log an error at `error` level. Mirrors pino's merge-object signature with
|
|
40
|
+
* `err` as a required key:
|
|
41
|
+
*
|
|
42
|
+
* logger.logError({ err })
|
|
43
|
+
* logger.logError({ err, requestId, userId }, 'optional message override')
|
|
44
|
+
*
|
|
45
|
+
* VError `info` from the full cause chain is always emitted under the `error_info`
|
|
46
|
+
* key — never spread at the top level. This means `error_info` is a reserved key:
|
|
47
|
+
* passing it in the context object is a TypeScript error.
|
|
48
|
+
*
|
|
49
|
+
* `err` must be an `Error` instance. For non-Error caught values, narrow first or
|
|
50
|
+
* use `logger.error()` directly.
|
|
51
|
+
*/
|
|
52
|
+
logError(obj: {
|
|
53
|
+
err: Error;
|
|
54
|
+
error_info?: never;
|
|
55
|
+
} & Record<string, unknown>, message?: string): void;
|
|
56
|
+
child(bindings: Bindings, options?: ChildLoggerOptions): AppLogger;
|
|
57
|
+
};
|
|
58
|
+
export interface CreateLoggerOptions {
|
|
59
|
+
/** Enable pino-pretty output for development. Requires pino-pretty to be installed. */
|
|
60
|
+
pretty?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Custom pino destination stream. When provided, takes precedence over `pretty`.
|
|
63
|
+
* Intended for use in tests to capture log output.
|
|
64
|
+
*/
|
|
65
|
+
destination?: DestinationStream;
|
|
66
|
+
/**
|
|
67
|
+
* Optional Sentry adapter for automatic error capturing in logError.
|
|
68
|
+
* Pass your initialised @sentry/node instance (or any object satisfying
|
|
69
|
+
* SentryAdapter). Propagated automatically to all child loggers.
|
|
70
|
+
*/
|
|
71
|
+
sentry?: SentryAdapter;
|
|
72
|
+
}
|
|
73
|
+
export declare function createLogger(options?: CreateLoggerOptions): Promise<AppLogger>;
|
|
74
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAMnG;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IACrC,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAClD;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAC9C;;;;;;;;;;OAUG;IACH;;;;;;;;;;;;;OAaG;IACH,QAAQ,CACN,GAAG,EAAE;QAAE,GAAG,EAAE,KAAK,CAAC;QAAC,UAAU,CAAC,EAAE,KAAK,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,OAAO,CAAC,EAAE,MAAM,GACf,IAAI,CAAC;IACR,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;CACpE,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,uFAAuF;IACvF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC;;;;OAIG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAmCD,wBAAsB,YAAY,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC,CA2FpF"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { pino, stdSerializers } from 'pino';
|
|
2
|
+
import { VError, WError } from '@polygonlabs/verror';
|
|
3
|
+
function logError(logger, obj, message, sentry) {
|
|
4
|
+
const { err, ...context } = obj;
|
|
5
|
+
// WError is a wrapper whose own message is intentionally not the useful signal —
|
|
6
|
+
// the cause is. Skip the WError itself and recurse directly into its cause.
|
|
7
|
+
// Carry call-site context through; discard the developer's message override since
|
|
8
|
+
// it described the wrapper, not the cause.
|
|
9
|
+
if (err instanceof WError) {
|
|
10
|
+
const cause = VError.cause(err);
|
|
11
|
+
// The spread of context (Record<string, unknown>) widens err back to unknown;
|
|
12
|
+
// cast is safe because cause is narrowed to Error by the if-guard above.
|
|
13
|
+
if (cause)
|
|
14
|
+
logError(logger, { err: cause, ...context }, undefined, sentry);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
// info from the full cause chain is spread first so call-site context wins on collision.
|
|
18
|
+
const info = err instanceof VError ? VError.info(err) : {};
|
|
19
|
+
const infoEntry = Object.keys(info).length > 0 ? { error_info: info } : {};
|
|
20
|
+
logger.error({ err, ...context, ...infoEntry }, message ?? err.message);
|
|
21
|
+
sentry?.captureException(err);
|
|
22
|
+
}
|
|
23
|
+
export async function createLogger(options) {
|
|
24
|
+
// ref.self is assigned immediately after pino() returns. The formatters.log
|
|
25
|
+
// closure only fires when a log method is called — never during construction —
|
|
26
|
+
// so ref.self is always defined by the time the closure executes.
|
|
27
|
+
const ref = { self: undefined };
|
|
28
|
+
const pinoOptions = {
|
|
29
|
+
level: 'debug',
|
|
30
|
+
// Rename pino's default "msg" key to "message" for Datadog ingestion.
|
|
31
|
+
messageKey: 'message',
|
|
32
|
+
// Suppress pino's default pid and hostname fields.
|
|
33
|
+
base: undefined,
|
|
34
|
+
// Write ISO 8601 as "timestamp" — Datadog's expected field name.
|
|
35
|
+
// pino's default is Unix epoch under "time". The leading comma is required
|
|
36
|
+
// because pino appends this fragment directly onto the partially-constructed
|
|
37
|
+
// JSON string.
|
|
38
|
+
timestamp: () => `,"timestamp":"${new Date().toISOString()}"`,
|
|
39
|
+
formatters: {
|
|
40
|
+
// Emit string level labels ("info") instead of numeric values (30).
|
|
41
|
+
level(label) {
|
|
42
|
+
return { level: label };
|
|
43
|
+
},
|
|
44
|
+
// Detect a caller-supplied "timestamp" key and rename it so it cannot
|
|
45
|
+
// shadow the authoritative timestamp written by the timestamp function.
|
|
46
|
+
log(object) {
|
|
47
|
+
if ('timestamp' in object) {
|
|
48
|
+
const { timestamp, ...rest } = object;
|
|
49
|
+
ref.self?.warn({ callerTimestamp: timestamp }, 'Log call included "timestamp" in merge object — reserved key renamed to callerTimestamp. Fix the call site.');
|
|
50
|
+
return { callerTimestamp: timestamp, ...rest };
|
|
51
|
+
}
|
|
52
|
+
return object;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
serializers: {
|
|
56
|
+
err: stdSerializers.err
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
let destination = options?.destination;
|
|
60
|
+
if (!destination && options?.pretty) {
|
|
61
|
+
destination = (await import('pino-pretty')).default({
|
|
62
|
+
colorize: true,
|
|
63
|
+
timestampKey: 'timestamp',
|
|
64
|
+
translateTime: 'SYS:standard',
|
|
65
|
+
ignore: 'pid,hostname',
|
|
66
|
+
sync: true
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const base = destination ? pino(pinoOptions, destination) : pino(pinoOptions);
|
|
70
|
+
// Capture child from Logger.prototype directly rather than from the instance, so we
|
|
71
|
+
// hold a reference to the prototype method that cannot be shadowed by an own property.
|
|
72
|
+
// We call it with .call(target) where target is the raw pino instance (never the Proxy),
|
|
73
|
+
// so pino's child creation runs with a clean `this` and produces a raw child whose
|
|
74
|
+
// prototype chain is pure pino. We then wrap that child in a new Proxy via enrichLogger.
|
|
75
|
+
const pinoProtoChild = Object.getPrototypeOf(base).child;
|
|
76
|
+
function enrichLogger(instance) {
|
|
77
|
+
// Wrap in a Proxy rather than mutating the pino logger instance. Mutation would
|
|
78
|
+
// make logError and child OWN properties on the pino object, which become visible
|
|
79
|
+
// in descendant loggers via their prototype chain (pino creates children with
|
|
80
|
+
// Object.create(parent)). A Proxy intercepts only the two properties we care about
|
|
81
|
+
// and passes everything else through to the original logger untouched.
|
|
82
|
+
return new Proxy(instance, {
|
|
83
|
+
get(target, prop, receiver) {
|
|
84
|
+
if (prop === 'logError') {
|
|
85
|
+
return (obj, message) => logError(target, obj, message, options?.sentry);
|
|
86
|
+
}
|
|
87
|
+
if (prop === 'child') {
|
|
88
|
+
return (bindings, childOptions) => enrichLogger(pinoProtoChild.call(target, bindings, childOptions));
|
|
89
|
+
}
|
|
90
|
+
return Reflect.get(target, prop, receiver);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
const logger = enrichLogger(base);
|
|
95
|
+
ref.self = logger;
|
|
96
|
+
return logger;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polygonlabs/logger",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Pino-based logger with Sentry integration, configured for Datadog ingestion and prettified output capable.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/0xPolygon/apps-team-packages.git",
|
|
9
|
+
"directory": "packages/logger"
|
|
10
|
+
},
|
|
6
11
|
"publishConfig": {
|
|
7
12
|
"access": "public"
|
|
8
13
|
},
|
|
@@ -12,10 +17,12 @@
|
|
|
12
17
|
"import": "./dist/index.js"
|
|
13
18
|
}
|
|
14
19
|
},
|
|
15
|
-
"files": [
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
16
23
|
"dependencies": {
|
|
17
24
|
"pino": "^10.3.1",
|
|
18
|
-
"@polygonlabs/verror": "0.
|
|
25
|
+
"@polygonlabs/verror": "0.2.0"
|
|
19
26
|
},
|
|
20
27
|
"devDependencies": {
|
|
21
28
|
"@tsconfig/node-ts": "^23.0.0",
|