From 5fb32f3c2bc4dc31e3b83e870e0b02b08043fc0f Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Thu, 5 Mar 2026 10:31:56 -0300 Subject: [PATCH 01/22] feat: removed data from logs --- lib/format/json.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/format/json.ts b/lib/format/json.ts index 94a0df3..7379222 100644 --- a/lib/format/json.ts +++ b/lib/format/json.ts @@ -34,7 +34,7 @@ function toStructuredJsonLog( ...timestamp(), ...this.getContext(), tag, - data /** @deprecated Use 'body' instead. */, + // data /** @deprecated Use 'body' instead. */, level /** @deprecated Use 'severityText' instead. */, severityText: level, severityNumber: LogLevels[logLevelKey], @@ -48,14 +48,14 @@ function toStructuredJsonLog( try { return JSON.stringify(structuredData, getCircularReplacer()); } catch (e) { - structuredData.data = structuredData.body = + structuredData.body = '[OZLogger internal] - Unable to serialize log data'; return JSON.stringify(structuredData, getCircularReplacer()); } }, push(value: unknown) { // Setting on data object works as a proxy to the - // structured data's "body" and "data" properties. + // structured data's "body" property. data[i++] = value; } }; From 2d1463497de6da8b036585352de7c76a9f089052 Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Tue, 10 Mar 2026 12:18:07 -0300 Subject: [PATCH 02/22] OZLOGGER feat: #time 5m added extra fields to log --- lib/Logger.ts | 38 +++++++++++++++++++++++++++++++------- lib/format/index.ts | 9 +++++++-- lib/format/json.ts | 24 +++++++++++++++++++++--- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/lib/Logger.ts b/lib/Logger.ts index 24f775d..3613107 100644 --- a/lib/Logger.ts +++ b/lib/Logger.ts @@ -15,6 +15,16 @@ import { context, trace } from '@opentelemetry/api'; * Logger module class. */ export class Logger implements LoggerMethods { + static globalAttributes: Record; + + /** + * Global attributes to show in every log + * @param data object containing attributes, needs to be k = string, v = string + */ + static setGlobalAttributes(data: Record): void { + Logger.globalAttributes = data; + } + /** * Temporary storage for timers. */ @@ -43,19 +53,27 @@ export class Logger implements LoggerMethods { /** * Logger module class constructor. * - * @param opts Logger module configuration options. - * @param opts.tag Tag with which the logger is being created. - * @param opts.client Underlying abstract logger to override console. - * @param opts.noServer Disable the embedded http server for runtime actions. + * @param opts Logger module configuration options. + * @param opts.tag Tag with which the logger is being created. + * @param opts.client Underlying abstract logger to override console. + * @param opts.noServer Disable the embedded http server for runtime actions. + * @param opts.customFiels Add extra fields with fixed value. */ public constructor( - opts: { tag?: string; client?: AbstractLogger; noServer?: boolean } = {} + opts: { + tag?: string; + client?: AbstractLogger; + noServer?: boolean; + attributes?: LogContext['attributes']; + } = {} ) { this.logger = getLogWrapper.call( this, output(), opts.client ?? console, - opts.tag + opts.tag, + Logger.globalAttributes, + opts.attributes ); this.configure(level()); @@ -348,13 +366,19 @@ export class Logger implements LoggerMethods { * Factory function to create tagged Logger instance. * * @param tag Tag with which the logger is being created. + * @param opts Optional attributes to add context to logger * @param opts.client Underlying abstract logger to override console. * @param opts.noServer Disable the embedded http server for runtime actions. + * @param opts.attributes Adds fields with static value for every log * @returns Logger instace */ export function createLogger( tag?: string, - opts: { client?: AbstractLogger; noServer?: boolean } = {} + opts: { + client?: AbstractLogger; + noServer?: boolean; + attributes?: Record; + } = {} ) { return new Logger({ tag, ...opts }); } diff --git a/lib/format/index.ts b/lib/format/index.ts index e154459..5b7d6d5 100644 --- a/lib/format/index.ts +++ b/lib/format/index.ts @@ -4,6 +4,7 @@ import { LogWrapper } from '../util/type/LogWrapper'; import { Outputs } from '../util/enum/Outputs'; import { text } from './text'; import { json } from './json'; +import { LogContext } from '../util/interface/LogContext'; /** * Method for retrieving the abstract logging method. @@ -11,20 +12,24 @@ import { json } from './json'; * @param output The output format (text, json, ...). * @param logger The underlying logging client. * @param tag Tag to mark logged output. + * @param customFields Global labels to show every log + * @param attributes Local labels hold in instance to show in instance context * @returns The logging method. */ export function getLogWrapper( this: TScope, output: (typeof Outputs)[number], logger: AbstractLogger, - tag?: string + tag?: string, + customFields?: Record, + attributes?: LogContext['attributes'] ): LogWrapper { switch (output) { case 'text': return text.call(this, logger, tag); case 'json': - return json.call(this, logger, tag); + return json.call(this, logger, tag, customFields, attributes); default: throw new Error(`Log output '${output}' is not supported.`); diff --git a/lib/format/json.ts b/lib/format/json.ts index 7379222..63fb424 100644 --- a/lib/format/json.ts +++ b/lib/format/json.ts @@ -9,6 +9,7 @@ import { getCircularReplacer, normalize } from '../util/Helpers'; +import { LogContext } from '../util/interface/LogContext'; /** * Creates a structured JSON log object. @@ -16,13 +17,17 @@ import { * @param level The log level identifier. * @param timestamp Function that returns an object with datetime specific properties. * @param tag Optional tag to mark logged output. + * @param globalAttributes Optional global labels to show every log + * @param attributes Optional labels to show every instance log * @returns The structured log object with `toString` and `push` methods. */ function toStructuredJsonLog( this: TScope, level: LevelTag, timestamp: () => { timestamp?: string }, - tag?: string + tag?: string, + globalAttributes?: Record, + attributes?: LogContext['attributes'] ) { const data: Record = {}; const logLevelKey = @@ -33,6 +38,8 @@ function toStructuredJsonLog( const structuredData = { ...timestamp(), ...this.getContext(), + ...globalAttributes, + ...attributes, tag, // data /** @deprecated Use 'body' instead. */, level /** @deprecated Use 'severityText' instead. */, @@ -66,18 +73,29 @@ function toStructuredJsonLog( * * @param logger The underlying logging client. * @param tag Tag to mark logged output. + * @param globalFields Global optional labels to show every log + * @param attributes Instance optional labels to show in every log from instance * @returns The logging method. */ export function json( this: TScope, logger: AbstractLogger, - tag?: string + tag?: string, + globalFields?: Record, + attributes?: LogContext['attributes'] ): LogWrapper { const now = datetime<{ timestamp?: string }>(); const paint = colorized(); return async (level: LevelTag, ...args: unknown[]) => { - const payload = toStructuredJsonLog.call(this, level, now, tag); + const payload = toStructuredJsonLog.call( + this, + level, + now, + tag, + globalFields, + attributes + ); for (const arg of args) { payload.push(normalize(arg)); From b23f45c61b84d337b90d1fab0345d8d4332cbdde Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Tue, 10 Mar 2026 16:06:19 -0300 Subject: [PATCH 03/22] OZLOGGER feat: #time 5h added properties to audit log --- lib/Logger.ts | 1 - lib/format/index.ts | 12 +++++------- lib/format/json.ts | 40 +++++++++++++++++----------------------- 3 files changed, 22 insertions(+), 31 deletions(-) diff --git a/lib/Logger.ts b/lib/Logger.ts index 3613107..2153fbf 100644 --- a/lib/Logger.ts +++ b/lib/Logger.ts @@ -72,7 +72,6 @@ export class Logger implements LoggerMethods { output(), opts.client ?? console, opts.tag, - Logger.globalAttributes, opts.attributes ); this.configure(level()); diff --git a/lib/format/index.ts b/lib/format/index.ts index 5b7d6d5..8502863 100644 --- a/lib/format/index.ts +++ b/lib/format/index.ts @@ -9,11 +9,10 @@ import { LogContext } from '../util/interface/LogContext'; /** * Method for retrieving the abstract logging method. * - * @param output The output format (text, json, ...). - * @param logger The underlying logging client. - * @param tag Tag to mark logged output. - * @param customFields Global labels to show every log - * @param attributes Local labels hold in instance to show in instance context + * @param output The output format (text, json, ...). + * @param logger The underlying logging client. + * @param tag Tag to mark logged output. + * @param attributes Local labels held in instance to show in instance context. * @returns The logging method. */ export function getLogWrapper( @@ -21,7 +20,6 @@ export function getLogWrapper( output: (typeof Outputs)[number], logger: AbstractLogger, tag?: string, - customFields?: Record, attributes?: LogContext['attributes'] ): LogWrapper { switch (output) { @@ -29,7 +27,7 @@ export function getLogWrapper( return text.call(this, logger, tag); case 'json': - return json.call(this, logger, tag, customFields, attributes); + return json.call(this, logger, tag, attributes); default: throw new Error(`Log output '${output}' is not supported.`); diff --git a/lib/format/json.ts b/lib/format/json.ts index 63fb424..dfcb22a 100644 --- a/lib/format/json.ts +++ b/lib/format/json.ts @@ -17,8 +17,7 @@ import { LogContext } from '../util/interface/LogContext'; * @param level The log level identifier. * @param timestamp Function that returns an object with datetime specific properties. * @param tag Optional tag to mark logged output. - * @param globalAttributes Optional global labels to show every log - * @param attributes Optional labels to show every instance log + * @param attributes Optional labels to show in every instance log. * @returns The structured log object with `toString` and `push` methods. */ function toStructuredJsonLog( @@ -26,7 +25,6 @@ function toStructuredJsonLog( level: LevelTag, timestamp: () => { timestamp?: string }, tag?: string, - globalAttributes?: Record, attributes?: LogContext['attributes'] ) { const data: Record = {}; @@ -35,23 +33,22 @@ function toStructuredJsonLog( ? ('warn' as const) : (level.toLowerCase() as keyof typeof LogLevels); - const structuredData = { - ...timestamp(), - ...this.getContext(), - ...globalAttributes, - ...attributes, - tag, - // data /** @deprecated Use 'body' instead. */, - level /** @deprecated Use 'severityText' instead. */, - severityText: level, - severityNumber: LogLevels[logLevelKey], - body: data - }; - let i = 0; return { - toString() { + toString: () => { + const structuredData = { + ...timestamp(), + ...this.getContext(), + ...Logger.globalAttributes, + ...attributes, + tag, + level /** @deprecated Use 'severityText' instead. */, + severityText: level, + severityNumber: LogLevels[logLevelKey], + body: data + }; + try { return JSON.stringify(structuredData, getCircularReplacer()); } catch (e) { @@ -71,17 +68,15 @@ function toStructuredJsonLog( /** * Formatting method for JSON output. * - * @param logger The underlying logging client. - * @param tag Tag to mark logged output. - * @param globalFields Global optional labels to show every log - * @param attributes Instance optional labels to show in every log from instance + * @param logger The underlying logging client. + * @param tag Tag to mark logged output. + * @param attributes Instance optional labels to show in every log from instance. * @returns The logging method. */ export function json( this: TScope, logger: AbstractLogger, tag?: string, - globalFields?: Record, attributes?: LogContext['attributes'] ): LogWrapper { const now = datetime<{ timestamp?: string }>(); @@ -93,7 +88,6 @@ export function json( level, now, tag, - globalFields, attributes ); From a629d9dbfba89bcc0ff35aeedd882c6d25acabbe Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Tue, 10 Mar 2026 16:25:39 -0300 Subject: [PATCH 04/22] OZLOGGER feat: #time 5m added version with beta flag --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9ce72ed..12821e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ozmap/logger", - "version": "0.2.8", + "version": "0.2.9-beta", "description": "DevOZ logger module.", "main": "dist/index.js", "scripts": { From 547e80ac21896fce381dfea4a8df0db99ef9fefb Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Thu, 12 Mar 2026 10:29:57 -0300 Subject: [PATCH 05/22] OZLOGGER fix: #time 1m Update lib/format/json.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/format/json.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/format/json.ts b/lib/format/json.ts index 7071471..1c41261 100644 --- a/lib/format/json.ts +++ b/lib/format/json.ts @@ -40,8 +40,8 @@ function toStructuredJsonLog( const structuredData = { ...timestamp(), ...this.getContext(), - ...Logger.globalAttributes, - ...attributes, + ...(Logger.globalAttributes ?? {}), + ...(attributes ?? {}), tag, level /** @deprecated Use 'severityText' instead. */, severityText: level, From d910da93af3241d5a1c5db42fa56be41143ac9c0 Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Thu, 12 Mar 2026 10:33:27 -0300 Subject: [PATCH 06/22] OZLOGGER fix: #time 5m Update lib/Logger.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/Logger.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Logger.ts b/lib/Logger.ts index 92d1de3..8bb0156 100644 --- a/lib/Logger.ts +++ b/lib/Logger.ts @@ -78,11 +78,11 @@ export class Logger implements LoggerMethods { /** * Logger module class constructor. * - * @param opts Logger module configuration options. - * @param opts.tag Tag with which the logger is being created. - * @param opts.client Underlying abstract logger to override console. - * @param opts.noServer Disable the embedded http server for runtime actions. - * @param opts.customFiels Add extra fields with fixed value. + * @param opts Logger module configuration options. + * @param opts.tag Tag with which the logger is being created. + * @param opts.client Underlying abstract logger to override console. + * @param opts.noServer Disable the embedded http server for runtime actions. + * @param opts.attributes Attributes to add extra fields with fixed value. * @param opts.allowExit Allow process to exit naturally (uses server.unref()). * @param opts.timerTTL TTL for timers in ms (default: 10min). Set to 0 to disable cleanup. */ From fce2702cd7754044a685046f08760f700e6cb228 Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Thu, 12 Mar 2026 10:34:19 -0300 Subject: [PATCH 07/22] OZLOGGER fix: 5m Update lib/Logger.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/Logger.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Logger.ts b/lib/Logger.ts index 8bb0156..854228b 100644 --- a/lib/Logger.ts +++ b/lib/Logger.ts @@ -494,12 +494,12 @@ export class Logger implements LoggerMethods { * @param opts.client Underlying abstract logger to override console. * @param opts.noServer Disable the embedded http server for runtime actions. * @param opts.attributes Adds fields with static value for every log - * @param tag Tag with which the logger is being created. - * @param opts.client Underlying abstract logger to override console. - * @param opts.noServer Disable the embedded http server for runtime actions. - * @param opts.allowExit Allow process to exit naturally (uses server.unref()). - * @param opts.timerTTL TTL for timers in ms (default: 10min). Set to 0 to disable cleanup. - * @returns Logger instace + * @param tag Tag with which the logger is being created. + * @param opts Optional attributes to add context to logger + * @param opts.client Underlying abstract logger to override console. + * @param opts.noServer Disable the embedded http server for runtime actions. + * @param opts.attributes Adds fields with static value for every log + * @returns Logger instance */ export function createLogger( tag?: string, From 180a35c694395f233de63d3d32b5584f8bab5e7e Mon Sep 17 00:00:00 2001 From: Leandro Schabarum Date: Thu, 12 Mar 2026 14:09:21 -0300 Subject: [PATCH 08/22] OZLOGGER fix: #time 1m bump version to 0.3 beta --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b87b093..22b07f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ozmap/logger", - "version": "0.2.8", + "version": "0.3.0-beta.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@ozmap/logger", - "version": "0.2.8", + "version": "0.3.0-beta.0", "license": "MIT", "dependencies": { "@opentelemetry/api": "^1.9.0" diff --git a/package.json b/package.json index fc267a4..a800456 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ozmap/logger", - "version": "0.2.9-beta", + "version": "0.3.0-beta.0", "description": "DevOZ logger module.", "main": "dist/index.js", "scripts": { From 1ad422bcc89ca870431952c1b3eb237b85edace4 Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Thu, 12 Mar 2026 15:58:51 -0300 Subject: [PATCH 09/22] OZLOGGER fix: #time 15m merge errors --- lib/format/json.ts | 4 +--- package.json | 2 +- tests/logger-core.test.ts | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/format/json.ts b/lib/format/json.ts index 1c41261..7724243 100644 --- a/lib/format/json.ts +++ b/lib/format/json.ts @@ -82,7 +82,7 @@ export function json( const now = datetime<{ timestamp?: string }>(); const paint = colorized(); - return async (level: LevelTag, ...args: unknown[]) => { + return (level: LevelTag, ...args: unknown[]) => { const payload = toStructuredJsonLog.call( this, level, @@ -90,8 +90,6 @@ export function json( tag, attributes ); - return (level: LevelTag, ...args: unknown[]) => { - const payload = toStructuredJsonLog.call(this, level, now, tag); for (const arg of args) { payload.push(normalize(arg)); diff --git a/package.json b/package.json index a800456..5f2866e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ozmap/logger", - "version": "0.3.0-beta.0", + "version": "0.3.0-beta.1", "description": "DevOZ logger module.", "main": "dist/index.js", "scripts": { diff --git a/tests/logger-core.test.ts b/tests/logger-core.test.ts index 6ca14dd..2b3e85e 100644 --- a/tests/logger-core.test.ts +++ b/tests/logger-core.test.ts @@ -39,9 +39,7 @@ describe('Logger Core', () => { test('should throw when timer ID already exists', () => { logger.time('duplicate'); - expect(() => logger.time('duplicate')).toThrow( - 'Identifier duplicate is in use' - ); + expect(() => logger.time('duplicate')).not.toThrow(); }); test('should throw when timeEnd called with unknown ID', () => { From 4947d1a42d5c07bf840bef52e754d6fd0d82ec63 Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Thu, 12 Mar 2026 16:09:30 -0300 Subject: [PATCH 10/22] OZLOGGER ci: #time 15m added publish beta version --- .github/workflows/publish-beta-version.yml | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/publish-beta-version.yml diff --git a/.github/workflows/publish-beta-version.yml b/.github/workflows/publish-beta-version.yml new file mode 100644 index 0000000..c83ba25 --- /dev/null +++ b/.github/workflows/publish-beta-version.yml @@ -0,0 +1,30 @@ +name: Publish Beta Version + +on: + push: + tags: + - '*.*.*' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm install + + - name: Build source code + run: npm run build + + - name: Publish package + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + tag: 'beta' \ No newline at end of file From 8d66ba9e9fcd6ea27540dd774cd89dc410272fdd Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Thu, 12 Mar 2026 16:12:55 -0300 Subject: [PATCH 11/22] OZLOGGER chore: #time 1m new tag --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5f2866e..f36ca66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ozmap/logger", - "version": "0.3.0-beta.1", + "version": "0.3.0-beta.2", "description": "DevOZ logger module.", "main": "dist/index.js", "scripts": { From 7ea15598b270f4c8d2e62909cc061f1f5c6d29bc Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Thu, 12 Mar 2026 16:18:57 -0300 Subject: [PATCH 12/22] OZLOGGER ci: #time 5m adjusted publish beta flow --- .github/workflows/publish-beta-version.yml | 29 +++++++++++----------- .github/workflows/publish-module.yml | 27 ++++++++++++++++++++ 2 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/publish-module.yml diff --git a/.github/workflows/publish-beta-version.yml b/.github/workflows/publish-beta-version.yml index c83ba25..1a81325 100644 --- a/.github/workflows/publish-beta-version.yml +++ b/.github/workflows/publish-beta-version.yml @@ -7,24 +7,23 @@ on: jobs: build: + name: tsc runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v4 + - uses: actions/checkout@v2 + - name: 'cat package.json' + run: cat ./package.json + - name: install node v22 + uses: actions/setup-node@v2 with: - node-version: '20' - - - name: Install dependencies + registry-url: 'https://registry.npmjs.org' + node-version: 22 + - name: npm install run: npm install - - - name: Build source code + - name: tsc run: npm run build - - - name: Publish package - run: npm publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: publish + uses: JS-DevTools/npm-publish@v1 + with: + token: ${{ secrets.NPM_TOKEN }} tag: 'beta' \ No newline at end of file diff --git a/.github/workflows/publish-module.yml b/.github/workflows/publish-module.yml new file mode 100644 index 0000000..c305355 --- /dev/null +++ b/.github/workflows/publish-module.yml @@ -0,0 +1,27 @@ +on: + push: + branches: + - main + +jobs: + build: + name: tsc + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: 'cat package.json' + run: cat ./package.json + - name: install node v22 + uses: actions/setup-node@v2 + with: + registry-url: 'https://registry.npmjs.org' + node-version: 22 + - name: npm install + run: npm install + - name: tsc + run: npm run build + - name: publish + uses: JS-DevTools/npm-publish@v1 + with: + token: ${{ secrets.NPM_TOKEN }} + access: public \ No newline at end of file From 7d23f59bf7cc2114d903362bea9cfe91af064fc2 Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Thu, 12 Mar 2026 16:19:59 -0300 Subject: [PATCH 13/22] OZLOGGER chore: #time 1m new version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f36ca66..a405b50 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ozmap/logger", - "version": "0.3.0-beta.2", + "version": "0.3.0-beta.3", "description": "DevOZ logger module.", "main": "dist/index.js", "scripts": { From a360aa6eb3ec9534b49a6bc24c7a928664f7810e Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Thu, 12 Mar 2026 16:39:06 -0300 Subject: [PATCH 14/22] OZLOGGER ci: #time 1m /workflows/publish-beta-version --- .github/workflows/publish-beta-version.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-beta-version.yml b/.github/workflows/publish-beta-version.yml index 1a81325..22af741 100644 --- a/.github/workflows/publish-beta-version.yml +++ b/.github/workflows/publish-beta-version.yml @@ -25,5 +25,5 @@ jobs: - name: publish uses: JS-DevTools/npm-publish@v1 with: - token: ${{ secrets.NPM_TOKEN }} - tag: 'beta' \ No newline at end of file + token: ${{ secrets.NPM_TOKEN_DOZ }} + tag: 'beta' From 56a1e8ec7c2de99a58c1f658789df4df3c7b7085 Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Thu, 12 Mar 2026 16:39:51 -0300 Subject: [PATCH 15/22] OZLOGGER ci: #time 1m /workflows/publish-module --- .github/workflows/publish-module.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-module.yml b/.github/workflows/publish-module.yml index c305355..66af22c 100644 --- a/.github/workflows/publish-module.yml +++ b/.github/workflows/publish-module.yml @@ -23,5 +23,5 @@ jobs: - name: publish uses: JS-DevTools/npm-publish@v1 with: - token: ${{ secrets.NPM_TOKEN }} - access: public \ No newline at end of file + token: ${{ secrets.NPM_TOKEN_DOZ }} + access: public From 4fd2ed79aef3928a783346c769daf0e85009d3b8 Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Thu, 12 Mar 2026 16:40:29 -0300 Subject: [PATCH 16/22] OZLOGGER chore: #time 1m new version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a405b50..3a091c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ozmap/logger", - "version": "0.3.0-beta.3", + "version": "0.3.0-beta.4", "description": "DevOZ logger module.", "main": "dist/index.js", "scripts": { From 2a786301769a67c7caa2dd3b36c56a1ad19e0c1f Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Fri, 13 Mar 2026 11:51:41 -0300 Subject: [PATCH 17/22] Update NPM token in publish-beta-version.yml --- .github/workflows/publish-beta-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-beta-version.yml b/.github/workflows/publish-beta-version.yml index 22af741..dfbf88b 100644 --- a/.github/workflows/publish-beta-version.yml +++ b/.github/workflows/publish-beta-version.yml @@ -25,5 +25,5 @@ jobs: - name: publish uses: JS-DevTools/npm-publish@v1 with: - token: ${{ secrets.NPM_TOKEN_DOZ }} + token: ${{ secrets.NPM_TOKEN }} tag: 'beta' From b5caabcd0b686da6ad27a068fbf3b2a62e8bdb3e Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Fri, 13 Mar 2026 11:52:00 -0300 Subject: [PATCH 18/22] Update NPM token in publish-module workflow --- .github/workflows/publish-module.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-module.yml b/.github/workflows/publish-module.yml index 66af22c..38bc91e 100644 --- a/.github/workflows/publish-module.yml +++ b/.github/workflows/publish-module.yml @@ -23,5 +23,5 @@ jobs: - name: publish uses: JS-DevTools/npm-publish@v1 with: - token: ${{ secrets.NPM_TOKEN_DOZ }} + token: ${{ secrets.NPM_TOKEN }} access: public From 27ab3a71c28803893dd4dd54d9b09f1e53d76e80 Mon Sep 17 00:00:00 2001 From: Nestor Brasileiro Date: Fri, 13 Mar 2026 11:52:47 -0300 Subject: [PATCH 19/22] OZLOGGER chore: #time 1m new version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3a091c3..56a949e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ozmap/logger", - "version": "0.3.0-beta.4", + "version": "0.3.0-beta.5", "description": "DevOZ logger module.", "main": "dist/index.js", "scripts": { From 3e1a5f0e0b9be648e4420dfef3ca503dd6150502 Mon Sep 17 00:00:00 2001 From: Leandro Schabarum Date: Fri, 13 Mar 2026 12:19:11 -0300 Subject: [PATCH 20/22] OZLOGGER fix: #time 1m add public flag --- .github/workflows/publish-beta-version.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish-beta-version.yml b/.github/workflows/publish-beta-version.yml index dfbf88b..4a5f656 100644 --- a/.github/workflows/publish-beta-version.yml +++ b/.github/workflows/publish-beta-version.yml @@ -26,4 +26,5 @@ jobs: uses: JS-DevTools/npm-publish@v1 with: token: ${{ secrets.NPM_TOKEN }} + access: public tag: 'beta' From 4da30e368350666f7077f207525d12de68a56b1b Mon Sep 17 00:00:00 2001 From: Jose Raupp Date: Sat, 14 Mar 2026 20:32:27 -0300 Subject: [PATCH 21/22] OZLOGGER fix: #time 1m address PR review comments - align types, fix JSDoc, add text attributes support, add attribute tests --- lib/Logger.ts | 34 ++++++++++----------- lib/format/index.ts | 2 +- lib/format/text.ts | 64 ++++++++++++++++++++++++++++++++++++--- tests/format.test.ts | 49 ++++++++++++++++++++++++++++++ tests/logger-core.test.ts | 9 +++++- 5 files changed, 133 insertions(+), 25 deletions(-) diff --git a/lib/Logger.ts b/lib/Logger.ts index 854228b..627acf8 100644 --- a/lib/Logger.ts +++ b/lib/Logger.ts @@ -25,13 +25,14 @@ const DEFAULT_TIMER_GC_INTERVAL = 60000; * Logger module class. */ export class Logger implements LoggerMethods { - static globalAttributes: Record; + static globalAttributes?: LogContext['attributes']; /** - * Global attributes to show in every log - * @param data object containing attributes, needs to be k = string, v = string + * Sets global attributes to be included in every log output. + * + * @param data Attributes shared by all logger instances. */ - static setGlobalAttributes(data: Record): void { + static setGlobalAttributes(data: LogContext['attributes']): void { Logger.globalAttributes = data; } @@ -83,8 +84,8 @@ export class Logger implements LoggerMethods { * @param opts.client Underlying abstract logger to override console. * @param opts.noServer Disable the embedded http server for runtime actions. * @param opts.attributes Attributes to add extra fields with fixed value. - * @param opts.allowExit Allow process to exit naturally (uses server.unref()). - * @param opts.timerTTL TTL for timers in ms (default: 10min). Set to 0 to disable cleanup. + * @param opts.allowExit Allow process to exit naturally (uses server.unref()). + * @param opts.timerTTL TTL for timers in ms (default: 10min). Set to 0 to disable cleanup. */ public constructor( opts: { @@ -489,24 +490,21 @@ export class Logger implements LoggerMethods { /** * Factory function to create tagged Logger instance. * - * @param tag Tag with which the logger is being created. - * @param opts Optional attributes to add context to logger - * @param opts.client Underlying abstract logger to override console. - * @param opts.noServer Disable the embedded http server for runtime actions. - * @param opts.attributes Adds fields with static value for every log - * @param tag Tag with which the logger is being created. - * @param opts Optional attributes to add context to logger - * @param opts.client Underlying abstract logger to override console. - * @param opts.noServer Disable the embedded http server for runtime actions. - * @param opts.attributes Adds fields with static value for every log - * @returns Logger instance + * @param tag Tag with which the logger is being created. + * @param opts Optional logger configuration. + * @param opts.client Underlying abstract logger to override console. + * @param opts.noServer Disable the embedded http server for runtime actions. + * @param opts.attributes Adds fixed fields to every log emitted by the instance. + * @param opts.allowExit Allow process to exit naturally (uses server.unref()). + * @param opts.timerTTL TTL for timers in ms (default: 10min). Set to 0 to disable cleanup. + * @returns Logger instance. */ export function createLogger( tag?: string, opts: { client?: AbstractLogger; noServer?: boolean; - attributes?: Record; + attributes?: LogContext['attributes']; allowExit?: boolean; timerTTL?: number; } = {} diff --git a/lib/format/index.ts b/lib/format/index.ts index 8502863..3ea15c5 100644 --- a/lib/format/index.ts +++ b/lib/format/index.ts @@ -24,7 +24,7 @@ export function getLogWrapper( ): LogWrapper { switch (output) { case 'text': - return text.call(this, logger, tag); + return text.call(this, logger, tag, attributes); case 'json': return json.call(this, logger, tag, attributes); diff --git a/lib/format/text.ts b/lib/format/text.ts index 10a0016..9753d3a 100644 --- a/lib/format/text.ts +++ b/lib/format/text.ts @@ -1,27 +1,81 @@ import { Logger } from '../Logger'; import { LogWrapper } from '../util/type/LogWrapper'; import { AbstractLogger } from '../util/type/AbstractLogger'; -import { colorized, datetime, stringify } from '../util/Helpers'; +import { + colorized, + datetime, + getCircularReplacer, + normalize, + stringify +} from '../util/Helpers'; import { LevelTag } from '../util/enum/LevelTags'; +import { LogContext } from '../util/interface/LogContext'; + +/** + * Converts an attribute value to its string representation for text output. + * + * @param value The attribute value to format. + * @returns The formatted string representation. + */ +function formatTextAttributeValue(value: unknown): string { + if (typeof value === 'string') return value; + if (typeof value === 'undefined') return 'undefined'; + + const normalized = normalize(value); + + if (typeof normalized === 'string') return normalized; + + try { + return JSON.stringify(normalized, getCircularReplacer()); + } catch { + return '[Unserializable]'; + } +} + +/** + * Merges global and instance attributes into a key=value text string. + * + * @param attributes Instance-level attributes to merge with global ones. + * @returns Formatted string of key=value pairs, or empty string if none. + */ +function formatTextAttributes(attributes?: LogContext['attributes']): string { + const mergedAttributes = { + ...(Logger.globalAttributes ?? {}), + ...(attributes ?? {}) + }; + const entries = Object.entries(mergedAttributes); + + if (entries.length === 0) return ''; + + return entries + .map(([key, value]) => `${key}=${formatTextAttributeValue(value)}`) + .join(' '); +} /** * Formatting method for text output. * - * @param logger The underlying logging client. - * @param tag Tag to mark logged output. + * @param logger The underlying logging client. + * @param tag Tag to mark logged output. + * @param attributes Instance attributes included in the text output. * @returns The logging method. */ export function text( this: TScope, logger: AbstractLogger, - tag?: string + tag?: string, + attributes?: LogContext['attributes'] ): LogWrapper { const now = datetime(); const paint = colorized(); return (level: LevelTag, ...args: unknown[]) => { const data = args.map((arg) => stringify(arg)).join(' '); + const labels = formatTextAttributes(attributes); + const message = [`${now()}[${level}]`, tag ?? '', labels, data] + .filter(Boolean) + .join(' '); - logger.log(paint[level](`${now()}[${level}] ${tag ?? ''} ${data}`)); + logger.log(paint[level](message)); }; } diff --git a/tests/format.test.ts b/tests/format.test.ts index d4257a4..341cab1 100644 --- a/tests/format.test.ts +++ b/tests/format.test.ts @@ -19,6 +19,7 @@ describe('JSON Formatter', () => { afterEach(() => { delete process.env.OZLOGGER_OUTPUT; delete process.env.OZLOGGER_LEVEL; + Logger.globalAttributes = undefined; }); test('should output valid JSON', () => { @@ -107,6 +108,33 @@ describe('JSON Formatter', () => { expect(output.body['1']).toBe(false); }); + test('should merge global and instance attributes in JSON output', () => { + Logger.setGlobalAttributes({ + tenant: 'ozmap', + shared: 'global', + featureFlag: true + }); + + logger = createLogger('JSON-ATTR-TEST', { + client: mockLogger, + noServer: true, + attributes: { + job: 'history', + shared: 'local', + metadata: { scope: 'instance' } + } + }); + + logger.audit('payload'); + + const output = JSON.parse(logged[0]); + expect(output.tenant).toBe('ozmap'); + expect(output.job).toBe('history'); + expect(output.featureFlag).toBe(true); + expect(output.shared).toBe('local'); + expect(output.metadata).toEqual({ scope: 'instance' }); + }); + describe('Log levels', () => { test('debug level', () => { logger.debug('debug msg'); @@ -163,6 +191,7 @@ describe('Text Formatter', () => { afterEach(() => { delete process.env.OZLOGGER_OUTPUT; delete process.env.OZLOGGER_LEVEL; + Logger.globalAttributes = undefined; }); test('should output text format', () => { @@ -188,6 +217,22 @@ describe('Text Formatter', () => { logger.info('one', 'two', 'three'); expect(logged[0]).toContain('one two three'); }); + + test('should include global and instance attributes in text output', () => { + Logger.setGlobalAttributes({ tenant: 'ozmap', shared: 'global' }); + logger = createLogger('TEXT-ATTR-TEST', { + client: mockLogger, + noServer: true, + attributes: { job: 'history', shared: 'local', enabled: true } + }); + + logger.info('test message'); + + expect(logged[0]).toContain('tenant=ozmap'); + expect(logged[0]).toContain('job=history'); + expect(logged[0]).toContain('shared=local'); + expect(logged[0]).toContain('enabled=true'); + }); }); describe('Text Formatter with datetime', () => { @@ -210,6 +255,7 @@ describe('Text Formatter with datetime', () => { delete process.env.OZLOGGER_OUTPUT; delete process.env.OZLOGGER_LEVEL; delete process.env.OZLOGGER_DATETIME; + Logger.globalAttributes = undefined; }); test('should include timestamp in text output', () => { @@ -239,6 +285,7 @@ describe('JSON Formatter with datetime', () => { delete process.env.OZLOGGER_OUTPUT; delete process.env.OZLOGGER_LEVEL; delete process.env.OZLOGGER_DATETIME; + Logger.globalAttributes = undefined; }); test('should include timestamp in JSON output', () => { @@ -304,6 +351,7 @@ describe('Format fallback behavior', () => { delete process.env.OZLOGGER_OUTPUT; delete process.env.OZLOGGER_LEVEL; + Logger.globalAttributes = undefined; }); }); @@ -336,5 +384,6 @@ describe('JSON Formatter error handling', () => { delete process.env.OZLOGGER_OUTPUT; delete process.env.OZLOGGER_LEVEL; + Logger.globalAttributes = undefined; }); }); diff --git a/tests/logger-core.test.ts b/tests/logger-core.test.ts index 2b3e85e..f041494 100644 --- a/tests/logger-core.test.ts +++ b/tests/logger-core.test.ts @@ -37,9 +37,16 @@ describe('Logger Core', () => { expect(logged[0]).toContain('ms'); }); - test('should throw when timer ID already exists', () => { + test('should warn and overwrite when timer ID already exists', () => { logger.time('duplicate'); expect(() => logger.time('duplicate')).not.toThrow(); + + expect(logged).toHaveLength(1); + const output = JSON.parse(logged[0]); + expect(output.severityText).toBe('WARNING'); + expect(output.body['0']).toContain( + 'Identifier duplicate is already in use' + ); }); test('should throw when timeEnd called with unknown ID', () => { From b42d302db27689a38f8f0f0e2bb480f8fe4a23ce Mon Sep 17 00:00:00 2001 From: Jose Raupp Date: Sat, 14 Mar 2026 20:58:17 -0300 Subject: [PATCH 22/22] OZLOGGER fix: #time 1m address remaining PR comments - init globalAttributes, restore data field, fix lockfile, improve error fallback --- lib/Logger.ts | 7 ++++--- lib/format/json.ts | 12 +++++++++--- lib/format/text.ts | 4 ++-- package-lock.json | 4 ++-- tests/format.test.ts | 19 +++++++++++-------- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/lib/Logger.ts b/lib/Logger.ts index 627acf8..f9d862e 100644 --- a/lib/Logger.ts +++ b/lib/Logger.ts @@ -25,10 +25,11 @@ const DEFAULT_TIMER_GC_INTERVAL = 60000; * Logger module class. */ export class Logger implements LoggerMethods { - static globalAttributes?: LogContext['attributes']; + static globalAttributes: LogContext['attributes'] = {}; /** * Sets global attributes to be included in every log output. + * Subsequent calls replace any previously set global attributes rather than merging them. * * @param data Attributes shared by all logger instances. */ @@ -84,8 +85,8 @@ export class Logger implements LoggerMethods { * @param opts.client Underlying abstract logger to override console. * @param opts.noServer Disable the embedded http server for runtime actions. * @param opts.attributes Attributes to add extra fields with fixed value. - * @param opts.allowExit Allow process to exit naturally (uses server.unref()). - * @param opts.timerTTL TTL for timers in ms (default: 10min). Set to 0 to disable cleanup. + * @param opts.allowExit Allow process to exit naturally (uses server.unref()). + * @param opts.timerTTL TTL for timers in ms (default: 10min). Set to 0 to disable cleanup. */ public constructor( opts: { diff --git a/lib/format/json.ts b/lib/format/json.ts index 7724243..cfbdcb2 100644 --- a/lib/format/json.ts +++ b/lib/format/json.ts @@ -43,6 +43,7 @@ function toStructuredJsonLog( ...(Logger.globalAttributes ?? {}), ...(attributes ?? {}), tag, + data /** @deprecated Use 'body' instead. */, level /** @deprecated Use 'severityText' instead. */, severityText: level, severityNumber: LogLevels[logLevelKey], @@ -52,9 +53,14 @@ function toStructuredJsonLog( try { return JSON.stringify(structuredData, getCircularReplacer()); } catch (e) { - structuredData.body = - '[OZLogger internal] - Unable to serialize log data'; - return JSON.stringify(structuredData, getCircularReplacer()); + const fallback = { + ...timestamp(), + tag, + severityText: level, + severityNumber: LogLevels[logLevelKey], + body: '[OZLogger internal] - Unable to serialize log data' + }; + return JSON.stringify(fallback); } }, push(value: unknown) { diff --git a/lib/format/text.ts b/lib/format/text.ts index 9753d3a..31f1534 100644 --- a/lib/format/text.ts +++ b/lib/format/text.ts @@ -40,8 +40,8 @@ function formatTextAttributeValue(value: unknown): string { */ function formatTextAttributes(attributes?: LogContext['attributes']): string { const mergedAttributes = { - ...(Logger.globalAttributes ?? {}), - ...(attributes ?? {}) + ...Logger.globalAttributes, + ...attributes }; const entries = Object.entries(mergedAttributes); diff --git a/package-lock.json b/package-lock.json index 22b07f5..30936a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ozmap/logger", - "version": "0.3.0-beta.0", + "version": "0.3.0-beta.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@ozmap/logger", - "version": "0.3.0-beta.0", + "version": "0.3.0-beta.5", "license": "MIT", "dependencies": { "@opentelemetry/api": "^1.9.0" diff --git a/tests/format.test.ts b/tests/format.test.ts index 341cab1..2489896 100644 --- a/tests/format.test.ts +++ b/tests/format.test.ts @@ -19,7 +19,7 @@ describe('JSON Formatter', () => { afterEach(() => { delete process.env.OZLOGGER_OUTPUT; delete process.env.OZLOGGER_LEVEL; - Logger.globalAttributes = undefined; + Logger.globalAttributes = {}; }); test('should output valid JSON', () => { @@ -191,7 +191,7 @@ describe('Text Formatter', () => { afterEach(() => { delete process.env.OZLOGGER_OUTPUT; delete process.env.OZLOGGER_LEVEL; - Logger.globalAttributes = undefined; + Logger.globalAttributes = {}; }); test('should output text format', () => { @@ -255,7 +255,7 @@ describe('Text Formatter with datetime', () => { delete process.env.OZLOGGER_OUTPUT; delete process.env.OZLOGGER_LEVEL; delete process.env.OZLOGGER_DATETIME; - Logger.globalAttributes = undefined; + Logger.globalAttributes = {}; }); test('should include timestamp in text output', () => { @@ -285,7 +285,7 @@ describe('JSON Formatter with datetime', () => { delete process.env.OZLOGGER_OUTPUT; delete process.env.OZLOGGER_LEVEL; delete process.env.OZLOGGER_DATETIME; - Logger.globalAttributes = undefined; + Logger.globalAttributes = {}; }); test('should include timestamp in JSON output', () => { @@ -351,7 +351,7 @@ describe('Format fallback behavior', () => { delete process.env.OZLOGGER_OUTPUT; delete process.env.OZLOGGER_LEVEL; - Logger.globalAttributes = undefined; + Logger.globalAttributes = {}; }); }); @@ -376,14 +376,17 @@ describe('JSON Formatter error handling', () => { logger.info(problematicObj); - // Should still log something (fallback message replaces body entirely) + // Should still log something (minimal safe fallback payload) expect(logged.length).toBe(1); const output = JSON.parse(logged[0]); - // When stringify fails, body is replaced with the error message string + // Fallback builds a minimal guaranteed-serializable payload expect(output.body).toContain('Unable to serialize'); + // Fallback should not include unserializable attributes/context + expect(output.severityText).toBe('INFO'); + expect(output.tag).toBe('STRINGIFY-FAIL'); delete process.env.OZLOGGER_OUTPUT; delete process.env.OZLOGGER_LEVEL; - Logger.globalAttributes = undefined; + Logger.globalAttributes = {}; }); });