From 5e68cf9478c51f8f25100bbf3cfd251551431889 Mon Sep 17 00:00:00 2001 From: Daniel Ziegler Date: Fri, 13 Dec 2024 08:10:28 +0100 Subject: [PATCH] Fix huge memory consumption for logging --- CHANGELOG.md | 4 ++++ package-lock.json | 4 ++-- package.json | 2 +- src/util/stringify.test.ts | 14 ++++++++++++-- src/util/stringify.ts | 14 +++++++++++++- src/validation.ts | 9 ++++----- 6 files changed, 36 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25dbf8d..d873621 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## 3.1.2 (13.12.2024) +* Use default limits for array length, object depth or string length for logging (reverts change from version 3.1.1) as this could cause a huge memory consumption in some cases +* Use `JSON.stringify` to log objects that have a `toJSON` method + ## 3.1.1 (15.11.2024) * Do not limit array length, object depth or string length for logging diff --git a/package-lock.json b/package-lock.json index f4a69ac..e1cc0a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@senacor/azure-function-middleware", - "version": "3.1.1", + "version": "3.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@senacor/azure-function-middleware", - "version": "3.1.1", + "version": "3.1.2", "license": "MIT", "dependencies": { "@azure/functions": "^4.0.0", diff --git a/package.json b/package.json index 14476a3..67d069d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@senacor/azure-function-middleware", - "version": "3.1.1", + "version": "3.1.2", "description": "Middleware for azure functions to handle authentication, authorization, error handling and logging", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/util/stringify.test.ts b/src/util/stringify.test.ts index 2d14c48..5ea6ff8 100644 --- a/src/util/stringify.test.ts +++ b/src/util/stringify.test.ts @@ -25,7 +25,17 @@ describe('stringify should', () => { expect(stringify('Test', 2, { message: 'World' })).toBe("Test 2 { message: 'World' }"); }); - it('print all array items', () => { - expect(stringify(Array(250).fill(1)).match(/1/g)?.length).toBe(250); + it('do not print all items of large arrays', () => { + expect(stringify(Array(250).fill(1)).match(/1/g)?.length).toBeLessThan(250); + }); + + it('use toJSON function if available', () => { + const input = { + toJSON: () => ({ + value: 42, + }), + }; + + expect(stringify(input)).toEqual('{"value":42}'); }); }); diff --git a/src/util/stringify.ts b/src/util/stringify.ts index 71096b5..caef247 100644 --- a/src/util/stringify.ts +++ b/src/util/stringify.ts @@ -9,5 +9,17 @@ function stringifyValue(value: unknown): string { return value; } - return inspect(value, { depth: null, maxArrayLength: null, maxStringLength: null }); + if (isJsonSerializable(value)) { + return JSON.stringify(value); + } + + return inspect(value); +} + +type JsonSerializable = { + toJSON: () => unknown; +}; + +function isJsonSerializable(value: unknown): value is JsonSerializable { + return typeof (value as JsonSerializable)?.toJSON === 'function'; } diff --git a/src/validation.ts b/src/validation.ts index 31084b6..07e67a8 100644 --- a/src/validation.ts +++ b/src/validation.ts @@ -3,7 +3,6 @@ import { AnySchema } from 'joi'; import { ApplicationError } from './error'; import { BeforeExecutionFunction, MiddlewareResult, PostExecutionFunction, isErrorResult } from './middleware'; -import { stringify } from './util/stringify'; type ValidationOptions = Partial<{ transformErrorMessage: (message: string) => unknown; @@ -39,8 +38,8 @@ export function requestValidation(schema: AnySchema, opts?: ValidationOptions): if (validationResult && validationResult.error) { context.error( `The request did not match the given schema.${ - printRequest ? stringify(toBeValidatedContent) : '' - }: ${stringify(validationResult)}`, + printRequest ? JSON.stringify(toBeValidatedContent) : '' + }: ${JSON.stringify(validationResult)}`, ); if (shouldThrowOnValidationError) { @@ -93,8 +92,8 @@ export function responseValidation(schema: AnySchema, opts?: ValidationOptions): if (validationResult && validationResult.error) { context.error( `The response did not match the given schema.${ - printResponse ? stringify(toBeValidatedContent) : '' - }: ${stringify(validationResult)}`, + printResponse ? JSON.stringify(toBeValidatedContent) : '' + }: ${JSON.stringify(validationResult)}`, ); if (shouldThrowOnValidationError) {