From 22fa653c5cc654391bba475553ab4cf922c2e124 Mon Sep 17 00:00:00 2001 From: Ryan Ling Date: Fri, 6 Oct 2023 17:36:17 +1100 Subject: [PATCH] Simplify `createOmitPropertiesSerializer` --- src/serializers/index.ts | 44 +++++++++---------- .../omitPropertiesSerializer.test.ts | 35 +++------------ src/serializers/omitPropertiesSerializer.ts | 39 +++++----------- src/serializers/types.ts | 1 + 4 files changed, 39 insertions(+), 80 deletions(-) create mode 100644 src/serializers/types.ts diff --git a/src/serializers/index.ts b/src/serializers/index.ts index 4505086..9124d04 100644 --- a/src/serializers/index.ts +++ b/src/serializers/index.ts @@ -1,8 +1,8 @@ import type pino from 'pino'; import { err, errWithCause } from 'pino-std-serializers'; -import { omitProperties } from './omitProperties'; import { createOmitPropertiesSerializer } from './omitPropertiesSerializer'; +import type { SerializerFn } from './types'; export const defaultOmitHeaderNames = [ 'x-envoy-attempt-count', @@ -45,19 +45,17 @@ const isObject = (value: unknown): boolean => { return value != null && (type === 'object' || type === 'function'); }; -const createReqSerializer = (opts: SerializerOptions) => (request: Request) => - isObject(request) - ? { - method: request.method, - url: request.url, - headers: omitProperties( - request.headers, - opts.omitHeaderNames ?? defaultOmitHeaderNames, - ), - remoteAddress: request?.socket?.remoteAddress, - remotePort: request?.socket?.remotePort, - } - : request; +const createReqSerializer = + (serializeHeaders: SerializerFn) => (request: Request) => + isObject(request) + ? { + method: request.method, + url: request.url, + headers: serializeHeaders(request.headers), + remoteAddress: request?.socket?.remoteAddress, + remotePort: request?.socket?.remotePort, + } + : request; const res = (response: Response) => isObject(response) @@ -70,15 +68,17 @@ const res = (response: Response) => export const createSerializers = ( opts: SerializerOptions & Pick, ) => { - const omitHeaderNamesSerializer = createOmitPropertiesSerializer('headers', { - omitPropertyNames: opts.omitHeaderNames ?? defaultOmitHeaderNames, - }); - return { + const serializeHeaders = createOmitPropertiesSerializer( + opts.omitHeaderNames ?? defaultOmitHeaderNames, + ); + + const serializers = { err, errWithCause, - req: createReqSerializer(opts), + req: createReqSerializer(serializeHeaders), res, - ...omitHeaderNamesSerializer, - ...opts.serializers, - }; + headers: serializeHeaders, + } satisfies pino.LoggerOptions['serializers']; + + return serializers; }; diff --git a/src/serializers/omitPropertiesSerializer.test.ts b/src/serializers/omitPropertiesSerializer.test.ts index 4d442f1..4f26287 100644 --- a/src/serializers/omitPropertiesSerializer.test.ts +++ b/src/serializers/omitPropertiesSerializer.test.ts @@ -8,21 +8,6 @@ import { createOmitPropertiesSerializer } from './omitPropertiesSerializer'; const omitPropertyNamesBase = ['remove-prop', 'remove.prop']; -it.each` - scenario | topLevelPropertyName - ${'undefined'} | ${undefined} - ${'null'} | ${null} -`( - 'returns empty object when topLevelPropertyName is $scenario', - ({ topLevelPropertyName }) => { - const serializer = createOmitPropertiesSerializer(topLevelPropertyName, { - omitPropertyNames: [...omitPropertyNamesBase], - }); - - expect(serializer).toStrictEqual({}); - }, -); - it.each` scenario | omitPropertyNames ${'undefined'} | ${undefined} @@ -32,20 +17,16 @@ it.each` `( 'returns empty object when omitPropertyNames is $scenario', ({ omitPropertyNames }) => { - const serializer = createOmitPropertiesSerializer('main', { - omitPropertyNames, - }); + const serializer = createOmitPropertiesSerializer(omitPropertyNames); - expect(serializer).toStrictEqual({}); + expect(serializer({})).toStrictEqual({}); }, ); it('returns object with named property containing function', () => { - const serializer = createOmitPropertiesSerializer('main', { - omitPropertyNames: [...omitPropertyNamesBase], - }); + const serializer = createOmitPropertiesSerializer(omitPropertyNamesBase); - expect(serializer).toStrictEqual({ main: expect.any(Function) }); + expect(serializer).toStrictEqual(expect.any(Function)); }); const sink = () => @@ -71,9 +52,7 @@ function once(emitter: Transform, name: string) { } it('omits properties from logged object', async () => { - const serializer = createOmitPropertiesSerializer('main', { - omitPropertyNames: [...omitPropertyNamesBase], - }); + const serializer = createOmitPropertiesSerializer(omitPropertyNamesBase); const input = { main: { @@ -112,9 +91,7 @@ it.each` `( 'does nothing when top-level property is $scenario', async ({ value, expected }) => { - const serializer = createOmitPropertiesSerializer('main', { - omitPropertyNames: ['keepProp'], - }); + const serializer = createOmitPropertiesSerializer(['keepProp']); const input = { main: value, diff --git a/src/serializers/omitPropertiesSerializer.ts b/src/serializers/omitPropertiesSerializer.ts index ca67339..5c4c7e2 100644 --- a/src/serializers/omitPropertiesSerializer.ts +++ b/src/serializers/omitPropertiesSerializer.ts @@ -1,38 +1,19 @@ import { omitProperties } from './omitProperties'; +import type { SerializerFn } from './types'; -export interface OmitPropertiesSerializerOptions { +export const createOmitPropertiesSerializer = ( /** * A list of properties that should not be logged. */ - omitPropertyNames: string[]; -} - -type OmitHeaderNamesFn = (record: unknown) => unknown; -type OmitHeaderNamesSerializer = Record; + properties: string[], +): SerializerFn => { + const uniquePropertySet = new Set(properties); -/** Creates a serializer that operates on the logged object's top-level property named `topLevelPropertyName` - * and omits the properties listed in `options.omitPropertyNames`. - * @param topLevelPropertyName - The name of the root property on the logged object that to omit properties from. - * @param options - Options for the serializer. - */ -export const createOmitPropertiesSerializer = ( - topLevelPropertyName: string, - options: OmitPropertiesSerializerOptions, -): OmitHeaderNamesSerializer => { - const propertyNames = [...new Set(options.omitPropertyNames ?? [])].filter( - (propertyName) => typeof propertyName === 'string', - ); - - if ( - !topLevelPropertyName || - typeof topLevelPropertyName !== 'string' || - topLevelPropertyName.length === 0 || - propertyNames.length === 0 - ) { - return {}; + if (uniquePropertySet.size === 0) { + return (input) => input; } - return { - [topLevelPropertyName]: (record) => omitProperties(record, propertyNames), - }; + const uniqueProperties = Array.from(uniquePropertySet); + + return (input) => omitProperties(input, uniqueProperties); }; diff --git a/src/serializers/types.ts b/src/serializers/types.ts new file mode 100644 index 0000000..356cb88 --- /dev/null +++ b/src/serializers/types.ts @@ -0,0 +1 @@ +export type SerializerFn = (input: unknown) => unknown;