From d5ada3c426274e162de4683b575f2828899fba88 Mon Sep 17 00:00:00 2001 From: Ryan Fink Date: Fri, 15 Jul 2022 10:10:44 -0400 Subject: [PATCH] Allow custom json replacer function - addresses issues #46 --- package-lock.json | 4 ++-- src/buffer-manager.ts | 2 +- src/capture.ts | 2 +- src/logdna.d.ts | 5 +++-- src/utils.ts | 5 +++-- tests/init.spec.ts | 11 +++++++++++ tests/utils.spec.ts | 13 +++++++++++++ 7 files changed, 34 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index d61efd3..658481d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@logdna/browser", - "version": "1.1.4", + "version": "2.0.10", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@logdna/browser", - "version": "1.1.4", + "version": "2.0.10", "license": "MIT", "dependencies": { "detect-browser": "^5.2.0", diff --git a/src/buffer-manager.ts b/src/buffer-manager.ts index e79e91e..979e13b 100644 --- a/src/buffer-manager.ts +++ b/src/buffer-manager.ts @@ -94,7 +94,7 @@ const send = async (lines: LogDNALogLine[]) => { Authorization: `Basic ${btoa(`${opts.ingestionKey}:`)}`, 'Content-Type': 'application/json', }, - body: utils.stringify({ lines }), + body: utils.stringify({ lines }, opts.jsonReplacer), }); if (response.ok) { diff --git a/src/capture.ts b/src/capture.ts index 5ba8d1f..06a0962 100644 --- a/src/capture.ts +++ b/src/capture.ts @@ -57,7 +57,7 @@ const generateLogLine = ({ level = 'log', message, lineContext = {}, errorContex process({ timestamp: Math.floor(Date.now() / 1000), app: opts.app || window.location.host, - line: typeof data.message === 'string' ? data.message : utils.stringify(data.message), + line: typeof data.message === 'string' ? data.message : utils.stringify(data.message, opts.jsonReplacer), level: data.level, meta: { sessionId: getSessionId(), diff --git a/src/logdna.d.ts b/src/logdna.d.ts index e8a72ad..01bad8e 100644 --- a/src/logdna.d.ts +++ b/src/logdna.d.ts @@ -1,7 +1,7 @@ import { GlobalErrorHandlerPlugin } from './plugins/global-handler'; -declare module 'logdna-browser-2' { } +declare module 'logdna-browser-2' {} -interface LogDNAMethods { } +interface LogDNAMethods {} // This is fallback to 3rd party plugin methods // Until TS has better Module Augmentation without @@ -54,6 +54,7 @@ export type LogDNABrowserOptions = { ingestionKey?: string; hooks?: HooksOption; internalErrorLogger?: Function; + jsonReplacer?: (key: string, value: any) => any | undefined; }; export type LineContext = object; diff --git a/src/utils.ts b/src/utils.ts index 7b44551..e253316 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,6 @@ import safeStringify from 'fast-safe-stringify'; import { HOSTNAME_CHECK, DEFAULT_TAG, SESSION_SCORE_KEY } from './constants'; +import { getOptions } from './init'; import { Tags } from './logdna'; @@ -17,7 +18,7 @@ const parseTags = (tags: Tags = []) => { return [DEFAULT_TAG, ...tags].filter(tag => tag !== '').join(','); }; -const stringify = (obj: unknown) => safeStringify(obj); +const stringify = (obj: unknown, replacer?: ((key: string, value: any) => any) | undefined) => safeStringify(obj, replacer); const getStackTrace = () => { const stack = new Error().stack || ''; @@ -48,7 +49,7 @@ const _randomBetween = (min: number, max: number) => { const backOffWithJitter = (base: number, cap: number, lastSleep: number) => Math.min(cap, _randomBetween(base, lastSleep * 3)); const jsonByteSize = (obj: unknown) => { - const stringified = stringify(obj); + const stringified = stringify(obj, getOptions().jsonReplacer); if (window.TextEncoder) { return new TextEncoder().encode(stringified).length; } diff --git a/tests/init.spec.ts b/tests/init.spec.ts index 13d8084..b28a0b3 100644 --- a/tests/init.spec.ts +++ b/tests/init.spec.ts @@ -54,6 +54,17 @@ describe('init.ts', () => { expect(addDefaultPlugins).toHaveBeenCalled(); expect(addPluginMethods).toHaveBeenCalled(); }); + + it('should add the JSON replacer', () => { + let jsonReplacer = (key: string, value: any) => { + if (key === 'password') { + return '[redacted]'; + } + return value; + }; + config(INGESTION_KEY, { jsonReplacer }); + expect(getOptions().jsonReplacer).toEqual(jsonReplacer); + }); }); describe('init', () => { diff --git a/tests/utils.spec.ts b/tests/utils.spec.ts index 9751f8c..a8f00a1 100644 --- a/tests/utils.spec.ts +++ b/tests/utils.spec.ts @@ -31,6 +31,19 @@ describe('Utils', () => { }; expect(utils.stringify(encode)).toEqual('{"data":{"obj":{"obj":"[Circular]"}}}'); }); + it('should properly use the JSON replacer function', () => { + let obj: any = { + email: 'test@example.com', + password: 'SuperSecretPassword', + }; + let jsonReplacer = (key: string, value: any) => { + if (key === 'password') { + return '[redacted]'; + } + return value; + }; + expect(utils.stringify(obj, jsonReplacer)).toEqual('{"email":"test@example.com","password":"[redacted]"}'); + }); }); describe('jsonByteSize', () => {