From 26ef565c8a20d1b6c2e09c679eb96d3f56811a87 Mon Sep 17 00:00:00 2001 From: Jens-Uwe Mager Date: Wed, 8 Jan 2025 20:22:26 +0100 Subject: [PATCH] feat: Add a settings option to log to console in json format (#25649) Co-authored-by: Nerivec <62446222+Nerivec@users.noreply.github.com> --- lib/types/types.d.ts | 1 + lib/util/logger.ts | 16 +++++++++------- lib/util/settings.schema.json | 7 +++++++ lib/util/settings.ts | 1 + test/extensions/bridge.test.ts | 1 + test/logger.test.ts | 24 ++++++++++++++++++++++++ 6 files changed, 43 insertions(+), 7 deletions(-) diff --git a/lib/types/types.d.ts b/lib/types/types.d.ts index 86220707a0..b3158b4359 100644 --- a/lib/types/types.d.ts +++ b/lib/types/types.d.ts @@ -168,6 +168,7 @@ declare global { device_options: KeyValue; advanced: { log_rotation: boolean; + log_console_json: boolean; log_symlink_current: boolean; log_output: ('console' | 'file' | 'syslog')[]; log_directory: string; diff --git a/lib/util/logger.ts b/lib/util/logger.ts index 25dca6029d..03aebf3c3d 100644 --- a/lib/util/logger.ts +++ b/lib/util/logger.ts @@ -56,13 +56,15 @@ class Logger { this.logger.add( new winston.transports.Console({ silent: consoleSilenced, - // winston.config.syslog.levels sets 'warning' as 'red' - format: winston.format.combine( - winston.format.colorize({colors: {debug: 'blue', info: 'green', warning: 'yellow', error: 'red'}}), - winston.format.printf((info) => { - return `[${info.timestamp}] ${info.level}: \t${info.message}`; - }), - ), + format: settings.get().advanced.log_console_json + ? winston.format.json() + : winston.format.combine( + // winston.config.syslog.levels sets 'warning' as 'red' + winston.format.colorize({colors: {debug: 'blue', info: 'green', warning: 'yellow', error: 'red'}}), + winston.format.printf((info) => { + return `[${info.timestamp}] ${info.level}: \t${info.message}`; + }), + ), }), ); diff --git a/lib/util/settings.schema.json b/lib/util/settings.schema.json index 7aa3eb207d..dc7ef0366a 100644 --- a/lib/util/settings.schema.json +++ b/lib/util/settings.schema.json @@ -452,6 +452,13 @@ "description": "Log rotation", "default": true }, + "log_console_json": { + "type": "boolean", + "title": "Console json log", + "requiresRestart": true, + "description": "Console json log", + "default": false + }, "log_symlink_current": { "type": "boolean", "title": "Log symlink current", diff --git a/lib/util/settings.ts b/lib/util/settings.ts index c236ee3880..db53afc269 100644 --- a/lib/util/settings.ts +++ b/lib/util/settings.ts @@ -86,6 +86,7 @@ export const defaults: RecursivePartial = { device_options: {}, advanced: { log_rotation: true, + log_console_json: false, log_symlink_current: false, log_output: ['console', 'file'], log_directory: path.join(data.getPath(), 'log', '%TIMESTAMP%'), diff --git a/test/extensions/bridge.test.ts b/test/extensions/bridge.test.ts index d345481c85..5172de4c9c 100644 --- a/test/extensions/bridge.test.ts +++ b/test/extensions/bridge.test.ts @@ -118,6 +118,7 @@ describe('Extension: Bridge', () => { log_level: 'info', log_namespaced_levels: {}, log_output: ['console', 'file'], + log_console_json: false, log_rotation: true, log_symlink_current: false, log_syslog: {}, diff --git a/test/logger.test.ts b/test/logger.test.ts index 4420844300..a193ab7835 100644 --- a/test/logger.test.ts +++ b/test/logger.test.ts @@ -397,4 +397,28 @@ describe('Logger', () => { expect(logSpy).toHaveBeenLastCalledWith('debug', `z2m:test: ${splatChars}`); expect(consoleWriteSpy.mock.calls[1][0]).toMatch(new RegExp(`^.*\tz2m:test: ${splatChars}`)); }); + + it('Logs to console in JSON when configured', () => { + settings.set(['advanced', 'log_console_json'], true); + logger.init(); + + consoleWriteSpy.mockClear(); + logger.info(`Test JSON message`, 'z2m'); + + const outputJSON = JSON.parse(consoleWriteSpy.mock.calls[0][0]); + expect(outputJSON).toStrictEqual({ + level: 'info', + message: 'z2m: Test JSON message', + timestamp: expect.any(String), + }); + + settings.set(['advanced', 'log_console_json'], false); + logger.init(); + + consoleWriteSpy.mockClear(); + logger.info(`Test JSON message`, 'z2m'); + + const outputStr: string = consoleWriteSpy.mock.calls[0][0]; + expect(outputStr.trim().endsWith('\u001b[32minfo\u001b[39m: \tz2m: Test JSON message')).toStrictEqual(true); + }); });