@geekmidas/logger 10.0.0-alpha.98 → 10.0.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/console.cjs +25 -24
  3. package/dist/console.cjs.map +1 -1
  4. package/dist/console.d.cts +7 -4
  5. package/dist/console.d.cts.map +1 -1
  6. package/dist/console.d.mts +7 -4
  7. package/dist/console.d.mts.map +1 -1
  8. package/dist/console.mjs +25 -23
  9. package/dist/console.mjs.map +1 -1
  10. package/dist/index.cjs +3 -3
  11. package/dist/index.d.cts +2 -2
  12. package/dist/index.d.mts +2 -2
  13. package/dist/index.mjs +3 -2
  14. package/dist/pino.cjs +39 -67
  15. package/dist/pino.cjs.map +1 -1
  16. package/dist/pino.d.cts +12 -9
  17. package/dist/pino.d.cts.map +1 -1
  18. package/dist/pino.d.mts +12 -9
  19. package/dist/pino.d.mts.map +1 -1
  20. package/dist/pino.mjs +16 -66
  21. package/dist/pino.mjs.map +1 -1
  22. package/dist/redact-paths-CsK0_uz-.d.cts +19 -0
  23. package/dist/redact-paths-CsK0_uz-.d.cts.map +1 -0
  24. package/dist/redact-paths-D07eTMlH.d.mts +19 -0
  25. package/dist/redact-paths-D07eTMlH.d.mts.map +1 -0
  26. package/dist/redact-paths-D0m0DIuQ.cjs +73 -0
  27. package/dist/redact-paths-D0m0DIuQ.cjs.map +1 -0
  28. package/dist/redact-paths-DQoIXhkS.mjs +67 -0
  29. package/dist/redact-paths-DQoIXhkS.mjs.map +1 -0
  30. package/dist/redact-paths.cjs +2 -116
  31. package/dist/redact-paths.d.cts +2 -31
  32. package/dist/redact-paths.d.mts +2 -31
  33. package/dist/redact-paths.mjs +2 -113
  34. package/dist/types-BDdpcrpy.d.mts +166 -0
  35. package/dist/types-BDdpcrpy.d.mts.map +1 -0
  36. package/dist/types-Dk_k2-f_.d.cts +166 -0
  37. package/dist/types-Dk_k2-f_.d.cts.map +1 -0
  38. package/dist/types-ag_0Cvbg.cjs +38 -0
  39. package/dist/types-ag_0Cvbg.cjs.map +1 -0
  40. package/dist/types-yQ6XOihF.mjs +32 -0
  41. package/dist/types-yQ6XOihF.mjs.map +1 -0
  42. package/dist/types.cjs +2 -32
  43. package/dist/types.d.cts +2 -175
  44. package/dist/types.d.mts +2 -175
  45. package/dist/types.mjs +2 -31
  46. package/package.json +5 -35
  47. package/src/__tests__/console.spec.ts +661 -0
  48. package/src/__tests__/pino-redaction.integration.spec.ts +307 -0
  49. package/src/__tests__/pino.spec.ts +355 -0
  50. package/src/console.ts +165 -0
  51. package/src/index.ts +7 -0
  52. package/src/pino.ts +129 -0
  53. package/src/redact-paths.ts +71 -0
  54. package/src/types.ts +167 -0
  55. package/tsconfig.json +9 -0
  56. package/tsdown.config.ts +3 -0
  57. package/dist/otel.cjs +0 -113
  58. package/dist/otel.cjs.map +0 -1
  59. package/dist/otel.d.cts +0 -31
  60. package/dist/otel.d.cts.map +0 -1
  61. package/dist/otel.d.mts +0 -31
  62. package/dist/otel.d.mts.map +0 -1
  63. package/dist/otel.mjs +0 -110
  64. package/dist/otel.mjs.map +0 -1
  65. package/dist/redact-paths.cjs.map +0 -1
  66. package/dist/redact-paths.d.cts.map +0 -1
  67. package/dist/redact-paths.d.mts.map +0 -1
  68. package/dist/redact-paths.mjs.map +0 -1
  69. package/dist/types.cjs.map +0 -1
  70. package/dist/types.d.cts.map +0 -1
  71. package/dist/types.d.mts.map +0 -1
  72. package/dist/types.mjs.map +0 -1
package/src/console.ts ADDED
@@ -0,0 +1,165 @@
1
+ import type { CreateLoggerOptions, LogFn, Logger } from './types';
2
+ import { LogLevel } from './types';
3
+
4
+ /**
5
+ * Numeric priority for log levels (higher = more severe)
6
+ */
7
+ const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {
8
+ [LogLevel.Trace]: 10,
9
+ [LogLevel.Debug]: 20,
10
+ [LogLevel.Info]: 30,
11
+ [LogLevel.Warn]: 40,
12
+ [LogLevel.Error]: 50,
13
+ [LogLevel.Fatal]: 60,
14
+ [LogLevel.Silent]: 70,
15
+ };
16
+
17
+ export class ConsoleLogger implements Logger {
18
+ private readonly level: LogLevel;
19
+
20
+ /**
21
+ * Creates a new ConsoleLogger instance.
22
+ *
23
+ * @param data - Initial context data to include in all log messages
24
+ * @param level - Minimum log level to output (default: Info)
25
+ */
26
+ constructor(
27
+ readonly data: object = {},
28
+ level: LogLevel = LogLevel.Info,
29
+ ) {
30
+ this.level = level;
31
+ }
32
+
33
+ /**
34
+ * Checks if a log level should be output based on the configured minimum level.
35
+ */
36
+ private shouldLog(level: LogLevel): boolean {
37
+ if (this.level === LogLevel.Silent) {
38
+ return false;
39
+ }
40
+ return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[this.level];
41
+ }
42
+
43
+ /**
44
+ * Creates a logging function that merges context data and adds timestamps.
45
+ *
46
+ * @param logMethod - The console method to use (e.g., console.log, console.error)
47
+ * @param level - The log level for this method
48
+ * @returns A LogFn that handles both structured and simple logging
49
+ * @private
50
+ */
51
+ private createLogFn(
52
+ logMethod: (...args: any[]) => void,
53
+ level: LogLevel,
54
+ ): LogFn {
55
+ return <T extends object>(
56
+ objOrMsg: T | string,
57
+ msg?: string,
58
+ ...args: any[]
59
+ ): void => {
60
+ if (!this.shouldLog(level)) {
61
+ return;
62
+ }
63
+
64
+ const ts = Date.now();
65
+
66
+ // Handle simple string logging: logger.info('message')
67
+ if (typeof objOrMsg === 'string') {
68
+ const logData = { ...this.data, msg: objOrMsg, ts };
69
+ logMethod(logData, ...args);
70
+ return;
71
+ }
72
+
73
+ // Handle structured logging: logger.info({ data }, 'message')
74
+ const logData = msg
75
+ ? { ...this.data, ...objOrMsg, msg, ts }
76
+ : { ...this.data, ...objOrMsg, ts };
77
+ logMethod(logData, ...args);
78
+ };
79
+ }
80
+
81
+ /** Trace level logging function */
82
+ trace: LogFn = this.createLogFn(console.trace.bind(console), LogLevel.Trace);
83
+ /** Debug level logging function */
84
+ debug: LogFn = this.createLogFn(console.debug.bind(console), LogLevel.Debug);
85
+ /** Info level logging function */
86
+ info: LogFn = this.createLogFn(console.info.bind(console), LogLevel.Info);
87
+ /** Warning level logging function */
88
+ warn: LogFn = this.createLogFn(console.warn.bind(console), LogLevel.Warn);
89
+ /** Error level logging function */
90
+ error: LogFn = this.createLogFn(console.error.bind(console), LogLevel.Error);
91
+ /** Fatal level logging function (uses console.error) */
92
+ fatal: LogFn = this.createLogFn(console.error.bind(console), LogLevel.Fatal);
93
+
94
+ /**
95
+ * Creates a child logger with additional context data.
96
+ * The child logger inherits all context from the parent and adds its own.
97
+ *
98
+ * @param obj - Additional context data for the child logger
99
+ * @returns A new ConsoleLogger instance with merged context
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const parentLogger = new ConsoleLogger({ app: 'myApp' });
104
+ * const childLogger = parentLogger.child({ module: 'database' });
105
+ * childLogger.info({ query: 'SELECT * FROM users' }, 'Query executed');
106
+ * // Context includes both { app: 'myApp' } and { module: 'database' }
107
+ * ```
108
+ */
109
+ child(obj: object): Logger {
110
+ return new ConsoleLogger(
111
+ {
112
+ ...this.data,
113
+ ...obj,
114
+ },
115
+ this.level,
116
+ );
117
+ }
118
+ }
119
+
120
+ /**
121
+ * @example Basic usage
122
+ * ```typescript
123
+ * const logger = new ConsoleLogger({ app: 'myApp' });
124
+ * logger.info({ action: 'start' }, 'Application starting');
125
+ * // Logs: { app: 'myApp', action: 'start', msg: 'Application starting', ts: 1234567890 }
126
+ * ```
127
+ *
128
+ * @example Child logger usage
129
+ * ```typescript
130
+ * const childLogger = logger.child({ module: 'auth' });
131
+ * childLogger.debug({ userId: 123 }, 'User authenticated');
132
+ * // Logs: { app: 'myApp', module: 'auth', userId: 123, msg: 'User authenticated', ts: 1234567891 }
133
+ * ```
134
+ *
135
+ * @example Error logging with context
136
+ * ```typescript
137
+ * try {
138
+ * await someOperation();
139
+ * } catch (error) {
140
+ * logger.error({ error, operation: 'someOperation' }, 'Operation failed');
141
+ * }
142
+ * ```
143
+ */
144
+
145
+ export const DEFAULT_LOGGER = new ConsoleLogger() as any;
146
+
147
+ /**
148
+ * Creates a console logger with the same API as pino's createLogger.
149
+ *
150
+ * @param options - Logger configuration options
151
+ * @returns A ConsoleLogger instance
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * import { createLogger } from '@geekmidas/logger/console';
156
+ * import { LogLevel } from '@geekmidas/logger';
157
+ *
158
+ * const logger = createLogger({ level: LogLevel.Debug });
159
+ * logger.debug('This will be logged');
160
+ * logger.trace('This will NOT be logged (below Debug level)');
161
+ * ```
162
+ */
163
+ export function createLogger(options: CreateLoggerOptions = {}): Logger {
164
+ return new ConsoleLogger({}, options.level ?? LogLevel.Info);
165
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export {
2
+ type CreateLoggerOptions,
3
+ type LogFn,
4
+ type Logger,
5
+ LogLevel,
6
+ type RedactOptions,
7
+ } from './types';
package/src/pino.ts ADDED
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Pino logger with built-in redaction support for sensitive data.
3
+ *
4
+ * @example
5
+ * ```typescript
6
+ * import { createLogger, DEFAULT_REDACT_PATHS } from '@geekmidas/logger/pino';
7
+ *
8
+ * // Enable redaction with sensible defaults
9
+ * const logger = createLogger({ redact: true });
10
+ *
11
+ * // Sensitive data is automatically masked
12
+ * logger.info({ password: 'secret123', user: 'john' }, 'Login');
13
+ * // Output: { password: '[Redacted]', user: 'john' } Login
14
+ *
15
+ * // Add custom paths (merged with defaults)
16
+ * const logger2 = createLogger({ redact: ['user.ssn'] });
17
+ *
18
+ * // Override defaults for full control
19
+ * const logger3 = createLogger({
20
+ * redact: {
21
+ * paths: ['onlyThis'],
22
+ * resolution: 'override',
23
+ * }
24
+ * });
25
+ * ```
26
+ *
27
+ * @module
28
+ */
29
+ import { pino } from 'pino';
30
+ import { DEFAULT_REDACT_PATHS } from './redact-paths';
31
+ import type { CreateLoggerOptions, RedactOptions } from './types';
32
+
33
+ // Re-export for backwards compatibility
34
+ export { DEFAULT_REDACT_PATHS } from './redact-paths';
35
+
36
+ /**
37
+ * Type for the resolved pino redact config (without our custom resolution field).
38
+ */
39
+ type PinoRedactConfig =
40
+ | string[]
41
+ | {
42
+ paths: string[];
43
+ censor?: string | ((value: unknown, path: string[]) => unknown);
44
+ remove?: boolean;
45
+ };
46
+
47
+ /**
48
+ * Resolves redaction configuration from options.
49
+ * Returns undefined if redaction is disabled, or a pino-compatible redact config.
50
+ *
51
+ * By default (resolution: 'merge'), custom paths are merged with DEFAULT_REDACT_PATHS.
52
+ * With resolution: 'override', only the custom paths are used.
53
+ */
54
+ function resolveRedactConfig(
55
+ redact: boolean | RedactOptions | undefined,
56
+ ): PinoRedactConfig | undefined {
57
+ if (redact === undefined || redact === false) {
58
+ return undefined;
59
+ }
60
+
61
+ if (redact === true) {
62
+ return DEFAULT_REDACT_PATHS;
63
+ }
64
+
65
+ // Array syntax - merge with defaults
66
+ if (Array.isArray(redact)) {
67
+ return [...DEFAULT_REDACT_PATHS, ...redact];
68
+ }
69
+
70
+ // Object syntax - check resolution mode
71
+ const { resolution = 'merge', paths, censor, remove } = redact;
72
+
73
+ const resolvedPaths =
74
+ resolution === 'override' ? paths : [...DEFAULT_REDACT_PATHS, ...paths];
75
+
76
+ // Return clean pino config without our resolution field
77
+ const config: PinoRedactConfig = { paths: resolvedPaths };
78
+ if (censor !== undefined) config.censor = censor;
79
+ if (remove !== undefined) config.remove = remove;
80
+
81
+ return config;
82
+ }
83
+
84
+ /**
85
+ * Creates a pino logger instance with optional redaction support.
86
+ *
87
+ * @param options - Logger configuration options
88
+ * @returns A configured pino logger instance
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * // Basic logger
93
+ * const logger = createLogger({ level: 'debug' });
94
+ *
95
+ * // With redaction enabled
96
+ * const secureLogger = createLogger({ redact: true });
97
+ *
98
+ * // Pretty printing in development
99
+ * const devLogger = createLogger({ pretty: true, redact: true });
100
+ * ```
101
+ */
102
+ export function createLogger(options: CreateLoggerOptions = {}) {
103
+ // @ts-expect-error
104
+ const pretty = options?.pretty && process.NODE_ENV !== 'production';
105
+ const baseOptions = pretty
106
+ ? {
107
+ transport: {
108
+ target: 'pino-pretty',
109
+ options: { colorize: true },
110
+ },
111
+ }
112
+ : {};
113
+
114
+ const redact = resolveRedactConfig(options.redact);
115
+
116
+ return pino({
117
+ ...baseOptions,
118
+ ...(options.level && { level: options.level }),
119
+ ...(redact && { redact }),
120
+ formatters: {
121
+ bindings() {
122
+ return { nodeVersion: process.version };
123
+ },
124
+ level: (label) => {
125
+ return { level: label.toUpperCase() };
126
+ },
127
+ },
128
+ });
129
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Default sensitive field paths for redaction.
3
+ *
4
+ * These paths are automatically used when `redact: true` is set,
5
+ * and merged with custom paths unless `resolution: 'override'` is specified.
6
+ *
7
+ * Includes:
8
+ * - Authentication: password, token, apiKey, authorization, credentials
9
+ * - Headers: authorization, cookie, x-api-key, x-auth-token
10
+ * - Personal data: ssn, creditCard, cvv, pin
11
+ * - Secrets: secret, connectionString, databaseUrl
12
+ * - Wildcards: *.password, *.secret, *.token (catches nested fields)
13
+ */
14
+ export const DEFAULT_REDACT_PATHS: string[] = [
15
+ // Authentication & authorization
16
+ 'password',
17
+ 'pass',
18
+ 'passwd',
19
+ 'secret',
20
+ 'token',
21
+ 'accessToken',
22
+ 'refreshToken',
23
+ 'idToken',
24
+ 'apiKey',
25
+ 'api_key',
26
+ 'apikey',
27
+ 'auth',
28
+ 'authorization',
29
+ 'credential',
30
+ 'credentials',
31
+
32
+ // Common nested patterns (headers, body, etc.)
33
+ '*.password',
34
+ '*.secret',
35
+ '*.token',
36
+ '*.apiKey',
37
+ '*.api_key',
38
+ '*.authorization',
39
+ '*.accessToken',
40
+ '*.refreshToken',
41
+
42
+ // HTTP headers (case variations)
43
+ 'headers.authorization',
44
+ 'headers.Authorization',
45
+ 'headers["authorization"]',
46
+ 'headers["Authorization"]',
47
+ 'headers.cookie',
48
+ 'headers.Cookie',
49
+ 'headers["x-api-key"]',
50
+ 'headers["X-Api-Key"]',
51
+ 'headers["x-auth-token"]',
52
+ 'headers["X-Auth-Token"]',
53
+
54
+ // Common sensitive data fields
55
+ 'ssn',
56
+ 'socialSecurityNumber',
57
+ 'social_security_number',
58
+ 'creditCard',
59
+ 'credit_card',
60
+ 'cardNumber',
61
+ 'card_number',
62
+ 'cvv',
63
+ 'cvc',
64
+ 'pin',
65
+
66
+ // Database & connection strings
67
+ 'connectionString',
68
+ 'connection_string',
69
+ 'databaseUrl',
70
+ 'database_url',
71
+ ];
package/src/types.ts ADDED
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Logging function type that supports both structured and simple logging.
3
+ * Can be called with an object for structured logging or just a message string.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * // Structured logging with context object
8
+ * logger.info({ userId: 123, action: 'login' }, 'User logged in');
9
+ *
10
+ * // Simple string logging
11
+ * logger.info('Application started');
12
+ * ```
13
+ */
14
+ export type LogFn = {
15
+ /** Structured logging with context object, optional message, and additional arguments */
16
+ <T extends object>(obj: T, msg?: string, ...args: any[]): void;
17
+ /** Simple string logging */
18
+ (msg: string): void;
19
+ };
20
+
21
+ /**
22
+ * Standard logger interface with multiple log levels and child logger support.
23
+ * Follows common logging patterns with structured logging capabilities.
24
+ *
25
+ * @interface Logger
26
+ */
27
+ export interface Logger {
28
+ /** Debug level logging - verbose information for debugging */
29
+ debug: LogFn;
30
+ /** Info level logging - general informational messages */
31
+ info: LogFn;
32
+ /** Warning level logging - potentially harmful situations */
33
+ warn: LogFn;
34
+ /** Error level logging - error events that might still allow the application to continue */
35
+ error: LogFn;
36
+ /** Fatal level logging - severe errors that will likely cause the application to abort */
37
+ fatal: LogFn;
38
+ /** Trace level logging - most detailed information */
39
+ trace: LogFn;
40
+ /**
41
+ * Creates a child logger with additional context.
42
+ * Child loggers inherit parent context and add their own.
43
+ *
44
+ * @param obj - Additional context to include in all child logger calls
45
+ * @returns A new Logger instance with merged context
46
+ */
47
+ child: (obj: object) => Logger;
48
+ }
49
+
50
+ /**
51
+ * Console-based logger implementation that outputs to standard console methods.
52
+ * Supports structured logging with automatic timestamp injection and context inheritance.
53
+ *
54
+ * @implements {Logger}
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const logger = new ConsoleLogger({ app: 'myApp', version: '1.0.0' });
59
+ * logger.info({ userId: 123 }, 'User action performed');
60
+ * // Output: { app: 'myApp', version: '1.0.0', userId: 123, ts: 1234567890 } User action performed
61
+ *
62
+ * const childLogger = logger.child({ module: 'auth' });
63
+ * childLogger.debug({ action: 'validate' }, 'Validating token');
64
+ * // Output: { app: 'myApp', version: '1.0.0', module: 'auth', action: 'validate', ts: 1234567891 } Validating token
65
+ * ```
66
+ */
67
+ export enum LogLevel {
68
+ Trace = 'trace',
69
+ Debug = 'debug',
70
+ Info = 'info',
71
+ Warn = 'warn',
72
+ Error = 'error',
73
+ Fatal = 'fatal',
74
+ Silent = 'silent',
75
+ }
76
+
77
+ /**
78
+ * Redaction configuration for masking sensitive data in logs.
79
+ * Uses pino's fast-redact library under the hood.
80
+ *
81
+ * By default, custom paths are merged with the default sensitive paths.
82
+ * Use `resolution: 'override'` to use only your custom paths.
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * // Simple path array (merges with defaults)
87
+ * redact: ['user.ssn', 'custom.field']
88
+ *
89
+ * // Override defaults completely
90
+ * redact: {
91
+ * paths: ['only.these.paths'],
92
+ * resolution: 'override',
93
+ * }
94
+ *
95
+ * // With custom censor
96
+ * redact: {
97
+ * paths: ['extra.secret'],
98
+ * censor: '***',
99
+ * }
100
+ *
101
+ * // Remove fields entirely
102
+ * redact: {
103
+ * paths: ['temporary.data'],
104
+ * remove: true,
105
+ * }
106
+ * ```
107
+ */
108
+ export type RedactOptions =
109
+ | string[]
110
+ | {
111
+ /** Paths to redact using dot notation or bracket notation for special chars */
112
+ paths: string[];
113
+ /** Custom replacement text (default: '[REDACTED]') */
114
+ censor?: string | ((value: unknown, path: string[]) => unknown);
115
+ /** Remove the field entirely instead of replacing (default: false) */
116
+ remove?: boolean;
117
+ /**
118
+ * How to combine custom paths with default sensitive paths.
119
+ * - 'merge': Custom paths are added to default paths (default)
120
+ * - 'override': Only custom paths are used, defaults are ignored
121
+ */
122
+ resolution?: 'merge' | 'override';
123
+ };
124
+
125
+ export type CreateLoggerOptions = {
126
+ /** Enable pretty printing with colors (disabled in production) */
127
+ pretty?: boolean;
128
+ /** Minimum log level to output */
129
+ level?: LogLevel;
130
+ /**
131
+ * Redaction configuration for masking sensitive data.
132
+ *
133
+ * - `true`: Uses default sensitive paths (password, token, secret, etc.)
134
+ * - `false` or `undefined`: No redaction applied
135
+ * - `string[]`: Custom paths merged with defaults
136
+ * - `object`: Advanced config with paths, censor, remove, and resolution options
137
+ *
138
+ * By default, custom paths are **merged** with the default sensitive paths.
139
+ * Use `resolution: 'override'` to disable defaults and use only your paths.
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * // Use defaults only
144
+ * createLogger({ redact: true });
145
+ *
146
+ * // Add custom paths (merged with defaults)
147
+ * createLogger({ redact: ['user.ssn', 'custom.field'] });
148
+ *
149
+ * // Override defaults completely
150
+ * createLogger({
151
+ * redact: {
152
+ * paths: ['only.these.paths'],
153
+ * resolution: 'override',
154
+ * }
155
+ * });
156
+ *
157
+ * // Merge with custom censor
158
+ * createLogger({
159
+ * redact: {
160
+ * paths: ['extra.secret'],
161
+ * censor: '***',
162
+ * }
163
+ * });
164
+ * ```
165
+ */
166
+ redact?: boolean | RedactOptions;
167
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "composite": true
7
+ },
8
+ "include": ["src/**/*"]
9
+ }
@@ -0,0 +1,3 @@
1
+ import { defineConfig } from 'tsdown';
2
+
3
+ export default defineConfig({});
package/dist/otel.cjs DELETED
@@ -1,113 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- let _opentelemetry_api_logs = require("@opentelemetry/api-logs");
3
- //#region src/otel.ts
4
- /**
5
- * The bridge from pino to OpenTelemetry logs.
6
- *
7
- * A pino record is also emitted through `@opentelemetry/api-logs` when a
8
- * `LoggerProvider` is registered globally — as the generated telemetry setup
9
- * of a production server does. Nothing hooks module loading, so it works the
10
- * same in a single bundled file as from `node_modules`.
11
- *
12
- * `@opentelemetry/api-logs` keeps its provider on `globalThis` under a
13
- * `Symbol.for` key, so the copy imported here and the copy the SDK registered
14
- * through share one provider even when a bundle holds both. That is why the
15
- * provider is looked up per record rather than once: a logger obtained before
16
- * the SDK registered — or through another copy's proxy — would stay a no-op.
17
- *
18
- * @module
19
- */
20
- /** The instrumentation scope every bridged record carries. */
21
- const OTEL_SCOPE = "@geekmidas/logger";
22
- /** Pino's level labels, upper-cased as the logger prints them. */
23
- const SEVERITY = {
24
- TRACE: _opentelemetry_api_logs.SeverityNumber.TRACE,
25
- DEBUG: _opentelemetry_api_logs.SeverityNumber.DEBUG,
26
- INFO: _opentelemetry_api_logs.SeverityNumber.INFO,
27
- WARN: _opentelemetry_api_logs.SeverityNumber.WARN,
28
- ERROR: _opentelemetry_api_logs.SeverityNumber.ERROR,
29
- FATAL: _opentelemetry_api_logs.SeverityNumber.FATAL
30
- };
31
- /** Pino's numeric levels, for a record whose level was not formatted. */
32
- function severityFromNumber(level) {
33
- if (level >= 60) return _opentelemetry_api_logs.SeverityNumber.FATAL;
34
- if (level >= 50) return _opentelemetry_api_logs.SeverityNumber.ERROR;
35
- if (level >= 40) return _opentelemetry_api_logs.SeverityNumber.WARN;
36
- if (level >= 30) return _opentelemetry_api_logs.SeverityNumber.INFO;
37
- if (level >= 20) return _opentelemetry_api_logs.SeverityNumber.DEBUG;
38
- return _opentelemetry_api_logs.SeverityNumber.TRACE;
39
- }
40
- const SEVERITY_TEXT = {
41
- [_opentelemetry_api_logs.SeverityNumber.TRACE]: "TRACE",
42
- [_opentelemetry_api_logs.SeverityNumber.DEBUG]: "DEBUG",
43
- [_opentelemetry_api_logs.SeverityNumber.INFO]: "INFO",
44
- [_opentelemetry_api_logs.SeverityNumber.WARN]: "WARN",
45
- [_opentelemetry_api_logs.SeverityNumber.ERROR]: "ERROR",
46
- [_opentelemetry_api_logs.SeverityNumber.FATAL]: "FATAL"
47
- };
48
- let cached;
49
- /** The bridge's logger from whichever provider is registered now. */
50
- function otelLogger() {
51
- const provider = _opentelemetry_api_logs.logs.getLoggerProvider();
52
- if (cached?.provider !== provider) cached = {
53
- provider,
54
- logger: provider.getLogger(OTEL_SCOPE)
55
- };
56
- return cached.logger;
57
- }
58
- /**
59
- * Emit one serialized pino line through OpenTelemetry, when a provider is
60
- * registered that wants it.
61
- *
62
- * The line is what pino is about to write — after path redaction and the
63
- * URL-credential redaction — so the exported copy is exactly as redacted as
64
- * stdout. It runs synchronously inside the log call, so the active context —
65
- * and with it the trace and span ids — is the caller's.
66
- *
67
- * Without a registered provider the logger is a no-op whose `enabled()` is
68
- * false, and the line is not parsed.
69
- */
70
- function emitToOtel(line) {
71
- const logger = otelLogger();
72
- if (!logger.enabled()) return;
73
- let record;
74
- try {
75
- record = JSON.parse(line);
76
- } catch {
77
- return;
78
- }
79
- const { level, time, msg, ...attributes } = record;
80
- const severityNumber = typeof level === "number" ? severityFromNumber(level) : SEVERITY[String(level).toUpperCase()] ?? _opentelemetry_api_logs.SeverityNumber.UNSPECIFIED;
81
- if (!logger.enabled({ severityNumber })) return;
82
- logger.emit({
83
- severityNumber,
84
- severityText: SEVERITY_TEXT[severityNumber] ?? (level === void 0 ? void 0 : String(level)),
85
- ...msg !== void 0 && { body: String(msg) },
86
- ...typeof time === "number" && { timestamp: time },
87
- attributes
88
- });
89
- }
90
- /**
91
- * The bridge as a pino `streamWrite` hook, for a logger made with `pino()`
92
- * directly rather than `createLogger` — which installs it already.
93
- *
94
- * @example
95
- * ```typescript
96
- * import pino from 'pino';
97
- * import { otelStreamWrite } from '@geekmidas/logger/otel';
98
- *
99
- * const logger = pino({ hooks: { streamWrite: otelStreamWrite } });
100
- * ```
101
- */
102
- function otelStreamWrite(line) {
103
- try {
104
- emitToOtel(line);
105
- } catch {}
106
- return line;
107
- }
108
- //#endregion
109
- exports.OTEL_SCOPE = OTEL_SCOPE;
110
- exports.emitToOtel = emitToOtel;
111
- exports.otelStreamWrite = otelStreamWrite;
112
-
113
- //# sourceMappingURL=otel.cjs.map
package/dist/otel.cjs.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"otel.cjs","names":["SeverityNumber","logs"],"sources":["../src/otel.ts"],"sourcesContent":["/**\n * The bridge from pino to OpenTelemetry logs.\n *\n * A pino record is also emitted through `@opentelemetry/api-logs` when a\n * `LoggerProvider` is registered globally — as the generated telemetry setup\n * of a production server does. Nothing hooks module loading, so it works the\n * same in a single bundled file as from `node_modules`.\n *\n * `@opentelemetry/api-logs` keeps its provider on `globalThis` under a\n * `Symbol.for` key, so the copy imported here and the copy the SDK registered\n * through share one provider even when a bundle holds both. That is why the\n * provider is looked up per record rather than once: a logger obtained before\n * the SDK registered — or through another copy's proxy — would stay a no-op.\n *\n * @module\n */\nimport {\n\ttype AnyValueMap,\n\ttype Logger,\n\ttype LoggerProvider,\n\tlogs,\n\tSeverityNumber,\n} from '@opentelemetry/api-logs';\n\n/** The instrumentation scope every bridged record carries. */\nexport const OTEL_SCOPE = '@geekmidas/logger';\n\n/** Pino's level labels, upper-cased as the logger prints them. */\nconst SEVERITY: Record<string, SeverityNumber> = {\n\tTRACE: SeverityNumber.TRACE,\n\tDEBUG: SeverityNumber.DEBUG,\n\tINFO: SeverityNumber.INFO,\n\tWARN: SeverityNumber.WARN,\n\tERROR: SeverityNumber.ERROR,\n\tFATAL: SeverityNumber.FATAL,\n};\n\n/** Pino's numeric levels, for a record whose level was not formatted. */\nfunction severityFromNumber(level: number): SeverityNumber {\n\tif (level >= 60) return SeverityNumber.FATAL;\n\tif (level >= 50) return SeverityNumber.ERROR;\n\tif (level >= 40) return SeverityNumber.WARN;\n\tif (level >= 30) return SeverityNumber.INFO;\n\tif (level >= 20) return SeverityNumber.DEBUG;\n\treturn SeverityNumber.TRACE;\n}\n\nconst SEVERITY_TEXT: Partial<Record<SeverityNumber, string>> = {\n\t[SeverityNumber.TRACE]: 'TRACE',\n\t[SeverityNumber.DEBUG]: 'DEBUG',\n\t[SeverityNumber.INFO]: 'INFO',\n\t[SeverityNumber.WARN]: 'WARN',\n\t[SeverityNumber.ERROR]: 'ERROR',\n\t[SeverityNumber.FATAL]: 'FATAL',\n};\n\nlet cached: { provider: LoggerProvider; logger: Logger } | undefined;\n\n/** The bridge's logger from whichever provider is registered now. */\nfunction otelLogger(): Logger {\n\tconst provider = logs.getLoggerProvider();\n\tif (cached?.provider !== provider) {\n\t\tcached = { provider, logger: provider.getLogger(OTEL_SCOPE) };\n\t}\n\treturn cached.logger;\n}\n\n/**\n * Emit one serialized pino line through OpenTelemetry, when a provider is\n * registered that wants it.\n *\n * The line is what pino is about to write — after path redaction and the\n * URL-credential redaction — so the exported copy is exactly as redacted as\n * stdout. It runs synchronously inside the log call, so the active context —\n * and with it the trace and span ids — is the caller's.\n *\n * Without a registered provider the logger is a no-op whose `enabled()` is\n * false, and the line is not parsed.\n */\nexport function emitToOtel(line: string): void {\n\tconst logger = otelLogger();\n\tif (!logger.enabled()) return;\n\n\tlet record: Record<string, unknown>;\n\ttry {\n\t\trecord = JSON.parse(line);\n\t} catch {\n\t\treturn;\n\t}\n\n\tconst { level, time, msg, ...attributes } = record;\n\tconst severityNumber =\n\t\ttypeof level === 'number'\n\t\t\t? severityFromNumber(level)\n\t\t\t: (SEVERITY[String(level).toUpperCase()] ?? SeverityNumber.UNSPECIFIED);\n\n\tif (!logger.enabled({ severityNumber })) return;\n\n\tlogger.emit({\n\t\tseverityNumber,\n\t\tseverityText:\n\t\t\tSEVERITY_TEXT[severityNumber] ??\n\t\t\t(level === undefined ? undefined : String(level)),\n\t\t...(msg !== undefined && { body: String(msg) }),\n\t\t...(typeof time === 'number' && { timestamp: time }),\n\t\tattributes: attributes as AnyValueMap,\n\t});\n}\n\n/**\n * The bridge as a pino `streamWrite` hook, for a logger made with `pino()`\n * directly rather than `createLogger` — which installs it already.\n *\n * @example\n * ```typescript\n * import pino from 'pino';\n * import { otelStreamWrite } from '@geekmidas/logger/otel';\n *\n * const logger = pino({ hooks: { streamWrite: otelStreamWrite } });\n * ```\n */\nexport function otelStreamWrite(line: string): string {\n\ttry {\n\t\temitToOtel(line);\n\t} catch {\n\t\t// Exporting a log is never a reason to lose it from stdout.\n\t}\n\treturn line;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAyBA,MAAa,aAAa;;AAG1B,MAAM,WAA2C;CAChD,OAAOA,wBAAAA,eAAe;CACtB,OAAOA,wBAAAA,eAAe;CACtB,MAAMA,wBAAAA,eAAe;CACrB,MAAMA,wBAAAA,eAAe;CACrB,OAAOA,wBAAAA,eAAe;CACtB,OAAOA,wBAAAA,eAAe;AACvB;;AAGA,SAAS,mBAAmB,OAA+B;CAC1D,IAAI,SAAS,IAAI,OAAOA,wBAAAA,eAAe;CACvC,IAAI,SAAS,IAAI,OAAOA,wBAAAA,eAAe;CACvC,IAAI,SAAS,IAAI,OAAOA,wBAAAA,eAAe;CACvC,IAAI,SAAS,IAAI,OAAOA,wBAAAA,eAAe;CACvC,IAAI,SAAS,IAAI,OAAOA,wBAAAA,eAAe;CACvC,OAAOA,wBAAAA,eAAe;AACvB;AAEA,MAAM,gBAAyD;EAC7DA,wBAAAA,eAAe,QAAQ;EACvBA,wBAAAA,eAAe,QAAQ;EACvBA,wBAAAA,eAAe,OAAO;EACtBA,wBAAAA,eAAe,OAAO;EACtBA,wBAAAA,eAAe,QAAQ;EACvBA,wBAAAA,eAAe,QAAQ;AACzB;AAEA,IAAI;;AAGJ,SAAS,aAAqB;CAC7B,MAAM,WAAWC,wBAAAA,KAAK,kBAAkB;CACxC,IAAI,QAAQ,aAAa,UACxB,SAAS;EAAE;EAAU,QAAQ,SAAS,UAAU,UAAU;CAAE;CAE7D,OAAO,OAAO;AACf;;;;;;;;;;;;;AAcA,SAAgB,WAAW,MAAoB;CAC9C,MAAM,SAAS,WAAW;CAC1B,IAAI,CAAC,OAAO,QAAQ,GAAG;CAEvB,IAAI;CACJ,IAAI;EACH,SAAS,KAAK,MAAM,IAAI;CACzB,QAAQ;EACP;CACD;CAEA,MAAM,EAAE,OAAO,MAAM,KAAK,GAAG,eAAe;CAC5C,MAAM,iBACL,OAAO,UAAU,WACd,mBAAmB,KAAK,IACvB,SAAS,OAAO,KAAK,CAAC,CAAC,YAAY,MAAMD,wBAAAA,eAAe;CAE7D,IAAI,CAAC,OAAO,QAAQ,EAAE,eAAe,CAAC,GAAG;CAEzC,OAAO,KAAK;EACX;EACA,cACC,cAAc,oBACb,UAAU,KAAA,IAAY,KAAA,IAAY,OAAO,KAAK;EAChD,GAAI,QAAQ,KAAA,KAAa,EAAE,MAAM,OAAO,GAAG,EAAE;EAC7C,GAAI,OAAO,SAAS,YAAY,EAAE,WAAW,KAAK;EACtC;CACb,CAAC;AACF;;;;;;;;;;;;;AAcA,SAAgB,gBAAgB,MAAsB;CACrD,IAAI;EACH,WAAW,IAAI;CAChB,QAAQ,CAER;CACA,OAAO;AACR"}