Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5fb32f3
feat: removed data from logs
Nestor-Brasileiro Mar 5, 2026
2d14634
OZLOGGER feat: #time 5m added extra fields to log
Nestor-Brasileiro Mar 10, 2026
b23f45c
OZLOGGER feat: #time 5h added properties to audit log
Nestor-Brasileiro Mar 10, 2026
a629d9d
OZLOGGER feat: #time 5m added version with beta flag
Nestor-Brasileiro Mar 10, 2026
2433a28
Merge branch 'develop' into feature/auditoria-victorialogs
raupp Mar 11, 2026
547e80a
OZLOGGER fix: #time 1m Update lib/format/json.ts
Nestor-Brasileiro Mar 12, 2026
d910da9
OZLOGGER fix: #time 5m Update lib/Logger.ts
Nestor-Brasileiro Mar 12, 2026
fce2702
OZLOGGER fix: 5m Update lib/Logger.ts
Nestor-Brasileiro Mar 12, 2026
180a35c
OZLOGGER fix: #time 1m bump version to 0.3 beta
leandroschabarum Mar 12, 2026
1ad422b
OZLOGGER fix: #time 15m merge errors
Nestor-Brasileiro Mar 12, 2026
4947d1a
OZLOGGER ci: #time 15m added publish beta version
Nestor-Brasileiro Mar 12, 2026
8d66ba9
OZLOGGER chore: #time 1m new tag
Nestor-Brasileiro Mar 12, 2026
7ea1559
OZLOGGER ci: #time 5m adjusted publish beta flow
Nestor-Brasileiro Mar 12, 2026
7d23f59
OZLOGGER chore: #time 1m new version
Nestor-Brasileiro Mar 12, 2026
a360aa6
OZLOGGER ci: #time 1m /workflows/publish-beta-version
Nestor-Brasileiro Mar 12, 2026
56a1e8e
OZLOGGER ci: #time 1m /workflows/publish-module
Nestor-Brasileiro Mar 12, 2026
4fd2ed7
OZLOGGER chore: #time 1m new version
Nestor-Brasileiro Mar 12, 2026
2a78630
Update NPM token in publish-beta-version.yml
Nestor-Brasileiro Mar 13, 2026
b5caabc
Update NPM token in publish-module workflow
Nestor-Brasileiro Mar 13, 2026
27ab3a7
OZLOGGER chore: #time 1m new version
Nestor-Brasileiro Mar 13, 2026
3e1a5f0
OZLOGGER fix: #time 1m add public flag
leandroschabarum Mar 13, 2026
4da30e3
OZLOGGER fix: #time 1m address PR review comments - align types, fix …
Mar 14, 2026
b42d302
OZLOGGER fix: #time 1m address remaining PR comments - init globalAtt…
Mar 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/publish-beta-version.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Publish Beta Version

on:
push:
tags:
- '*.*.*'

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
Comment on lines +13 to +17
with:
registry-url: 'https://registry.npmjs.org'
node-version: 22
Comment on lines +16 to +20
Comment on lines +13 to +20
- name: npm install
run: npm install
Comment on lines +13 to +22
- name: tsc
run: npm run build
- name: publish
uses: JS-DevTools/npm-publish@v1
with:
token: ${{ secrets.NPM_TOKEN }}
access: public
tag: 'beta'
27 changes: 27 additions & 0 deletions .github/workflows/publish-module.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
on:
push:
branches:
- main
Comment on lines +3 to +4

jobs:
Comment on lines +1 to +6
build:
name: tsc
runs-on: ubuntu-latest
Comment on lines +1 to +9
steps:
- uses: actions/checkout@v2
- name: 'cat package.json'
run: cat ./package.json
- name: install node v22
uses: actions/setup-node@v2
Comment on lines +11 to +15
with:
registry-url: 'https://registry.npmjs.org'
node-version: 22
Comment on lines +14 to +18
Comment on lines +11 to +18
- name: npm install
run: npm install
Comment on lines +11 to +20
- name: tsc
run: npm run build
- name: publish
uses: JS-DevTools/npm-publish@v1
with:
token: ${{ secrets.NPM_TOKEN }}
access: public
Comment on lines +1 to +27
44 changes: 31 additions & 13 deletions lib/Logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ const DEFAULT_TIMER_GC_INTERVAL = 60000;
* Logger module class.
*/
export class Logger implements LoggerMethods {
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.
*/
static setGlobalAttributes(data: LogContext['attributes']): void {
Logger.globalAttributes = data;
}

/**
* Temporary storage for timers.
*/
Expand Down Expand Up @@ -68,18 +80,20 @@ 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.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 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.
*/
public constructor(
opts: {
tag?: string;
client?: AbstractLogger;
noServer?: boolean;
attributes?: LogContext['attributes'];
allowExit?: boolean;
timerTTL?: number;
} = {}
Expand All @@ -88,7 +102,8 @@ export class Logger implements LoggerMethods {
this,
output(),
opts.client ?? console,
opts.tag
opts.tag,
opts.attributes
);
this.configure(level());

Expand Down Expand Up @@ -476,18 +491,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.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 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?: LogContext['attributes'];
allowExit?: boolean;
timerTTL?: number;
} = {}
Expand Down
15 changes: 9 additions & 6 deletions lib/format/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,30 @@ 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.
*
* @param output The output format (text, json, ...).
* @param logger The underlying logging client.
* @param tag Tag to mark logged output.
* @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<TScope extends Logger>(
this: TScope,
output: (typeof Outputs)[number],
logger: AbstractLogger,
tag?: string
tag?: string,
attributes?: LogContext['attributes']
): 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);
return json.call(this, logger, tag, attributes);

Comment on lines 18 to 31
default:
throw new Error(`Log output '${output}' is not supported.`);
Expand Down
60 changes: 39 additions & 21 deletions lib/format/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,53 +9,63 @@ import {
getCircularReplacer,
normalize
} from '../util/Helpers';
import { LogContext } from '../util/interface/LogContext';

/**
* Creates a structured JSON log object.
*
* @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 attributes Optional labels to show in every instance log.
* @returns The structured log object with `toString` and `push` methods.
*/
function toStructuredJsonLog<TScope extends Logger>(
this: TScope,
level: LevelTag,
timestamp: () => { timestamp?: string },
tag?: string
tag?: string,
attributes?: LogContext['attributes']
) {
const data: Record<number, unknown> = {};
const logLevelKey =
level === 'WARNING'
? ('warn' as const)
: (level.toLowerCase() as keyof typeof LogLevels);

const structuredData = {
...timestamp(),
...this.getContext(),
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,
Comment on lines +40 to +45
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

attributes are spread into structuredData without normalization. If a consumer passes a non-JSON-serializable value (e.g. BigInt/symbol) in attributes, JSON.stringify will throw; the catch block only replaces body and then stringifies again, which will still throw for the same bad attribute. Consider normalizing/sanitizing attributes (and/or Logger.globalAttributes) before spreading, or in the catch block fall back to a minimal guaranteed-serializable payload (e.g. omit attributes/context when serialization fails).

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +45
Comment on lines +40 to +45
data /** @deprecated Use 'body' instead. */,
level /** @deprecated Use 'severityText' instead. */,
severityText: level,
severityNumber: LogLevels[logLevelKey],
body: data
Comment on lines +45 to +50
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change removes the previously emitted data field (which was marked deprecated). Even if deprecated, removing it is a breaking change for any consumers still reading it. Consider keeping data as an alias of body for one more release (or explicitly document the breaking change / bump major version).

Copilot uses AI. Check for mistakes.
};
Comment on lines +40 to +51
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New behavior adds per-instance attributes and global attributes into JSON output, but the existing JSON formatter tests don’t assert these fields. Add tests covering: (1) instance attributes passed to createLogger appear on the root of the JSON payload, and (2) Logger.setGlobalAttributes() adds fields without breaking logs when unset.

Copilot uses AI. Check for mistakes.

try {
return JSON.stringify(structuredData, getCircularReplacer());
} catch (e) {
structuredData.data = 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);
}
Comment on lines 55 to 64
},
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;
}
};
Expand All @@ -64,20 +74,28 @@ function toStructuredJsonLog<TScope extends Logger>(
/**
* Formatting method for JSON 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 optional labels to show in every log from instance.
* @returns The logging method.
*/
export function json<TScope extends Logger>(
this: TScope,
logger: AbstractLogger,
tag?: string
tag?: string,
attributes?: LogContext['attributes']
): LogWrapper {
const now = datetime<{ timestamp?: string }>();
const paint = colorized();

return (level: LevelTag, ...args: unknown[]) => {
const payload = toStructuredJsonLog.call(this, level, now, tag);
const payload = toStructuredJsonLog.call(
this,
level,
now,
tag,
attributes
);

for (const arg of args) {
payload.push(normalize(arg));
Expand Down
64 changes: 59 additions & 5 deletions lib/format/text.ts
Original file line number Diff line number Diff line change
@@ -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<TScope extends Logger>(
this: TScope,
logger: AbstractLogger,
tag?: string
tag?: string,
attributes?: LogContext['attributes']
): LogWrapper {
const now = datetime<string>();
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));
Comment on lines 72 to +79
};
}
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading