@volontariapp/logger 0.2.7 → 0.3.0-snap-271697b

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 (34) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +2 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/logger.d.ts +3 -0
  7. package/dist/logger.d.ts.map +1 -1
  8. package/dist/logger.js +9 -2
  9. package/dist/logger.js.map +1 -1
  10. package/dist/masking.d.ts +21 -0
  11. package/dist/masking.d.ts.map +1 -0
  12. package/dist/masking.js +86 -0
  13. package/dist/masking.js.map +1 -0
  14. package/dist/test/core/logger-masking.unit.spec.d.ts +2 -0
  15. package/dist/test/core/logger-masking.unit.spec.d.ts.map +1 -0
  16. package/dist/test/core/logger-masking.unit.spec.js +62 -0
  17. package/dist/test/core/logger-masking.unit.spec.js.map +1 -0
  18. package/dist/test/core/masking.unit.spec.d.ts +2 -0
  19. package/dist/test/core/masking.unit.spec.d.ts.map +1 -0
  20. package/dist/test/core/masking.unit.spec.js +117 -0
  21. package/dist/test/core/masking.unit.spec.js.map +1 -0
  22. package/dist/test/core/trace-context.unit.spec.d.ts +2 -0
  23. package/dist/test/core/trace-context.unit.spec.d.ts.map +1 -0
  24. package/dist/test/core/trace-context.unit.spec.js +69 -0
  25. package/dist/test/core/trace-context.unit.spec.js.map +1 -0
  26. package/dist/test/factories/span-context.factory.d.ts +5 -0
  27. package/dist/test/factories/span-context.factory.d.ts.map +1 -0
  28. package/dist/test/factories/span-context.factory.js +10 -0
  29. package/dist/test/factories/span-context.factory.js.map +1 -0
  30. package/dist/trace-context.d.ts +7 -0
  31. package/dist/trace-context.d.ts.map +1 -0
  32. package/dist/trace-context.js +15 -0
  33. package/dist/trace-context.js.map +1 -0
  34. package/package.json +4 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Mask personal data and add trace correlation (tickets 3.0 and 3.1).
8
+
9
+ - Masking (3.0): metadata, positional params, error details and the message are masked before serialization, in JSON and text. Keys are matched by fragment, case-insensitively and ignoring `-` and `_` (`password`, `token`, `authorization`, `cookie`, `apikey`, `email`, `phone`, `iban`... so `x-internal-token` and `refresh_token` are covered), and strings are scanned for emails, JWTs and `Bearer` credentials. The walk is recursive, handles arrays, class instances, errors and circular references (`[Circular]`), and never mutates the object passed by the caller.
10
+ - Configurable: `new Logger({ masking: { keys, patterns, replacement } })` extends the defaults, `masking: false` disables it. New exports: `Masker`, `MaskingConfig`, `DEFAULT_SENSITIVE_KEYS`, `DEFAULT_SENSITIVE_PATTERNS`, `DEFAULT_REDACTION`, `CIRCULAR_REFERENCE`.
11
+ - Trace correlation (3.1): inside an active OpenTelemetry span, the JSON payload carries `trace_id` (hex), `dd.trace_id` (lower 64 bits, decimal) and `dd.span_id` (decimal). They are written after the caller metadata, which cannot override them. The text format is unchanged. New exports: `getTraceFields`, `TraceFields`. New dependency: `@opentelemetry/api` (`^1.9.0`, no SDK).
12
+
13
+ Behavior change: masking is on by default, so a consumer that logged an `email`, `phone` or `token` field will now see `[REDACTED]` once it bumps.
14
+
3
15
  ## 0.2.7
4
16
 
5
17
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from './colors.js';
2
2
  export * from './logger.js';
3
+ export * from './masking.js';
4
+ export * from './trace-context.js';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from './colors.js';
2
2
  export * from './logger.js';
3
+ export * from './masking.js';
4
+ export * from './trace-context.js';
3
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC"}
package/dist/logger.d.ts CHANGED
@@ -1,13 +1,16 @@
1
1
  import type { LogLevel } from './colors.js';
2
+ import type { MaskingConfig } from './masking.js';
2
3
  export interface LoggerConfig {
3
4
  context?: string;
4
5
  format?: 'json' | 'text';
5
6
  minLevel?: LogLevel;
7
+ masking?: MaskingConfig | false;
6
8
  }
7
9
  export declare class Logger {
8
10
  private readonly context;
9
11
  private readonly format;
10
12
  private readonly minLevel;
13
+ private readonly masker;
11
14
  private readonly levels;
12
15
  constructor(config?: LoggerConfig);
13
16
  log(message: unknown, ...optionalParams: unknown[]): void;
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IAEpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAMrB;gBAEU,MAAM,CAAC,EAAE,YAAY;IAM1B,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI;IAIzD,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI;IAI3D,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI;IAI1D,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI;IAI1D,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI;IAI3D,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI;IAI3D,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI;IAIpE,OAAO,CAAC,KAAK;IAkDb,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,WAAW;CAoBpB"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAIlD,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB,OAAO,CAAC,EAAE,aAAa,GAAG,KAAK,CAAC;CACjC;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAE5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAMrB;gBAEU,MAAM,CAAC,EAAE,YAAY;IAO1B,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI;IAIzD,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI;IAI3D,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI;IAI1D,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI;IAI1D,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI;IAI3D,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI;IAI3D,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI;IAIpE,OAAO,CAAC,KAAK;IAqDb,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,WAAW;CAoBpB"}
package/dist/logger.js CHANGED
@@ -1,8 +1,11 @@
1
1
  import { Colors, getLevelColor } from './colors.js';
2
+ import { Masker } from './masking.js';
3
+ import { getTraceFields } from './trace-context.js';
2
4
  export class Logger {
3
5
  context;
4
6
  format;
5
7
  minLevel;
8
+ masker;
6
9
  levels = {
7
10
  debug: 0,
8
11
  info: 1,
@@ -14,6 +17,7 @@ export class Logger {
14
17
  this.context = config?.context ?? 'App';
15
18
  this.format = config?.format ?? 'text';
16
19
  this.minLevel = config?.minLevel ?? 'debug';
20
+ this.masker = config?.masking === false ? undefined : new Masker(config?.masking);
17
21
  }
18
22
  log(message, ...optionalParams) {
19
23
  this.info(message, ...optionalParams);
@@ -47,8 +51,10 @@ export class Logger {
47
51
  contextOverride = params[params.length - 1];
48
52
  optionalArgs = params.slice(0, params.length - 1);
49
53
  }
50
- const parsedMessage = typeof message === 'string' ? message : this.stringifyUnknown(message);
51
- const meta = this.parseParams(optionalArgs);
54
+ const rawMessage = typeof message === 'string' ? message : this.stringifyUnknown(message);
55
+ const parsedMessage = this.masker?.maskString(rawMessage) ?? rawMessage;
56
+ const rawMeta = this.parseParams(optionalArgs);
57
+ const meta = this.masker?.maskRecord(rawMeta) ?? rawMeta;
52
58
  const logMethod = level === 'error' || level === 'fatal' ? 'error' : level === 'warn' ? 'warn' : 'log';
53
59
  if (this.format === 'json') {
54
60
  const payload = {
@@ -57,6 +63,7 @@ export class Logger {
57
63
  context: contextOverride,
58
64
  message: parsedMessage,
59
65
  ...meta,
66
+ ...getTraceFields(),
60
67
  };
61
68
  console[logMethod](JSON.stringify(payload));
62
69
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ export declare const DEFAULT_REDACTION = "[REDACTED]";
2
+ export declare const CIRCULAR_REFERENCE = "[Circular]";
3
+ export declare const DEFAULT_SENSITIVE_KEYS: readonly string[];
4
+ export declare const DEFAULT_SENSITIVE_PATTERNS: readonly RegExp[];
5
+ export interface MaskingConfig {
6
+ keys?: readonly string[];
7
+ patterns?: readonly RegExp[];
8
+ replacement?: string;
9
+ }
10
+ export declare class Masker {
11
+ private readonly keys;
12
+ private readonly patterns;
13
+ private readonly replacement;
14
+ constructor(config?: MaskingConfig);
15
+ maskString(value: string): string;
16
+ mask(value: unknown): unknown;
17
+ maskRecord(value: Record<string, unknown>): Record<string, unknown>;
18
+ private isSensitiveKey;
19
+ private visit;
20
+ }
21
+ //# sourceMappingURL=masking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"masking.d.ts","sourceRoot":"","sources":["../src/masking.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAC9C,eAAO,MAAM,kBAAkB,eAAe,CAAC;AAE/C,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAkBnD,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAIvD,CAAC;AAEF,MAAM,WAAW,aAAa;IAE5B,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEzB,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAE7B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAOD,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,MAAM,CAAC,EAAE,aAAa;IAU3B,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAQjC,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;IAI7B,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAI1E,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,KAAK;CAiCd"}
@@ -0,0 +1,86 @@
1
+ export const DEFAULT_REDACTION = '[REDACTED]';
2
+ export const CIRCULAR_REFERENCE = '[Circular]';
3
+ export const DEFAULT_SENSITIVE_KEYS = [
4
+ 'password',
5
+ 'passwd',
6
+ 'secret',
7
+ 'token',
8
+ 'authorization',
9
+ 'cookie',
10
+ 'apikey',
11
+ 'privatekey',
12
+ 'email',
13
+ 'phone',
14
+ 'firstname',
15
+ 'lastname',
16
+ 'birthdate',
17
+ 'iban',
18
+ 'creditcard',
19
+ 'cardnumber',
20
+ 'cvv',
21
+ ];
22
+ export const DEFAULT_SENSITIVE_PATTERNS = [
23
+ /[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}/g,
24
+ /eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*/g,
25
+ /Bearer\s+[A-Za-z0-9._~+/=-]+/gi,
26
+ ];
27
+ const normalizeKey = (key) => key.toLowerCase().replace(/[-_\s]/g, '');
28
+ const withGlobalFlag = (pattern) => pattern.global ? pattern : new RegExp(pattern.source, `${pattern.flags}g`);
29
+ export class Masker {
30
+ keys;
31
+ patterns;
32
+ replacement;
33
+ constructor(config) {
34
+ this.keys = [...DEFAULT_SENSITIVE_KEYS, ...(config?.keys ?? [])]
35
+ .map(normalizeKey)
36
+ .filter((key) => key.length > 0);
37
+ this.patterns = [...DEFAULT_SENSITIVE_PATTERNS, ...(config?.patterns ?? [])].map(withGlobalFlag);
38
+ this.replacement = config?.replacement ?? DEFAULT_REDACTION;
39
+ }
40
+ maskString(value) {
41
+ return this.patterns.reduce((masked, pattern) => masked.replace(pattern, this.replacement), value);
42
+ }
43
+ mask(value) {
44
+ return this.visit(value, new WeakSet());
45
+ }
46
+ maskRecord(value) {
47
+ return this.visit(value, new WeakSet());
48
+ }
49
+ isSensitiveKey(key) {
50
+ const normalized = normalizeKey(key);
51
+ return this.keys.some((fragment) => normalized.includes(fragment));
52
+ }
53
+ visit(value, ancestors) {
54
+ if (typeof value === 'string') {
55
+ return this.maskString(value);
56
+ }
57
+ if (typeof value !== 'object' || value === null || value instanceof Date) {
58
+ return value;
59
+ }
60
+ if (ancestors.has(value)) {
61
+ return CIRCULAR_REFERENCE;
62
+ }
63
+ ancestors.add(value);
64
+ try {
65
+ if (Array.isArray(value)) {
66
+ return value.map((item) => this.visit(item, ancestors));
67
+ }
68
+ if (value instanceof Error) {
69
+ return {
70
+ name: value.name,
71
+ message: this.maskString(value.message),
72
+ stack: value.stack === undefined ? undefined : this.maskString(value.stack),
73
+ };
74
+ }
75
+ const masked = {};
76
+ for (const [key, child] of Object.entries(value)) {
77
+ masked[key] = this.isSensitiveKey(key) ? this.replacement : this.visit(child, ancestors);
78
+ }
79
+ return masked;
80
+ }
81
+ finally {
82
+ ancestors.delete(value);
83
+ }
84
+ }
85
+ }
86
+ //# sourceMappingURL=masking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"masking.js","sourceRoot":"","sources":["../src/masking.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=logger-masking.unit.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger-masking.unit.spec.d.ts","sourceRoot":"","sources":["../../../src/test/core/logger-masking.unit.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,62 @@
1
+ import { jest, describe, it, expect, beforeEach, afterEach } from '@jest/globals';
2
+ import { Logger } from '../../logger.js';
3
+ import { DEFAULT_REDACTION } from '../../masking.js';
4
+ describe('Logger masking', () => {
5
+ let consoleLogSpy;
6
+ let consoleErrorSpy;
7
+ beforeEach(() => {
8
+ consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
9
+ consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
10
+ });
11
+ afterEach(() => {
12
+ jest.restoreAllMocks();
13
+ });
14
+ const lastOutput = () => consoleLogSpy.mock.calls[0][0];
15
+ it('should mask metadata in json format', () => {
16
+ const logger = new Logger({ format: 'json' });
17
+ logger.info('Login', { email: 'jane@example.com', password: 'hunter2', userId: 7 });
18
+ const payload = JSON.parse(lastOutput());
19
+ expect(payload).toMatchObject({
20
+ email: DEFAULT_REDACTION,
21
+ password: DEFAULT_REDACTION,
22
+ userId: 7,
23
+ });
24
+ expect(lastOutput()).not.toContain('hunter2');
25
+ expect(lastOutput()).not.toContain('jane@example.com');
26
+ });
27
+ it('should mask metadata in text format', () => {
28
+ const logger = new Logger({ format: 'text' });
29
+ logger.info('Login', { nested: { token: 'abc123' } });
30
+ expect(lastOutput()).toContain(DEFAULT_REDACTION);
31
+ expect(lastOutput()).not.toContain('abc123');
32
+ });
33
+ it('should mask patterns inside the message and error details', () => {
34
+ const logger = new Logger({ format: 'json' });
35
+ logger.error('Failed for jane@example.com', new Error('boom jane@example.com'));
36
+ const output = consoleErrorSpy.mock.calls[0][0];
37
+ expect(output).toContain(DEFAULT_REDACTION);
38
+ expect(output).not.toContain('jane@example.com');
39
+ });
40
+ it('should mask positional params', () => {
41
+ const logger = new Logger({ format: 'json' });
42
+ logger.info('Params', 'a@b.io', 3);
43
+ expect(lastOutput()).not.toContain('a@b.io');
44
+ });
45
+ it('should not mutate the metadata passed by the caller', () => {
46
+ const logger = new Logger({ format: 'json' });
47
+ const meta = { password: 'hunter2' };
48
+ logger.info('Login', meta);
49
+ expect(meta.password).toBe('hunter2');
50
+ });
51
+ it('should apply the configured keys and replacement', () => {
52
+ const logger = new Logger({ format: 'json', masking: { keys: ['ssn'], replacement: '***' } });
53
+ logger.info('Profile', { ssn: '123', password: 'x' });
54
+ expect(JSON.parse(lastOutput())).toMatchObject({ ssn: '***', password: '***' });
55
+ });
56
+ it('should let masking be disabled explicitly', () => {
57
+ const logger = new Logger({ format: 'json', masking: false });
58
+ logger.info('Login', { password: 'hunter2' });
59
+ expect(JSON.parse(lastOutput())).toMatchObject({ password: 'hunter2' });
60
+ });
61
+ });
62
+ //# sourceMappingURL=logger-masking.unit.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger-masking.unit.spec.js","sourceRoot":"","sources":["../../../src/test/core/logger-masking.unit.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=masking.unit.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"masking.unit.spec.d.ts","sourceRoot":"","sources":["../../../src/test/core/masking.unit.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,117 @@
1
+ import { describe, it, expect } from '@jest/globals';
2
+ import { CIRCULAR_REFERENCE, DEFAULT_REDACTION, Masker } from '../../masking.js';
3
+ const JWT = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U';
4
+ describe('Masker', () => {
5
+ describe('keys', () => {
6
+ it('should mask sensitive keys whatever their casing or separators', () => {
7
+ const masked = new Masker().mask({
8
+ password: 'hunter2',
9
+ 'x-internal-token': 'abc',
10
+ Authorization: 'Bearer abc',
11
+ refresh_token: 'def',
12
+ userEmail: 'jane@example.com',
13
+ phoneNumber: '0612345678',
14
+ });
15
+ expect(masked).toEqual({
16
+ password: DEFAULT_REDACTION,
17
+ 'x-internal-token': DEFAULT_REDACTION,
18
+ Authorization: DEFAULT_REDACTION,
19
+ refresh_token: DEFAULT_REDACTION,
20
+ userEmail: DEFAULT_REDACTION,
21
+ phoneNumber: DEFAULT_REDACTION,
22
+ });
23
+ });
24
+ it('should keep non sensitive keys untouched', () => {
25
+ const input = { port: 3000, host: 'localhost', ok: true, nothing: null };
26
+ expect(new Masker().mask(input)).toEqual(input);
27
+ });
28
+ it('should accept extra keys and a custom replacement', () => {
29
+ const masker = new Masker({ keys: ['ssn'], replacement: '***' });
30
+ expect(masker.mask({ SSN: '123', password: 'x' })).toEqual({ SSN: '***', password: '***' });
31
+ });
32
+ });
33
+ describe('patterns', () => {
34
+ it('should mask emails and JWTs found inside strings', () => {
35
+ const masked = new Masker().maskString(`user jane.doe+tag@example.co.uk sent ${JWT}`);
36
+ expect(masked).toBe(`user ${DEFAULT_REDACTION} sent ${DEFAULT_REDACTION}`);
37
+ });
38
+ it('should mask bearer credentials', () => {
39
+ expect(new Masker().maskString('header Bearer abc.def-123')).toBe(`header ${DEFAULT_REDACTION}`);
40
+ });
41
+ it('should accept extra patterns, even without the global flag', () => {
42
+ const masker = new Masker({ patterns: [/\d{4}-\d{4}/] });
43
+ expect(masker.maskString('a 1234-5678 b 8765-4321')).toBe(`a ${DEFAULT_REDACTION} b ${DEFAULT_REDACTION}`);
44
+ });
45
+ it('should be stable across repeated calls', () => {
46
+ const masker = new Masker();
47
+ expect(masker.maskString('a@b.io')).toBe(DEFAULT_REDACTION);
48
+ expect(masker.maskString('a@b.io')).toBe(DEFAULT_REDACTION);
49
+ });
50
+ });
51
+ describe('nested structures', () => {
52
+ it('should mask recursively in objects and arrays', () => {
53
+ const masked = new Masker().mask({
54
+ request: {
55
+ headers: { authorization: 'secret-value', accept: 'json' },
56
+ users: [
57
+ { id: 1, email: 'a@b.io' },
58
+ { id: 2, note: 'contact c@d.io' },
59
+ ],
60
+ },
61
+ });
62
+ expect(masked).toEqual({
63
+ request: {
64
+ headers: { authorization: DEFAULT_REDACTION, accept: 'json' },
65
+ users: [
66
+ { id: 1, email: DEFAULT_REDACTION },
67
+ { id: 2, note: `contact ${DEFAULT_REDACTION}` },
68
+ ],
69
+ },
70
+ });
71
+ });
72
+ it('should not mutate the original object', () => {
73
+ const input = { password: 'hunter2', nested: { token: 'abc' }, list: ['a@b.io'] };
74
+ const snapshot = JSON.stringify(input);
75
+ new Masker().mask(input);
76
+ expect(JSON.stringify(input)).toBe(snapshot);
77
+ });
78
+ it('should mask class instances', () => {
79
+ class UserDto {
80
+ id;
81
+ email;
82
+ constructor(id, email) {
83
+ this.id = id;
84
+ this.email = email;
85
+ }
86
+ }
87
+ expect(new Masker().mask(new UserDto(1, 'a@b.io'))).toEqual({
88
+ id: 1,
89
+ email: DEFAULT_REDACTION,
90
+ });
91
+ });
92
+ it('should mask errors and keep their shape', () => {
93
+ const error = new Error('failed for a@b.io');
94
+ const masked = new Masker().mask(error);
95
+ expect(masked.name).toBe('Error');
96
+ expect(masked.message).toBe(`failed for ${DEFAULT_REDACTION}`);
97
+ });
98
+ it('should keep dates and primitives', () => {
99
+ const date = new Date('2026-10-07T00:00:00.000Z');
100
+ expect(new Masker().mask({ date, count: 3, flag: false })).toEqual({
101
+ date,
102
+ count: 3,
103
+ flag: false,
104
+ });
105
+ });
106
+ it('should survive circular references', () => {
107
+ const input = { id: 1 };
108
+ input.self = input;
109
+ expect(new Masker().mask(input)).toEqual({ id: 1, self: CIRCULAR_REFERENCE });
110
+ });
111
+ it('should not flag a shared reference as circular', () => {
112
+ const shared = { id: 1 };
113
+ expect(new Masker().mask({ a: shared, b: shared })).toEqual({ a: shared, b: shared });
114
+ });
115
+ });
116
+ });
117
+ //# sourceMappingURL=masking.unit.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"masking.unit.spec.js","sourceRoot":"","sources":["../../../src/test/core/masking.unit.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=trace-context.unit.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-context.unit.spec.d.ts","sourceRoot":"","sources":["../../../src/test/core/trace-context.unit.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,69 @@
1
+ import { jest, describe, it, expect, beforeEach, afterEach } from '@jest/globals';
2
+ import { trace } from '@opentelemetry/api';
3
+ import { Logger } from '../../logger.js';
4
+ import { getTraceFields } from '../../trace-context.js';
5
+ import { SPAN_ID_HEX, TRACE_ID_HEX, createSpanContext } from '../factories/span-context.factory.js';
6
+ const DD_TRACE_ID = '9532127138774266268';
7
+ const DD_SPAN_ID = '13235353014750950193';
8
+ describe('Trace correlation', () => {
9
+ let consoleLogSpy;
10
+ beforeEach(() => {
11
+ consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
12
+ });
13
+ afterEach(() => {
14
+ jest.restoreAllMocks();
15
+ });
16
+ const activateSpan = (overrides) => {
17
+ jest
18
+ .spyOn(trace, 'getActiveSpan')
19
+ .mockReturnValue(trace.wrapSpanContext(createSpanContext(overrides)));
20
+ };
21
+ describe('getTraceFields', () => {
22
+ it('should return undefined without an active span', () => {
23
+ expect(getTraceFields()).toBeUndefined();
24
+ });
25
+ it('should convert the ids to the Datadog decimal format', () => {
26
+ activateSpan();
27
+ expect(getTraceFields()).toEqual({
28
+ trace_id: TRACE_ID_HEX,
29
+ 'dd.trace_id': DD_TRACE_ID,
30
+ 'dd.span_id': DD_SPAN_ID,
31
+ });
32
+ });
33
+ it('should ignore an invalid span context', () => {
34
+ activateSpan({ traceId: '0'.repeat(32), spanId: '0'.repeat(16) });
35
+ expect(getTraceFields()).toBeUndefined();
36
+ });
37
+ });
38
+ describe('Logger json output', () => {
39
+ const lastPayload = () => JSON.parse(consoleLogSpy.mock.calls[0][0]);
40
+ it('should add the trace fields inside an active span', () => {
41
+ activateSpan();
42
+ new Logger({ format: 'json' }).info('inside span');
43
+ expect(lastPayload()).toMatchObject({
44
+ message: 'inside span',
45
+ trace_id: TRACE_ID_HEX,
46
+ 'dd.trace_id': DD_TRACE_ID,
47
+ 'dd.span_id': DD_SPAN_ID,
48
+ });
49
+ });
50
+ it('should not add the trace fields outside a span', () => {
51
+ new Logger({ format: 'json' }).info('outside span');
52
+ const payload = lastPayload();
53
+ expect(payload).not.toHaveProperty('trace_id');
54
+ expect(payload).not.toHaveProperty('dd.trace_id');
55
+ expect(payload).not.toHaveProperty('dd.span_id');
56
+ });
57
+ it('should not let caller metadata override the trace fields', () => {
58
+ activateSpan();
59
+ new Logger({ format: 'json' }).info('spoof', { 'dd.trace_id': '1' });
60
+ expect(lastPayload()['dd.trace_id']).toBe(DD_TRACE_ID);
61
+ });
62
+ it('should leave the text format untouched', () => {
63
+ activateSpan();
64
+ new Logger({ format: 'text' }).info('text');
65
+ expect(consoleLogSpy.mock.calls[0][0]).not.toContain(SPAN_ID_HEX);
66
+ });
67
+ });
68
+ });
69
+ //# sourceMappingURL=trace-context.unit.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-context.unit.spec.js","sourceRoot":"","sources":["../../../src/test/core/trace-context.unit.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAIpG,MAAM,WAAW,GAAG,qBAAqB,CAAC;AAC1C,MAAM,UAAU,GAAG,sBAAsB,CAAC;AAE1C,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAI,aAAqD,CAAC;IAE1D,UAAU,CAAC,GAAG,EAAE;QACd,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,CAAC,SAAmD,EAAQ,EAAE;QACjF,IAAI;aACD,KAAK,CAAC,KAAK,EAAE,eAAe,CAAC;aAC7B,eAAe,CAAC,KAAK,CAAC,eAAe,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC;IAEF,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;YACxD,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;YAC9D,YAAY,EAAE,CAAC;YAEf,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC;gBAC/B,QAAQ,EAAE,YAAY;gBACtB,aAAa,EAAE,WAAW;gBAC1B,YAAY,EAAE,UAAU;aACzB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAC/C,YAAY,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YAElE,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC;QAC3C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,MAAM,WAAW,GAAG,GAAe,EAAE,CACnC,IAAI,CAAC,KAAK,CAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAc,CAAC,CAAC,CAAC,CAAe,CAAC;QAEzE,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;YAC3D,YAAY,EAAE,CAAC;YAEf,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAEnD,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,aAAa,CAAC;gBAClC,OAAO,EAAE,aAAa;gBACtB,QAAQ,EAAE,YAAY;gBACtB,aAAa,EAAE,WAAW;gBAC1B,YAAY,EAAE,UAAU;aACzB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;YACxD,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAEpD,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;YAC9B,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;YAC/C,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;YAClD,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;YAClE,YAAY,EAAE,CAAC;YAEf,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC,CAAC;YAErE,MAAM,CAAC,WAAW,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,YAAY,EAAE,CAAC;YAEf,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAE5C,MAAM,CAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAc,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { SpanContext } from '@opentelemetry/api';
2
+ export declare const TRACE_ID_HEX = "0af7651916cd43dd8448eb211c80319c";
3
+ export declare const SPAN_ID_HEX = "b7ad6b7169203331";
4
+ export declare const createSpanContext: (overrides?: Partial<SpanContext>) => SpanContext;
5
+ //# sourceMappingURL=span-context.factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"span-context.factory.d.ts","sourceRoot":"","sources":["../../../src/test/factories/span-context.factory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,eAAO,MAAM,YAAY,qCAAqC,CAAC;AAC/D,eAAO,MAAM,WAAW,qBAAqB,CAAC;AAE9C,eAAO,MAAM,iBAAiB,eAAe,OAAO,CAAC,WAAW,CAAC,KAAQ,WAKvE,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { TraceFlags } from '@opentelemetry/api';
2
+ export const TRACE_ID_HEX = '0af7651916cd43dd8448eb211c80319c';
3
+ export const SPAN_ID_HEX = 'b7ad6b7169203331';
4
+ export const createSpanContext = (overrides = {}) => ({
5
+ traceId: TRACE_ID_HEX,
6
+ spanId: SPAN_ID_HEX,
7
+ traceFlags: TraceFlags.SAMPLED,
8
+ ...overrides,
9
+ });
10
+ //# sourceMappingURL=span-context.factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"span-context.factory.js","sourceRoot":"","sources":["../../../src/test/factories/span-context.factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,MAAM,CAAC,MAAM,YAAY,GAAG,kCAAkC,CAAC;AAC/D,MAAM,CAAC,MAAM,WAAW,GAAG,kBAAkB,CAAC;AAE9C,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,YAAkC,EAAE,EAAe,EAAE,CAAC,CAAC;IACvF,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,UAAU,EAAE,UAAU,CAAC,OAAO;IAC9B,GAAG,SAAS;CACb,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ export interface TraceFields {
2
+ trace_id: string;
3
+ 'dd.trace_id': string;
4
+ 'dd.span_id': string;
5
+ }
6
+ export declare function getTraceFields(): TraceFields | undefined;
7
+ //# sourceMappingURL=trace-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-context.d.ts","sourceRoot":"","sources":["../src/trace-context.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB;AAQD,wBAAgB,cAAc,IAAI,WAAW,GAAG,SAAS,CAYxD"}
@@ -0,0 +1,15 @@
1
+ import { trace } from '@opentelemetry/api';
2
+ const DATADOG_ID_HEX_LENGTH = 16;
3
+ const toDatadogId = (hex) => BigInt(`0x${hex.slice(-DATADOG_ID_HEX_LENGTH)}`).toString();
4
+ export function getTraceFields() {
5
+ const spanContext = trace.getActiveSpan()?.spanContext();
6
+ if (spanContext === undefined || !trace.isSpanContextValid(spanContext)) {
7
+ return undefined;
8
+ }
9
+ return {
10
+ trace_id: spanContext.traceId,
11
+ 'dd.trace_id': toDatadogId(spanContext.traceId),
12
+ 'dd.span_id': toDatadogId(spanContext.spanId),
13
+ };
14
+ }
15
+ //# sourceMappingURL=trace-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-context.js","sourceRoot":"","sources":["../src/trace-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAQ3C,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAGjC,MAAM,WAAW,GAAG,CAAC,GAAW,EAAU,EAAE,CAC1C,MAAM,CAAC,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;AAE9D,MAAM,UAAU,cAAc;IAC5B,MAAM,WAAW,GAAG,KAAK,CAAC,aAAa,EAAE,EAAE,WAAW,EAAE,CAAC;IAEzD,IAAI,WAAW,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC;QACxE,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,WAAW,CAAC,OAAO;QAC7B,aAAa,EAAE,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC;QAC/C,YAAY,EAAE,WAAW,CAAC,WAAW,CAAC,MAAM,CAAC;KAC9C,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volontariapp/logger",
3
- "version": "0.2.7",
3
+ "version": "0.3.0-snap-271697b",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": true
@@ -33,6 +33,9 @@
33
33
  "test": "node --experimental-vm-modules $(yarn bin jest)",
34
34
  "test:coverage": "node --experimental-vm-modules $(yarn bin jest) --coverage --coverageReporters=\"text\" --coverageReporters=\"json-summary\" --coverageReporters=\"lcov\""
35
35
  },
36
+ "dependencies": {
37
+ "@opentelemetry/api": "^1.9.0"
38
+ },
36
39
  "devDependencies": {
37
40
  "@jest/globals": "^30.3.0",
38
41
  "@types/jest": "^30.0.0",