@leaflink/snitch 0.0.0-PR-56--4e938e8 → 0.0.0-PR-56--70fa2e6

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 CHANGED
@@ -1,6 +1,7 @@
1
1
  export type LogLevel = 'error' | 'warn' | 'info' | 'debug';
2
- type LogMessage = object | string;
3
- type LogFunction = (message: LogMessage, meta: Record<string, unknown> | undefined, level: LogLevel) => void;
2
+ export type LogMessage = string | typeof Error | unknown | object;
3
+ export type LogMeta = Record<string, unknown> | typeof Error | unknown | object;
4
+ export type LogFunction = (message: LogMessage, meta: LogMeta, level: LogLevel) => void;
4
5
  export interface Transport {
5
6
  level: LogLevel;
6
7
  log: LogFunction;
@@ -11,16 +12,16 @@ export declare class Logger {
11
12
  transports: Transport[];
12
13
  });
13
14
  private _log;
14
- debug(message: LogMessage, meta?: Record<string, unknown>): void;
15
- info(message: LogMessage, meta?: Record<string, unknown>): void;
16
- log(message: LogMessage, meta?: Record<string, unknown>): void;
17
- warn(message: LogMessage, meta?: Record<string, unknown>): void;
18
- error(message: LogMessage, meta?: Record<string, unknown>): void;
15
+ debug(message: LogMessage, meta?: LogMeta): void;
16
+ info(message: LogMessage, meta?: LogMeta): void;
17
+ log(message: LogMessage, meta?: LogMeta): void;
18
+ warn(message: LogMessage, meta?: LogMeta): void;
19
+ error(message: LogMessage, meta?: LogMeta): void;
19
20
  addTransport(transport: Transport): void;
20
21
  clearTransports(): void;
21
22
  }
22
23
  declare const logger: Logger;
23
24
  export default logger;
24
25
  export { ConsoleTransport } from './transports/console.js';
25
- export { DatadogTransport } from './transports/datadog.js';
26
+ export { DatadogTransport, ReactNativeDatadogTransport } from './transports/datadog.js';
26
27
  export { SentryTransport } from './transports/sentry.js';
package/dist/index.js CHANGED
@@ -41,6 +41,6 @@ export class Logger {
41
41
  const logger = new Logger();
42
42
  export default logger;
43
43
  export { ConsoleTransport } from './transports/console.js';
44
- export { DatadogTransport } from './transports/datadog.js';
44
+ export { DatadogTransport, ReactNativeDatadogTransport } from './transports/datadog.js';
45
45
  export { SentryTransport } from './transports/sentry.js';
46
46
  //# 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":"AACA,MAAM,MAAM,GAAG;IACb,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AAUF,MAAM,OAAO,MAAM;IACjB,UAAU,CAAc;IAExB,YAAY,IAAkC;QAC5C,IAAI,CAAC,UAAU,GAAG,IAAI,EAAE,UAAU,IAAI,EAAE,CAAC;IAC3C,CAAC;IAEO,IAAI,CAAC,KAAe,EAAE,OAAmB,EAAE,IAA8B;QAC/E,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU,EAAE;YACvC,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE;gBAC5C,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;aACrC;SACF;IACH,CAAC;IAED,KAAK,CAAC,OAAmB,EAAE,IAA8B;QACvD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,CAAC,OAAmB,EAAE,IAA8B;QACtD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,GAAG,CAAC,OAAmB,EAAE,IAA8B;QACrD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,IAAI,CAAC,OAAmB,EAAE,IAA8B;QACtD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,OAAmB,EAAE,IAA8B;QACvD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,YAAY,CAAC,SAAoB;QAC/B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAED,eAAe;QACb,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;IACvB,CAAC;CACF;AAED,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;AAC5B,eAAe,MAAM,CAAC;AAEtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC","sourcesContent":["export type LogLevel = 'error' | 'warn' | 'info' | 'debug';\nconst levels = {\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n};\n\ntype LogMessage = object | string;\ntype LogFunction = (message: LogMessage, meta: Record<string, unknown> | undefined, level: LogLevel) => void;\n\nexport interface Transport {\n level: LogLevel;\n log: LogFunction;\n}\n\nexport class Logger {\n transports: Transport[];\n\n constructor(opts?: { transports: Transport[] }) {\n this.transports = opts?.transports || [];\n }\n\n private _log(level: LogLevel, message: LogMessage, meta?: Record<string, unknown>) {\n for (const transport of this.transports) {\n if (levels[transport.level] >= levels[level]) {\n transport.log(message, meta, level);\n }\n }\n }\n\n debug(message: LogMessage, meta?: Record<string, unknown>) {\n this._log('debug', message, meta);\n }\n\n info(message: LogMessage, meta?: Record<string, unknown>) {\n this._log('info', message, meta);\n }\n\n log(message: LogMessage, meta?: Record<string, unknown>) {\n this.info(message, meta);\n }\n\n warn(message: LogMessage, meta?: Record<string, unknown>) {\n this._log('warn', message, meta);\n }\n\n error(message: LogMessage, meta?: Record<string, unknown>) {\n this._log('error', message, meta);\n }\n\n addTransport(transport: Transport) {\n this.transports.push(transport);\n }\n\n clearTransports() {\n this.transports = [];\n }\n}\n\nconst logger = new Logger();\nexport default logger;\n\nexport { ConsoleTransport } from './transports/console.js';\nexport { DatadogTransport } from './transports/datadog.js';\nexport { SentryTransport } from './transports/sentry.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,GAAG;IACb,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AAcF,MAAM,OAAO,MAAM;IACjB,UAAU,CAAc;IAExB,YAAY,IAAkC;QAC5C,IAAI,CAAC,UAAU,GAAG,IAAI,EAAE,UAAU,IAAI,EAAE,CAAC;IAC3C,CAAC;IAEO,IAAI,CAAC,KAAe,EAAE,OAAmB,EAAE,IAAc;QAC/D,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU,EAAE;YACvC,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE;gBAC5C,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;aACrC;SACF;IACH,CAAC;IAED,KAAK,CAAC,OAAmB,EAAE,IAAc;QACvC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,CAAC,OAAmB,EAAE,IAAc;QACtC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,GAAG,CAAC,OAAmB,EAAE,IAAc;QACrC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,IAAI,CAAC,OAAmB,EAAE,IAAc;QACtC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,OAAmB,EAAE,IAAc;QACvC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,YAAY,CAAC,SAAoB;QAC/B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAED,eAAe;QACb,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;IACvB,CAAC;CACF;AAED,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;AAC5B,eAAe,MAAM,CAAC;AAEtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACxF,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC","sourcesContent":["export type LogLevel = 'error' | 'warn' | 'info' | 'debug';\nconst levels = {\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n};\n\n// Support `typeof Error | unknown` types to allow passing the error objects directly or unknown\n// objects that may be an error from a catch block for instance.\n// Todo - Generic `object` might be able to be removed?\nexport type LogMessage = string | typeof Error | unknown | object;\nexport type LogMeta = Record<string, unknown> | typeof Error | unknown | object;\nexport type LogFunction = (message: LogMessage, meta: LogMeta, level: LogLevel) => void;\n\nexport interface Transport {\n level: LogLevel;\n log: LogFunction;\n}\n\nexport class Logger {\n transports: Transport[];\n\n constructor(opts?: { transports: Transport[] }) {\n this.transports = opts?.transports || [];\n }\n\n private _log(level: LogLevel, message: LogMessage, meta?: LogMeta) {\n for (const transport of this.transports) {\n if (levels[transport.level] >= levels[level]) {\n transport.log(message, meta, level);\n }\n }\n }\n\n debug(message: LogMessage, meta?: LogMeta) {\n this._log('debug', message, meta);\n }\n\n info(message: LogMessage, meta?: LogMeta) {\n this._log('info', message, meta);\n }\n\n log(message: LogMessage, meta?: LogMeta) {\n this.info(message, meta);\n }\n\n warn(message: LogMessage, meta?: LogMeta) {\n this._log('warn', message, meta);\n }\n\n error(message: LogMessage, meta?: LogMeta) {\n this._log('error', message, meta);\n }\n\n addTransport(transport: Transport) {\n this.transports.push(transport);\n }\n\n clearTransports() {\n this.transports = [];\n }\n}\n\nconst logger = new Logger();\nexport default logger;\n\nexport { ConsoleTransport } from './transports/console.js';\nexport { DatadogTransport, ReactNativeDatadogTransport } from './transports/datadog.js';\nexport { SentryTransport } from './transports/sentry.js';\n"]}
@@ -1,9 +1,9 @@
1
- import { LogLevel, Transport } from '../index.js';
1
+ import { LogLevel, LogMessage, LogMeta, Transport } from '../index.js';
2
2
  export interface ConsoleTransportOptions {
3
3
  level?: LogLevel;
4
4
  }
5
5
  export declare class ConsoleTransport implements Transport {
6
6
  level: LogLevel;
7
- log: (message: string | object, meta: Record<string, unknown> | undefined, level: LogLevel) => void;
7
+ log: (message: LogMessage, meta: LogMeta, level: LogLevel) => void;
8
8
  constructor(opts?: ConsoleTransportOptions);
9
9
  }
@@ -6,16 +6,16 @@ export class ConsoleTransport {
6
6
  this.log = (message, meta, level) => {
7
7
  switch (level) {
8
8
  case 'error':
9
- console.error(message, meta);
9
+ console.error(message, meta ?? '');
10
10
  break;
11
11
  case 'warn':
12
- console.warn(message, meta);
12
+ console.warn(message, meta ?? '');
13
13
  break;
14
14
  case 'debug':
15
- console.debug(message, meta);
15
+ console.debug(message, meta ?? '');
16
16
  break;
17
17
  default:
18
- console.log(message, meta);
18
+ console.log(message, meta ?? '');
19
19
  }
20
20
  };
21
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"console.js","sourceRoot":"","sources":["../../src/transports/console.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,gBAAgB;IAC3B,KAAK,CAAW;IAChB,GAAG,CAAiG;IAEpG,YAAY,IAA8B;QACxC,IAAI,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI,OAAO,CAAC;QACpC,IAAI,CAAC,GAAG,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;YAClC,QAAQ,KAAK,EAAE;gBACb,KAAK,OAAO;oBACV,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;oBAC7B,MAAM;gBACR,KAAK,MAAM;oBACT,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;oBAC5B,MAAM;gBACR,KAAK,OAAO;oBACV,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;oBAC7B,MAAM;gBACR;oBACE,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;aAC9B;QACH,CAAC,CAAC;IACJ,CAAC;CACF","sourcesContent":["import { LogLevel, Transport } from '../index.js';\n\nexport interface ConsoleTransportOptions {\n level?: LogLevel;\n}\n\nexport class ConsoleTransport implements Transport {\n level: LogLevel;\n log: (message: string | object, meta: Record<string, unknown> | undefined, level: LogLevel) => void;\n\n constructor(opts?: ConsoleTransportOptions) {\n this.level = opts?.level || 'debug';\n this.log = (message, meta, level) => {\n switch (level) {\n case 'error':\n console.error(message, meta);\n break;\n case 'warn':\n console.warn(message, meta);\n break;\n case 'debug':\n console.debug(message, meta);\n break;\n default:\n console.log(message, meta);\n }\n };\n }\n}\n"]}
1
+ {"version":3,"file":"console.js","sourceRoot":"","sources":["../../src/transports/console.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,gBAAgB;IAC3B,KAAK,CAAW;IAChB,GAAG,CAAgE;IAEnE,YAAY,IAA8B;QACxC,IAAI,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI,OAAO,CAAC;QACpC,IAAI,CAAC,GAAG,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;YAClC,QAAQ,KAAK,EAAE;gBACb,KAAK,OAAO;oBACV,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;oBACnC,MAAM;gBACR,KAAK,MAAM;oBACT,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;oBAClC,MAAM;gBACR,KAAK,OAAO;oBACV,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;oBACnC,MAAM;gBACR;oBACE,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;aACpC;QACH,CAAC,CAAC;IACJ,CAAC;CACF","sourcesContent":["import { LogLevel, LogMessage, LogMeta, Transport } from '../index.js';\n\nexport interface ConsoleTransportOptions {\n level?: LogLevel;\n}\n\nexport class ConsoleTransport implements Transport {\n level: LogLevel;\n log: (message: LogMessage, meta: LogMeta, level: LogLevel) => void;\n\n constructor(opts?: ConsoleTransportOptions) {\n this.level = opts?.level || 'debug';\n this.log = (message, meta, level) => {\n switch (level) {\n case 'error':\n console.error(message, meta ?? '');\n break;\n case 'warn':\n console.warn(message, meta ?? '');\n break;\n case 'debug':\n console.debug(message, meta ?? '');\n break;\n default:\n console.log(message, meta ?? '');\n }\n };\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,50 @@
1
+ import logger, { ConsoleTransport } from '../index.js';
2
+ describe('console transport', () => {
3
+ beforeAll(() => {
4
+ logger.addTransport(new ConsoleTransport());
5
+ });
6
+ afterAll(() => {
7
+ logger.clearTransports();
8
+ });
9
+ it('should log error to console', () => {
10
+ const customErr = 'custom error';
11
+ const errorSpy = vi.spyOn(console, 'error');
12
+ logger.error(customErr);
13
+ expect(errorSpy).toHaveBeenCalledWith(customErr, '');
14
+ errorSpy.mockClear();
15
+ const customMeta = { meta: { extra: 'something' } };
16
+ logger.error(customErr, customMeta);
17
+ expect(errorSpy).toHaveBeenCalledWith(customErr, customMeta);
18
+ });
19
+ it('should log debug to console', () => {
20
+ const customMsg = 'custom debug';
21
+ const debugSpy = vi.spyOn(console, 'debug');
22
+ logger['debug'](customMsg);
23
+ expect(debugSpy).toHaveBeenCalledWith(customMsg, '');
24
+ debugSpy.mockClear();
25
+ const customMeta = { meta: { extra: 'something' } };
26
+ logger['debug'](customMsg, customMeta);
27
+ expect(debugSpy).toHaveBeenCalledWith(customMsg, customMeta);
28
+ });
29
+ it('should log warn to console', () => {
30
+ const customWarn = 'custom warn';
31
+ const debugSpy = vi.spyOn(console, 'warn');
32
+ logger.warn(customWarn);
33
+ expect(debugSpy).toHaveBeenCalledWith(customWarn, '');
34
+ debugSpy.mockClear();
35
+ const customMeta = { meta: { extra: 'something' } };
36
+ logger.warn(customWarn, customMeta);
37
+ expect(debugSpy).toHaveBeenCalledWith(customWarn, customMeta);
38
+ });
39
+ it('should log to console', () => {
40
+ const customMsg = 'custom log';
41
+ const logSpy = vi.spyOn(console, 'log');
42
+ logger.log(customMsg);
43
+ expect(logSpy).toHaveBeenCalledWith(customMsg, '');
44
+ logSpy.mockClear();
45
+ const customMeta = { meta: { extra: 'something' } };
46
+ logger.log(customMsg, customMeta);
47
+ expect(logSpy).toHaveBeenCalledWith(customMsg, customMeta);
48
+ });
49
+ });
50
+ //# sourceMappingURL=console.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.spec.js","sourceRoot":"","sources":["../../src/transports/console.spec.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEvD,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,YAAY,CAAC,IAAI,gBAAgB,EAAE,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,GAAG,EAAE;QACZ,MAAM,CAAC,eAAe,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,SAAS,GAAG,cAAc,CAAC;QAEjC,MAAM,QAAQ,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAE5C,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAExB,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAErD,QAAQ,CAAC,SAAS,EAAE,CAAC;QAErB,MAAM,UAAU,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC;QAEpD,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAEpC,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,SAAS,GAAG,cAAc,CAAC;QAEjC,MAAM,QAAQ,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAE5C,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC;QAE3B,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAErD,QAAQ,CAAC,SAAS,EAAE,CAAC;QAErB,MAAM,UAAU,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC;QAEpD,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAEvC,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,UAAU,GAAG,aAAa,CAAC;QAEjC,MAAM,QAAQ,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAE3C,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAExB,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAEtD,QAAQ,CAAC,SAAS,EAAE,CAAC;QAErB,MAAM,UAAU,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC;QAEpD,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAEpC,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,SAAS,GAAG,YAAY,CAAC;QAE/B,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAEtB,MAAM,CAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAEnD,MAAM,CAAC,SAAS,EAAE,CAAC;QAEnB,MAAM,UAAU,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC;QAEpD,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAElC,MAAM,CAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import logger, { ConsoleTransport } from '../index.js';\n\ndescribe('console transport', () => {\n beforeAll(() => {\n logger.addTransport(new ConsoleTransport());\n });\n\n afterAll(() => {\n logger.clearTransports();\n });\n\n it('should log error to console', () => {\n const customErr = 'custom error';\n\n const errorSpy = vi.spyOn(console, 'error');\n\n logger.error(customErr);\n\n expect(errorSpy).toHaveBeenCalledWith(customErr, '');\n\n errorSpy.mockClear();\n\n const customMeta = { meta: { extra: 'something' } };\n\n logger.error(customErr, customMeta);\n\n expect(errorSpy).toHaveBeenCalledWith(customErr, customMeta);\n });\n\n it('should log debug to console', () => {\n const customMsg = 'custom debug';\n\n const debugSpy = vi.spyOn(console, 'debug');\n\n logger['debug'](customMsg);\n\n expect(debugSpy).toHaveBeenCalledWith(customMsg, '');\n\n debugSpy.mockClear();\n\n const customMeta = { meta: { extra: 'something' } };\n\n logger['debug'](customMsg, customMeta);\n\n expect(debugSpy).toHaveBeenCalledWith(customMsg, customMeta);\n });\n\n it('should log warn to console', () => {\n const customWarn = 'custom warn';\n\n const debugSpy = vi.spyOn(console, 'warn');\n\n logger.warn(customWarn);\n\n expect(debugSpy).toHaveBeenCalledWith(customWarn, '');\n\n debugSpy.mockClear();\n\n const customMeta = { meta: { extra: 'something' } };\n\n logger.warn(customWarn, customMeta);\n\n expect(debugSpy).toHaveBeenCalledWith(customWarn, customMeta);\n });\n\n it('should log to console', () => {\n const customMsg = 'custom log';\n\n const logSpy = vi.spyOn(console, 'log');\n\n logger.log(customMsg);\n\n expect(logSpy).toHaveBeenCalledWith(customMsg, '');\n\n logSpy.mockClear();\n\n const customMeta = { meta: { extra: 'something' } };\n\n logger.log(customMsg, customMeta);\n\n expect(logSpy).toHaveBeenCalledWith(customMsg, customMeta);\n });\n});\n"]}
@@ -1,11 +1,20 @@
1
1
  import type { datadogLogs } from '@datadog/browser-logs';
2
- import { LogLevel, Transport } from '../index.js';
2
+ import type { DdLogs } from '@datadog/mobile-react-native';
3
+ import { LogFunction, LogLevel, Transport } from '../index.js';
3
4
  export interface DatadogTransportOptions {
4
5
  level?: LogLevel;
5
6
  datadogLogsInstance: typeof datadogLogs;
6
7
  }
8
+ export interface ReactNativeDatadogTransportOptions extends Omit<DatadogTransportOptions, 'datadogLogsInstance'> {
9
+ datadogLogsInstance: typeof DdLogs;
10
+ }
7
11
  export declare class DatadogTransport implements Transport {
8
12
  level: LogLevel;
9
- log: (message: string | object, meta: Record<string, unknown> | undefined, level: LogLevel) => void;
13
+ log: LogFunction;
10
14
  constructor(opts: DatadogTransportOptions);
11
15
  }
16
+ export declare class ReactNativeDatadogTransport implements Transport {
17
+ level: LogLevel;
18
+ log: LogFunction;
19
+ constructor(opts: ReactNativeDatadogTransportOptions);
20
+ }
@@ -1,3 +1,24 @@
1
+ function isEmpty(obj) {
2
+ if (obj === null || obj === undefined || !obj) {
3
+ return true;
4
+ }
5
+ for (const prop in obj) {
6
+ if (Object.hasOwn(obj, prop)) {
7
+ return false;
8
+ }
9
+ }
10
+ return true;
11
+ }
12
+ /**
13
+ * Wraps the meta object to avoid our custom meta properties mixing with datadog's built-in properties.
14
+ *
15
+ * @param meta - The meta object to wrap.
16
+ * @returns The wrapped meta object.
17
+ */
18
+ function wrapMeta(meta) {
19
+ // using `extra` just to match what Sentry does
20
+ return isEmpty(meta) ? {} : { extra: meta };
21
+ }
1
22
  export class DatadogTransport {
2
23
  level;
3
24
  log;
@@ -7,15 +28,72 @@ export class DatadogTransport {
7
28
  // LogLevel maps directly to datadogLogsInstance logger methods
8
29
  if (message instanceof Error) {
9
30
  // Datadog logger has an optional third error param for exception handling
10
- opts.datadogLogsInstance.logger[level](message.message, meta, message);
31
+ opts.datadogLogsInstance.logger[level](message.message, wrapMeta(meta), message);
32
+ }
33
+ else if (typeof message === 'string' && meta instanceof Error) {
34
+ opts.datadogLogsInstance.logger[level](message, {}, meta);
11
35
  }
12
36
  else if (typeof message === 'string') {
13
- opts.datadogLogsInstance.logger[level](message, meta);
37
+ opts.datadogLogsInstance.logger[level](message, wrapMeta(meta));
14
38
  }
15
39
  else {
16
40
  // Datadog requires a string for the message param. Our downstream usage currently provides
17
41
  // either a string or an Error, but for future-proofing this just stringify the unknown object as message.
18
- opts.datadogLogsInstance.logger[level](JSON.stringify(message), meta);
42
+ opts.datadogLogsInstance.logger[level](JSON.stringify(message), wrapMeta(meta));
43
+ }
44
+ };
45
+ }
46
+ }
47
+ /**
48
+ * Helper function to help call the datadog logger methods directly instead of dynamically calling
49
+ * them with the `level` param. This is a workaround to avoid the following error:
50
+ *
51
+ * ```
52
+ * Error: JS Functions are not convertible to dynamic
53
+ * ```
54
+ *
55
+ * @param level - The log level to call the datadog logger method with.
56
+ * @param args - The arguments to pass to the datadog logger method.
57
+ */
58
+ function callRnDatadogLogger(instance, level, args) {
59
+ if (level === 'debug') {
60
+ instance.debug(...args);
61
+ }
62
+ else if (level === 'info') {
63
+ instance.info(...args);
64
+ }
65
+ else if (level === 'warn') {
66
+ instance.warn(...args);
67
+ }
68
+ else if (level === 'error') {
69
+ instance.error(...args);
70
+ }
71
+ }
72
+ export class ReactNativeDatadogTransport {
73
+ level;
74
+ log;
75
+ constructor(opts) {
76
+ this.level = opts.level || 'info';
77
+ this.log = (message, meta = {}, level) => {
78
+ if (message instanceof Error) {
79
+ callRnDatadogLogger(opts.datadogLogsInstance, level, [
80
+ message.message,
81
+ message.name,
82
+ message.message,
83
+ message.stack,
84
+ wrapMeta(meta),
85
+ ]);
86
+ }
87
+ else if (typeof message === 'string') {
88
+ if (meta instanceof Error) {
89
+ callRnDatadogLogger(opts.datadogLogsInstance, level, [message, meta.name, meta.message, meta.stack]);
90
+ }
91
+ else {
92
+ callRnDatadogLogger(opts.datadogLogsInstance, level, [message, wrapMeta(meta)]);
93
+ }
94
+ }
95
+ else {
96
+ callRnDatadogLogger(opts.datadogLogsInstance, level, [JSON.stringify(message), wrapMeta(meta)]);
19
97
  }
20
98
  };
21
99
  }
@@ -1 +1 @@
1
- {"version":3,"file":"datadog.js","sourceRoot":"","sources":["../../src/transports/datadog.ts"],"names":[],"mappings":"AASA,MAAM,OAAO,gBAAgB;IAC3B,KAAK,CAAW;IAChB,GAAG,CAAiG;IAEpG,YAAY,IAA6B;QACvC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC;QAClC,IAAI,CAAC,GAAG,GAAG,CAAC,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE;YACvC,+DAA+D;YAC/D,IAAI,OAAO,YAAY,KAAK,EAAE;gBAC5B,0EAA0E;gBAC1E,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;aACxE;iBAAM,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE;gBACtC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;aACvD;iBAAM;gBACL,2FAA2F;gBAC3F,0GAA0G;gBAC1G,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;aACvE;QACH,CAAC,CAAC;IACJ,CAAC;CACF","sourcesContent":["import type { datadogLogs } from '@datadog/browser-logs';\n\nimport { LogLevel, Transport } from '../index.js';\n\nexport interface DatadogTransportOptions {\n level?: LogLevel;\n datadogLogsInstance: typeof datadogLogs;\n}\n\nexport class DatadogTransport implements Transport {\n level: LogLevel;\n log: (message: string | object, meta: Record<string, unknown> | undefined, level: LogLevel) => void;\n\n constructor(opts: DatadogTransportOptions) {\n this.level = opts.level || 'info';\n this.log = (message, meta = {}, level) => {\n // LogLevel maps directly to datadogLogsInstance logger methods\n if (message instanceof Error) {\n // Datadog logger has an optional third error param for exception handling\n opts.datadogLogsInstance.logger[level](message.message, meta, message);\n } else if (typeof message === 'string') {\n opts.datadogLogsInstance.logger[level](message, meta);\n } else {\n // Datadog requires a string for the message param. Our downstream usage currently provides\n // either a string or an Error, but for future-proofing this just stringify the unknown object as message.\n opts.datadogLogsInstance.logger[level](JSON.stringify(message), meta);\n }\n };\n }\n}\n"]}
1
+ {"version":3,"file":"datadog.js","sourceRoot":"","sources":["../../src/transports/datadog.ts"],"names":[],"mappings":"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","sourcesContent":["import type { datadogLogs } from '@datadog/browser-logs';\nimport type { DdLogs } from '@datadog/mobile-react-native';\n\nimport { LogFunction, LogLevel, Transport } from '../index.js';\n\nfunction isEmpty(obj: object) {\n if (obj === null || obj === undefined || !obj) {\n return true;\n }\n\n for (const prop in obj) {\n if (Object.hasOwn(obj, prop)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Wraps the meta object to avoid our custom meta properties mixing with datadog's built-in properties.\n *\n * @param meta - The meta object to wrap.\n * @returns The wrapped meta object.\n */\nfunction wrapMeta(meta: unknown) {\n // using `extra` just to match what Sentry does\n return isEmpty(meta as object) ? {} : { extra: meta };\n}\n\nexport interface DatadogTransportOptions {\n level?: LogLevel;\n datadogLogsInstance: typeof datadogLogs;\n}\n\nexport interface ReactNativeDatadogTransportOptions extends Omit<DatadogTransportOptions, 'datadogLogsInstance'> {\n datadogLogsInstance: typeof DdLogs;\n}\n\nexport class DatadogTransport implements Transport {\n level: LogLevel;\n log: LogFunction;\n\n constructor(opts: DatadogTransportOptions) {\n this.level = opts.level || 'info';\n this.log = (message, meta = {}, level) => {\n // LogLevel maps directly to datadogLogsInstance logger methods\n if (message instanceof Error) {\n // Datadog logger has an optional third error param for exception handling\n opts.datadogLogsInstance.logger[level](message.message, wrapMeta(meta), message);\n } else if (typeof message === 'string' && meta instanceof Error) {\n opts.datadogLogsInstance.logger[level](message, {}, meta);\n } else if (typeof message === 'string') {\n opts.datadogLogsInstance.logger[level](message, wrapMeta(meta));\n } else {\n // Datadog requires a string for the message param. Our downstream usage currently provides\n // either a string or an Error, but for future-proofing this just stringify the unknown object as message.\n opts.datadogLogsInstance.logger[level](JSON.stringify(message), wrapMeta(meta));\n }\n };\n }\n}\n\n/**\n * Helper function to help call the datadog logger methods directly instead of dynamically calling\n * them with the `level` param. This is a workaround to avoid the following error:\n *\n * ```\n * Error: JS Functions are not convertible to dynamic\n * ```\n *\n * @param level - The log level to call the datadog logger method with.\n * @param args - The arguments to pass to the datadog logger method.\n */\nfunction callRnDatadogLogger(\n instance: typeof DdLogs,\n level: LogLevel,\n args: [string, string?, string?, string?, object?] | [string, object?],\n) {\n if (level === 'debug') {\n instance.debug(...args);\n } else if (level === 'info') {\n instance.info(...args);\n } else if (level === 'warn') {\n instance.warn(...args);\n } else if (level === 'error') {\n instance.error(...args);\n }\n}\n\nexport class ReactNativeDatadogTransport implements Transport {\n level: LogLevel;\n log: LogFunction;\n\n constructor(opts: ReactNativeDatadogTransportOptions) {\n this.level = opts.level || 'info';\n this.log = (message, meta = {}, level) => {\n if (message instanceof Error) {\n callRnDatadogLogger(opts.datadogLogsInstance, level, [\n message.message,\n message.name,\n message.message,\n message.stack,\n wrapMeta(meta),\n ]);\n } else if (typeof message === 'string') {\n if (meta instanceof Error) {\n callRnDatadogLogger(opts.datadogLogsInstance, level, [message, meta.name, meta.message, meta.stack]);\n } else {\n callRnDatadogLogger(opts.datadogLogsInstance, level, [message, wrapMeta(meta)]);\n }\n } else {\n callRnDatadogLogger(opts.datadogLogsInstance, level, [JSON.stringify(message), wrapMeta(meta)]);\n }\n };\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,103 @@
1
+ import logger, { DatadogTransport, ReactNativeDatadogTransport } from '../index.js';
2
+ const DdInstance = {
3
+ logger: {
4
+ error: vi.fn(),
5
+ warn: vi.fn(),
6
+ info: vi.fn(),
7
+ debug: vi.fn(),
8
+ },
9
+ };
10
+ describe('datadog web transport', () => {
11
+ beforeAll(() => {
12
+ logger.addTransport(new DatadogTransport({ datadogLogsInstance: DdInstance, level: 'debug' }));
13
+ });
14
+ afterEach(() => {
15
+ Object.values(DdInstance.logger).forEach((level) => level.mockClear());
16
+ });
17
+ afterAll(() => {
18
+ logger.clearTransports();
19
+ });
20
+ it.each(['info', 'warn', 'debug', 'error'])('should log %s to dd', (level) => {
21
+ const customErr = 'custom error';
22
+ logger[level](customErr);
23
+ expect(DdInstance.logger[level]).toHaveBeenCalledWith(customErr, {});
24
+ });
25
+ it('should log error instance correctly as first arg', () => {
26
+ const customErr = Error('ERR');
27
+ logger.error(customErr);
28
+ expect(DdInstance.logger.error).toHaveBeenCalledWith(customErr.message, {}, customErr);
29
+ });
30
+ it('should log error instance correctly as first arg with meta', () => {
31
+ const customErr = Error('ERR');
32
+ const customMeta = { foo: 'bar' };
33
+ logger.error(customErr, customMeta);
34
+ expect(DdInstance.logger.error).toHaveBeenCalledWith(customErr.message, { extra: customMeta }, customErr);
35
+ });
36
+ it('should log correctly with string as first arg and error isntance as second', () => {
37
+ const customMsg = 'message';
38
+ const customErr = Error('ERR');
39
+ logger.error(customMsg, customErr);
40
+ expect(DdInstance.logger.error).toHaveBeenCalledWith(customMsg, {}, customErr);
41
+ });
42
+ it('should log correctly when first arg is a string and no second arg is passed', () => {
43
+ const customMsg = 'message';
44
+ logger.error(customMsg);
45
+ expect(DdInstance.logger.error).toHaveBeenCalledWith(customMsg, {});
46
+ });
47
+ it('should log correctly when first arg is not a string', () => {
48
+ const customArg = [1, 2, 3];
49
+ logger.error(customArg);
50
+ expect(DdInstance.logger.error).toHaveBeenCalledWith('[' + customArg.join(',') + ']', {});
51
+ });
52
+ });
53
+ const rnDdInstance = {
54
+ error: vi.fn(),
55
+ info: vi.fn(),
56
+ debug: vi.fn(),
57
+ warn: vi.fn(),
58
+ };
59
+ describe('datadog react native transport', () => {
60
+ beforeAll(() => {
61
+ logger.addTransport(new ReactNativeDatadogTransport({ datadogLogsInstance: rnDdInstance, level: 'debug' }));
62
+ });
63
+ afterEach(() => {
64
+ Object.values(rnDdInstance).forEach((level) => level.mockClear());
65
+ });
66
+ afterAll(() => {
67
+ logger.clearTransports();
68
+ });
69
+ it.each `
70
+ meta | expected
71
+ ${undefined} | ${{}}
72
+ `('should log error correctly if first argument is an Error', ({ meta, expected }) => {
73
+ const customError = Error('custom err');
74
+ logger.error(customError, meta);
75
+ expect(rnDdInstance.error).toHaveBeenCalledWith(customError.message, customError.name, customError.message, customError.stack, expected);
76
+ });
77
+ it('should log error correctly if first argument is not an Error nor string', () => {
78
+ const customMeta = 'string meta';
79
+ logger.error([1, 2], 'string meta');
80
+ expect(rnDdInstance.error).toHaveBeenCalledWith('[1,2]', { extra: customMeta });
81
+ });
82
+ describe('when message is a string', () => {
83
+ it('should log an error correctly when meta is an Error', () => {
84
+ const customMessage = 'my message';
85
+ const customError = Error('custom error');
86
+ logger.error(customMessage, customError);
87
+ expect(rnDdInstance.error).toHaveBeenCalledWith(customMessage, customError.name, customError.message, customError.stack);
88
+ });
89
+ it('should log an error message correctly when meta is an object', () => {
90
+ const customMessage = 'custom msg';
91
+ const customMeta = { foo: 'bar' };
92
+ logger.error(customMessage, customMeta);
93
+ expect(rnDdInstance.error).toHaveBeenCalledWith(customMessage, { extra: customMeta });
94
+ });
95
+ it('should log a message correctly when meta is not an object', () => {
96
+ const customMessage = 'custom msg';
97
+ const customMeta = 'string';
98
+ logger.error(customMessage, customMeta);
99
+ expect(rnDdInstance.error).toHaveBeenCalledWith(customMessage, { extra: customMeta });
100
+ });
101
+ });
102
+ });
103
+ //# sourceMappingURL=datadog.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"datadog.spec.js","sourceRoot":"","sources":["../../src/transports/datadog.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import type { datadogLogs } from '@datadog/browser-logs';\nimport type { DdLogs } from '@datadog/mobile-react-native';\n\nimport logger, { DatadogTransport, LogLevel, ReactNativeDatadogTransport } from '../index.js';\n\nconst DdInstance = {\n logger: {\n error: vi.fn(),\n warn: vi.fn(),\n info: vi.fn(),\n debug: vi.fn(),\n },\n} as unknown as typeof datadogLogs;\n\ndescribe('datadog web transport', () => {\n beforeAll(() => {\n logger.addTransport(new DatadogTransport({ datadogLogsInstance: DdInstance, level: 'debug' }));\n });\n\n afterEach(() => {\n Object.values(DdInstance.logger).forEach((level) => level.mockClear());\n });\n\n afterAll(() => {\n logger.clearTransports();\n });\n\n it.each(['info', 'warn', 'debug', 'error'])('should log %s to dd', (level: unknown) => {\n const customErr = 'custom error';\n\n logger[level as LogLevel](customErr);\n\n expect(DdInstance.logger[level as LogLevel]).toHaveBeenCalledWith(customErr, {});\n });\n\n it('should log error instance correctly as first arg', () => {\n const customErr = Error('ERR');\n\n logger.error(customErr);\n\n expect(DdInstance.logger.error).toHaveBeenCalledWith(customErr.message, {}, customErr);\n });\n\n it('should log error instance correctly as first arg with meta', () => {\n const customErr = Error('ERR');\n const customMeta = { foo: 'bar' };\n\n logger.error(customErr, customMeta);\n\n expect(DdInstance.logger.error).toHaveBeenCalledWith(customErr.message, { extra: customMeta }, customErr);\n });\n\n it('should log correctly with string as first arg and error isntance as second', () => {\n const customMsg = 'message';\n const customErr = Error('ERR');\n\n logger.error(customMsg, customErr);\n\n expect(DdInstance.logger.error).toHaveBeenCalledWith(customMsg, {}, customErr);\n });\n\n it('should log correctly when first arg is a string and no second arg is passed', () => {\n const customMsg = 'message';\n\n logger.error(customMsg);\n\n expect(DdInstance.logger.error).toHaveBeenCalledWith(customMsg, {});\n });\n\n it('should log correctly when first arg is not a string', () => {\n const customArg = [1, 2, 3];\n\n logger.error(customArg);\n\n expect(DdInstance.logger.error).toHaveBeenCalledWith('[' + customArg.join(',') + ']', {});\n });\n});\n\nconst rnDdInstance = {\n error: vi.fn(),\n info: vi.fn(),\n debug: vi.fn(),\n warn: vi.fn(),\n} as unknown as typeof DdLogs;\n\ndescribe('datadog react native transport', () => {\n beforeAll(() => {\n logger.addTransport(new ReactNativeDatadogTransport({ datadogLogsInstance: rnDdInstance, level: 'debug' }));\n });\n\n afterEach(() => {\n Object.values(rnDdInstance).forEach((level) => level.mockClear());\n });\n\n afterAll(() => {\n logger.clearTransports();\n });\n\n it.each`\n meta | expected\n ${undefined} | ${{}}\n `('should log error correctly if first argument is an Error', ({ meta, expected }) => {\n const customError = Error('custom err');\n\n logger.error(customError, meta);\n\n expect(rnDdInstance.error).toHaveBeenCalledWith(\n customError.message,\n customError.name,\n customError.message,\n customError.stack,\n expected,\n );\n });\n\n it('should log error correctly if first argument is not an Error nor string', () => {\n const customMeta = 'string meta';\n\n logger.error([1, 2], 'string meta');\n\n expect(rnDdInstance.error).toHaveBeenCalledWith('[1,2]', { extra: customMeta });\n });\n\n describe('when message is a string', () => {\n it('should log an error correctly when meta is an Error', () => {\n const customMessage = 'my message';\n const customError = Error('custom error');\n\n logger.error(customMessage, customError);\n\n expect(rnDdInstance.error).toHaveBeenCalledWith(\n customMessage,\n customError.name,\n customError.message,\n customError.stack,\n );\n });\n\n it('should log an error message correctly when meta is an object', () => {\n const customMessage = 'custom msg';\n const customMeta = { foo: 'bar' };\n\n logger.error(customMessage, customMeta);\n\n expect(rnDdInstance.error).toHaveBeenCalledWith(customMessage, { extra: customMeta });\n });\n\n it('should log a message correctly when meta is not an object', () => {\n const customMessage = 'custom msg';\n const customMeta = 'string';\n\n logger.error(customMessage, customMeta);\n\n expect(rnDdInstance.error).toHaveBeenCalledWith(customMessage, { extra: customMeta });\n });\n });\n});\n"]}
@@ -1,13 +1,21 @@
1
- import { LogLevel, Transport } from '../index.js';
1
+ import { LogFunction, LogLevel, LogMeta, Transport } from '../index.js';
2
2
  export interface SentryTransportOptions {
3
3
  level?: LogLevel;
4
4
  sentryInstance: {
5
- captureMessage: (message: string, sentryMeta: Record<string, unknown> | undefined) => void;
6
- captureException: (message: object, sentryMeta: Record<string, unknown> | undefined) => void;
5
+ captureMessage: (message: string, sentryMeta: Record<string, unknown> | unknown | undefined) => void;
6
+ captureException: (message: object, sentryMeta: Record<string, unknown> | unknown | undefined) => void;
7
7
  };
8
8
  }
9
+ export type SentryMeta = LogMeta & {
10
+ level?: string;
11
+ extra?: Record<string, unknown>;
12
+ tags?: Record<string, unknown>;
13
+ contexts?: Record<string, unknown>;
14
+ user?: Record<string, unknown>;
15
+ fingerprint?: string[];
16
+ };
9
17
  export declare class SentryTransport implements Transport {
10
18
  level: LogLevel;
11
- log: (message: string | object, meta: Record<string, unknown> | undefined, level: LogLevel) => void;
19
+ log: LogFunction;
12
20
  constructor(opts: SentryTransportOptions);
13
21
  }
@@ -9,16 +9,55 @@ export class SentryTransport {
9
9
  log;
10
10
  constructor(opts) {
11
11
  this.level = opts.level || 'error';
12
- this.log = (message, meta, level) => {
13
- const sentryMeta = {
14
- level: SentryErrorLevelMap[level],
15
- ...meta,
16
- };
12
+ this.log = (message, meta, logLevel) => {
13
+ // There's a handful of well defined context properties in sentry:
14
+ // https://docs.sentry.io/platforms/javascript/enriching-events/context/#passing-context-directly
15
+ // If the metadata you pass is not one of these, we will automatically put it into extra for
16
+ // you, which is just a dumping ground for contextual metadata that sentry normalizes for you.
17
+ if (meta &&
18
+ typeof meta === 'object' &&
19
+ !Array.isArray(meta) &&
20
+ Object.getPrototypeOf(meta) === Object.prototype) {
21
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
22
+ const { tags, extra, contexts, user, level, fingerprint, ...customMeta } = meta || {};
23
+ const extraSentry = {
24
+ ...(extra && typeof extra === 'object' ? extra : {}),
25
+ ...(customMeta && typeof customMeta === 'object' ? customMeta : {}),
26
+ };
27
+ const sentryMeta = {
28
+ level: SentryErrorLevelMap[logLevel],
29
+ extra: extraSentry,
30
+ tags,
31
+ contexts,
32
+ user,
33
+ fingerprint,
34
+ };
35
+ if (typeof message === 'string') {
36
+ opts.sentryInstance.captureMessage(message, sentryMeta);
37
+ }
38
+ else if (message instanceof Error) {
39
+ opts.sentryInstance.captureException(message, sentryMeta);
40
+ }
41
+ return;
42
+ }
17
43
  if (typeof message === 'string') {
18
- opts.sentryInstance.captureMessage(message, sentryMeta);
44
+ if (meta instanceof Error) {
45
+ const originalMessage = meta.message;
46
+ meta.message = message;
47
+ opts.sentryInstance.captureException(meta, { extra: { originalMessage } });
48
+ }
49
+ else {
50
+ opts.sentryInstance.captureMessage(message, meta);
51
+ }
52
+ }
53
+ else if (message instanceof Error) {
54
+ opts.sentryInstance.captureException(message, meta);
55
+ }
56
+ else if (meta instanceof Error) {
57
+ opts.sentryInstance.captureException(meta, meta);
19
58
  }
20
59
  else {
21
- opts.sentryInstance.captureException(message, sentryMeta);
60
+ opts.sentryInstance.captureMessage(JSON.stringify(message), meta);
22
61
  }
23
62
  };
24
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sentry.js","sourceRoot":"","sources":["../../src/transports/sentry.ts"],"names":[],"mappings":"AAEA,MAAM,mBAAmB,GAAG;IAC1B,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,OAAO;CACf,CAAC;AAUF,MAAM,OAAO,eAAe;IAC1B,KAAK,CAAW;IAChB,GAAG,CAAiG;IAEpG,YAAY,IAA4B;QACtC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC;QACnC,IAAI,CAAC,GAAG,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;YAClC,MAAM,UAAU,GAAG;gBACjB,KAAK,EAAE,mBAAmB,CAAC,KAAK,CAAC;gBACjC,GAAG,IAAI;aACR,CAAC;YACF,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE;gBAC/B,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;aACzD;iBAAM;gBACL,IAAI,CAAC,cAAc,CAAC,gBAAgB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;aAC3D;QACH,CAAC,CAAC;IACJ,CAAC;CACF","sourcesContent":["import { LogLevel, Transport } from '../index.js';\n\nconst SentryErrorLevelMap = {\n debug: 'debug',\n info: 'info',\n warn: 'warning',\n error: 'error',\n};\n\nexport interface SentryTransportOptions {\n level?: LogLevel;\n sentryInstance: {\n captureMessage: (message: string, sentryMeta: Record<string, unknown> | undefined) => void;\n captureException: (message: object, sentryMeta: Record<string, unknown> | undefined) => void;\n };\n}\n\nexport class SentryTransport implements Transport {\n level: LogLevel;\n log: (message: string | object, meta: Record<string, unknown> | undefined, level: LogLevel) => void;\n\n constructor(opts: SentryTransportOptions) {\n this.level = opts.level || 'error';\n this.log = (message, meta, level) => {\n const sentryMeta = {\n level: SentryErrorLevelMap[level],\n ...meta,\n };\n if (typeof message === 'string') {\n opts.sentryInstance.captureMessage(message, sentryMeta);\n } else {\n opts.sentryInstance.captureException(message, sentryMeta);\n }\n };\n }\n}\n"]}
1
+ {"version":3,"file":"sentry.js","sourceRoot":"","sources":["../../src/transports/sentry.ts"],"names":[],"mappings":"AAEA,MAAM,mBAAmB,GAAG;IAC1B,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,OAAO;CACf,CAAC;AAmBF,MAAM,OAAO,eAAe;IAC1B,KAAK,CAAW;IAChB,GAAG,CAAc;IAEjB,YAAY,IAA4B;QACtC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC;QACnC,IAAI,CAAC,GAAG,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;YACrC,kEAAkE;YAClE,iGAAiG;YACjG,4FAA4F;YAC5F,8FAA8F;YAC9F,IACE,IAAI;gBACJ,OAAO,IAAI,KAAK,QAAQ;gBACxB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;gBACpB,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,SAAS,EAChD;gBACA,6DAA6D;gBAC7D,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,UAAU,EAAE,GAAI,IAAmB,IAAI,EAAE,CAAC;gBACtG,MAAM,WAAW,GAAG;oBAClB,GAAG,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;oBACpD,GAAG,CAAC,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;iBACpE,CAAC;gBACF,MAAM,UAAU,GAAG;oBACjB,KAAK,EAAE,mBAAmB,CAAC,QAAQ,CAAC;oBACpC,KAAK,EAAE,WAAW;oBAClB,IAAI;oBACJ,QAAQ;oBACR,IAAI;oBACJ,WAAW;iBACZ,CAAC;gBAEF,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE;oBAC/B,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;iBACzD;qBAAM,IAAI,OAAO,YAAY,KAAK,EAAE;oBACnC,IAAI,CAAC,cAAc,CAAC,gBAAgB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;iBAC3D;gBACD,OAAO;aACR;YAED,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE;gBAC/B,IAAI,IAAI,YAAY,KAAK,EAAE;oBACzB,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC;oBACrC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;oBACvB,IAAI,CAAC,cAAc,CAAC,gBAAgB,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,eAAe,EAAE,EAAE,CAAC,CAAC;iBAC5E;qBAAM;oBACL,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;iBACnD;aACF;iBAAM,IAAI,OAAO,YAAY,KAAK,EAAE;gBACnC,IAAI,CAAC,cAAc,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;aACrD;iBAAM,IAAI,IAAI,YAAY,KAAK,EAAE;gBAChC,IAAI,CAAC,cAAc,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;aAClD;iBAAM;gBACL,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;aACnE;QACH,CAAC,CAAC;IACJ,CAAC;CACF","sourcesContent":["import { LogFunction, LogLevel, LogMeta, Transport } from '../index.js';\n\nconst SentryErrorLevelMap = {\n debug: 'debug',\n info: 'info',\n warn: 'warning',\n error: 'error',\n};\n\nexport interface SentryTransportOptions {\n level?: LogLevel;\n sentryInstance: {\n captureMessage: (message: string, sentryMeta: Record<string, unknown> | unknown | undefined) => void;\n captureException: (message: object, sentryMeta: Record<string, unknown> | unknown | undefined) => void;\n };\n}\n\nexport type SentryMeta = LogMeta & {\n level?: string;\n extra?: Record<string, unknown>;\n tags?: Record<string, unknown>;\n contexts?: Record<string, unknown>;\n user?: Record<string, unknown>;\n fingerprint?: string[];\n};\n\nexport class SentryTransport implements Transport {\n level: LogLevel;\n log: LogFunction;\n\n constructor(opts: SentryTransportOptions) {\n this.level = opts.level || 'error';\n this.log = (message, meta, logLevel) => {\n // There's a handful of well defined context properties in sentry:\n // https://docs.sentry.io/platforms/javascript/enriching-events/context/#passing-context-directly\n // If the metadata you pass is not one of these, we will automatically put it into extra for\n // you, which is just a dumping ground for contextual metadata that sentry normalizes for you.\n if (\n meta &&\n typeof meta === 'object' &&\n !Array.isArray(meta) &&\n Object.getPrototypeOf(meta) === Object.prototype\n ) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { tags, extra, contexts, user, level, fingerprint, ...customMeta } = (meta as SentryMeta) || {};\n const extraSentry = {\n ...(extra && typeof extra === 'object' ? extra : {}),\n ...(customMeta && typeof customMeta === 'object' ? customMeta : {}),\n };\n const sentryMeta = {\n level: SentryErrorLevelMap[logLevel],\n extra: extraSentry,\n tags,\n contexts,\n user,\n fingerprint,\n };\n\n if (typeof message === 'string') {\n opts.sentryInstance.captureMessage(message, sentryMeta);\n } else if (message instanceof Error) {\n opts.sentryInstance.captureException(message, sentryMeta);\n }\n return;\n }\n\n if (typeof message === 'string') {\n if (meta instanceof Error) {\n const originalMessage = meta.message;\n meta.message = message;\n opts.sentryInstance.captureException(meta, { extra: { originalMessage } });\n } else {\n opts.sentryInstance.captureMessage(message, meta);\n }\n } else if (message instanceof Error) {\n opts.sentryInstance.captureException(message, meta);\n } else if (meta instanceof Error) {\n opts.sentryInstance.captureException(meta, meta);\n } else {\n opts.sentryInstance.captureMessage(JSON.stringify(message), meta);\n }\n };\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,73 @@
1
+ import logger, { SentryTransport } from '../index.js';
2
+ const sentryInstance = {
3
+ captureMessage: vi.fn(),
4
+ captureException: vi.fn(),
5
+ };
6
+ describe('sentry transport', () => {
7
+ beforeAll(() => {
8
+ logger.addTransport(new SentryTransport({ sentryInstance }));
9
+ });
10
+ afterEach(() => {
11
+ Object.values(sentryInstance).forEach((val) => val.mockClear());
12
+ });
13
+ afterAll(() => {
14
+ logger.clearTransports();
15
+ });
16
+ it.each([undefined, 'meta'])('should capture message when first arg is a string', (meta) => {
17
+ const customMsg = 'message';
18
+ logger.error(customMsg, meta);
19
+ expect(sentryInstance.captureMessage).toHaveBeenCalledWith(customMsg, meta);
20
+ expect(sentryInstance.captureException).not.toHaveBeenCalled();
21
+ });
22
+ it.each([undefined, 'meta'])('should capture exception when first arg is an Error', (meta) => {
23
+ const customMsg = 'msg';
24
+ const customErr = Error(customMsg);
25
+ logger.error(customErr, meta);
26
+ expect(sentryInstance.captureException).toHaveBeenCalledWith(customErr, meta);
27
+ expect(sentryInstance.captureMessage).not.toHaveBeenCalled();
28
+ });
29
+ it.each([null, [], () => { }])('should capture exception when second arg is an Error', (message) => {
30
+ const customErr = Error('err');
31
+ logger.error(message, customErr);
32
+ expect(sentryInstance.captureException).toHaveBeenCalledWith(customErr, customErr);
33
+ expect(sentryInstance.captureMessage).not.toHaveBeenCalled();
34
+ });
35
+ it('should capture message when args fall into catch all', () => {
36
+ logger.error([], []);
37
+ expect(sentryInstance.captureMessage).toHaveBeenCalledWith('[]', []);
38
+ expect(sentryInstance.captureException).not.toHaveBeenCalled();
39
+ });
40
+ it('should capture message when first arg is string and second arg is properly filled', () => {
41
+ const customMessage = 'Something happened';
42
+ const customMeta = {
43
+ tags: { type: 'buyer' },
44
+ extra: { filters: { id: 1 } },
45
+ contexts: { order: { id: 2 } },
46
+ };
47
+ logger.error(customMessage, customMeta);
48
+ expect(sentryInstance.captureMessage).toHaveBeenCalledWith(customMessage, expect.objectContaining(customMeta));
49
+ expect(sentryInstance.captureException).not.toHaveBeenCalled();
50
+ });
51
+ it('should capture exception when first arg is an Error and second arg is properly filled', () => {
52
+ const customErr = Error('Something happened');
53
+ const customMeta = {
54
+ tags: { type: 'buyer' },
55
+ extra: { filters: { id: 1 } },
56
+ contexts: { order: { id: 2 } },
57
+ };
58
+ logger.error(customErr, customMeta);
59
+ expect(sentryInstance.captureException).toHaveBeenCalledWith(customErr, expect.objectContaining(customMeta));
60
+ expect(sentryInstance.captureMessage).not.toHaveBeenCalled();
61
+ });
62
+ it('should capture an exception with custom message when first arg is a string but second is an Error', () => {
63
+ const customMessage = 'My custom message';
64
+ const customErrorMessage = 'My custom error';
65
+ const customError = Error(customErrorMessage);
66
+ expect(customError.message).toBe(customErrorMessage);
67
+ logger.error(customMessage, customError);
68
+ expect(customError.message).toBe(customMessage);
69
+ expect(sentryInstance.captureException).toHaveBeenCalledWith(customError, expect.objectContaining({ extra: { originalMessage: customErrorMessage } }));
70
+ expect(sentryInstance.captureMessage).not.toHaveBeenCalled();
71
+ });
72
+ });
73
+ //# sourceMappingURL=sentry.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentry.spec.js","sourceRoot":"","sources":["../../src/transports/sentry.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import logger, { SentryTransport } from '../index.js';\n\nconst sentryInstance = {\n captureMessage: vi.fn(),\n captureException: vi.fn(),\n};\n\ndescribe('sentry transport', () => {\n beforeAll(() => {\n logger.addTransport(new SentryTransport({ sentryInstance }));\n });\n\n afterEach(() => {\n Object.values(sentryInstance).forEach((val) => val.mockClear());\n });\n\n afterAll(() => {\n logger.clearTransports();\n });\n\n it.each([undefined, 'meta'])('should capture message when first arg is a string', (meta) => {\n const customMsg = 'message';\n\n logger.error(customMsg, meta);\n\n expect(sentryInstance.captureMessage).toHaveBeenCalledWith(customMsg, meta);\n expect(sentryInstance.captureException).not.toHaveBeenCalled();\n });\n\n it.each([undefined, 'meta'])('should capture exception when first arg is an Error', (meta) => {\n const customMsg = 'msg';\n const customErr = Error(customMsg);\n\n logger.error(customErr, meta);\n\n expect(sentryInstance.captureException).toHaveBeenCalledWith(customErr, meta);\n expect(sentryInstance.captureMessage).not.toHaveBeenCalled();\n });\n\n it.each([null, [], () => {}])('should capture exception when second arg is an Error', (message) => {\n const customErr = Error('err');\n\n logger.error(message, customErr);\n\n expect(sentryInstance.captureException).toHaveBeenCalledWith(customErr, customErr);\n expect(sentryInstance.captureMessage).not.toHaveBeenCalled();\n });\n\n it('should capture message when args fall into catch all', () => {\n logger.error([], []);\n\n expect(sentryInstance.captureMessage).toHaveBeenCalledWith('[]', []);\n expect(sentryInstance.captureException).not.toHaveBeenCalled();\n });\n\n it('should capture message when first arg is string and second arg is properly filled', () => {\n const customMessage = 'Something happened';\n const customMeta = {\n tags: { type: 'buyer' },\n extra: { filters: { id: 1 } },\n contexts: { order: { id: 2 } },\n };\n\n logger.error(customMessage, customMeta);\n\n expect(sentryInstance.captureMessage).toHaveBeenCalledWith(customMessage, expect.objectContaining(customMeta));\n expect(sentryInstance.captureException).not.toHaveBeenCalled();\n });\n\n it('should capture exception when first arg is an Error and second arg is properly filled', () => {\n const customErr = Error('Something happened');\n const customMeta = {\n tags: { type: 'buyer' },\n extra: { filters: { id: 1 } },\n contexts: { order: { id: 2 } },\n };\n\n logger.error(customErr, customMeta);\n\n expect(sentryInstance.captureException).toHaveBeenCalledWith(customErr, expect.objectContaining(customMeta));\n expect(sentryInstance.captureMessage).not.toHaveBeenCalled();\n });\n\n it('should capture an exception with custom message when first arg is a string but second is an Error', () => {\n const customMessage = 'My custom message';\n const customErrorMessage = 'My custom error';\n const customError = Error(customErrorMessage);\n\n expect(customError.message).toBe(customErrorMessage);\n\n logger.error(customMessage, customError);\n\n expect(customError.message).toBe(customMessage);\n\n expect(sentryInstance.captureException).toHaveBeenCalledWith(\n customError,\n expect.objectContaining({ extra: { originalMessage: customErrorMessage } }),\n );\n expect(sentryInstance.captureMessage).not.toHaveBeenCalled();\n });\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leaflink/snitch",
3
- "version": "0.0.0-PR-56--4e938e8",
3
+ "version": "0.0.0-PR-56--70fa2e6",
4
4
  "description": "Front end logging inspired by winston.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -25,6 +25,8 @@
25
25
  "default": "./dist/transports/datadog.js"
26
26
  }
27
27
  },
28
+ "main": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
28
30
  "engines": {
29
31
  "node": ">=16",
30
32
  "npm": ">=8"
@@ -32,7 +34,6 @@
32
34
  "files": [
33
35
  "dist"
34
36
  ],
35
- "types": "./dist/index.d.ts",
36
37
  "publishConfig": {
37
38
  "access": "public"
38
39
  },
@@ -57,6 +58,7 @@
57
58
  "@commitlint/cli": "^17.4.4",
58
59
  "@commitlint/config-conventional": "^17.4.4",
59
60
  "@datadog/browser-logs": "^4.39.0",
61
+ "@datadog/mobile-react-native": "^2.6.3",
60
62
  "@vitest/coverage-c8": "^0.29.2",
61
63
  "eslint": "^8.41.0",
62
64
  "eslint-config-leaflink": "^1.3.1",