@polygonlabs/logger 0.0.0-0 → 0.1.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.
@@ -0,0 +1,3 @@
1
+ export { createLogger } from './logger.ts';
2
+ export type { AppLogger, CreateLoggerOptions, SentryAdapter } from './logger.ts';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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,2 @@
1
+ export { createLogger } from "./logger.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
@@ -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.0.0-0",
3
+ "version": "0.1.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.0.0-0"
25
+ "@polygonlabs/verror": "0.1.0"
19
26
  },
20
27
  "devDependencies": {
21
28
  "@tsconfig/node-ts": "^23.0.0",