Skip to content

Commit

Permalink
feat(node): pluggable logging class (support different strategies)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndriiAndreiev committed Aug 12, 2024
1 parent 714f447 commit 2664a8c
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 45 deletions.
55 changes: 55 additions & 0 deletions packages/node/src/lib/console-logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { Log, ErrorLog, LoggerStrategy } from './logger';

/**
* Implementation of the console logger strategy.
*/
export default class ConsoleLogger implements LoggerStrategy {
/**
* This method takes a level of the log and a message and formats it to the unified log format
* @returns A formatted log message
*/
// eslint-disable-next-line class-methods-use-this
private formatMessage(level: 'DEBUG' | 'ERROR' | 'TRACE', message: string): string[] {
return [`${level} ${new Date().toISOString()} [readmeio] ${message}`];
}

/**
* Logs a trace message to the console.
* @param log The trace log entry. Contains the message as required field and optional args.
*/
trace({ message, args }: Log): void {
const params: unknown[] = this.formatMessage('TRACE', message);
console.trace(...params);
if (args) {
console.dir(args, { depth: null });
console.log('\n');
}
}

/**
* Logs a debug message to the console.
* @param log The debug log entry. Contains the message as required field and optional args.
*/
debug({ message, args }: Log): void {
const params: unknown[] = this.formatMessage('DEBUG', message);
if (args) {
params.push('\nArguments:', args);
}
console.debug(...params, '\n');
}

/**
* Logs an error message to the console.
* @param log The error log entry. Contains the message and error object as required fields and optional args.
*/
error({ message, args, err }: ErrorLog): void {
const params: unknown[] = this.formatMessage('ERROR', message);
if (args) {
params.push('\nArguments:', args);
}
if (err) {
params.push('\n', err);
}
console.error(...params, '\n');
}
}
5 changes: 3 additions & 2 deletions packages/node/src/lib/construct-payload.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { LoggerStrategy } from './logger';
import type { OutgoingLogBody } from './metrics-log';
import type { UUID } from 'node:crypto';
import type { IncomingMessage, ServerResponse } from 'node:http';
Expand Down Expand Up @@ -55,9 +56,9 @@ export interface LogOptions {
fireAndForget?: boolean;

/**
* If true, the errors and other logs will be displayed in console.
* If true, the errors and other logs will be displayed in console. Your own logger strategy can be passed.
*/
logger?: boolean;
logger?: LoggerStrategy | boolean;

/**
* @deprecated use `allowList` instead
Expand Down
5 changes: 4 additions & 1 deletion packages/node/src/lib/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@ export function log(
) {
if (!readmeApiKey) throw new Error('You must provide your ReadMe API key');
if (!group) throw new Error('You must provide a group');
if (options.logger) logger.configure({ isLoggingEnabled: true });
if (options.logger) {
if (typeof options.logger === 'boolean') logger.configure({ isLoggingEnabled: true });
else logger.configure({ isLoggingEnabled: true, strategy: options.logger });
}

// Ensures the buffer length is between 1 and 30
const bufferLength = clamp(options.bufferLength || config.bufferLength, 1, 30);
Expand Down
64 changes: 22 additions & 42 deletions packages/node/src/lib/logger.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
import ConsoleLogger from './console-logger';

interface LoggerConfig {
isLoggingEnabled: boolean;
logPrefix: string;
strategy: LoggerStrategy;
}

interface Log {
export interface Log {
args?: Record<string, unknown>;
message: string;
}

type ErrorLog = Log & { err?: Error };
export type ErrorLog = Log & { err?: Error };

export interface Logger {
configure(config: Partial<LoggerConfig>): void;
export interface LoggerStrategy {
debug(log: Log): void;
error(log: ErrorLog): void;
trace(log: Log): void;
}

export interface Logger extends LoggerStrategy {
configure(config: Partial<LoggerConfig>): void;
}

/**
* Default implementation of the Logger interface. Represents a signleton class of console logger.
* Default implementation of the Logger interface. Represents a signleton class of logger with selected strategy.
*/
class DefaultLogger implements Logger {
private static instance: Logger;
Expand All @@ -37,7 +42,7 @@ class DefaultLogger implements Logger {
if (!DefaultLogger.instance) {
const defaultConfig: LoggerConfig = {
isLoggingEnabled: false,
logPrefix: '[readmeio]',
strategy: new ConsoleLogger(),
};
DefaultLogger.instance = new DefaultLogger(defaultConfig);
}
Expand All @@ -53,55 +58,30 @@ class DefaultLogger implements Logger {
}

/**
* This method takes a level of the log and a message and formats it to the unified log format
* @returns A formatted log message
*/

private formatMessage(level: 'DEBUG' | 'ERROR' | 'TRACE', message: string): string[] {
return [`${level} ${new Date().toISOString()} ${this.config.logPrefix} ${message}`];
}

/**
* Logs a trace message.
* @param log The trace log entry. Contains the message as required field and optional args.
* Logs an error message.
* @param log The error log entry. Contains the message and error object as required fields and optional args.
*/
trace({ message, args }: Log): void {
error(log: ErrorLog): void {
if (!this.config.isLoggingEnabled) return;
const params: unknown[] = this.formatMessage('TRACE', message);
console.log(...params);
if (args) {
console.dir(args, { depth: null });
console.log('\n');
}
this.config.strategy.error(log);
}

/**
* Logs a debug message.
* @param log The debug log entry. Contains the message as required field and optional args.
*/
debug({ message, args }: Log): void {
debug(log: Log): void {
if (!this.config.isLoggingEnabled) return;
const params: unknown[] = this.formatMessage('DEBUG', message);
if (args) {
params.push('\nArguments:', args);
}
console.debug(...params, '\n');
this.config.strategy.debug(log);
}

/**
* Logs an error message.
* @param log The error log entry. Contains the message and error object as required fields and optional args.
* Logs a trace message.
* @param log The trace log entry. Contains the message as required field and optional args.
*/
error({ message, args, err }: ErrorLog): void {
trace(log: Log): void {
if (!this.config.isLoggingEnabled) return;
const params: unknown[] = this.formatMessage('ERROR', message);
if (args) {
params.push('\nArguments:', args);
}
if (err) {
params.push('\n', err);
}
console.error(...params, '\n');
this.config.strategy.trace(log);
}
}

Expand Down

0 comments on commit 2664a8c

Please sign in to comment.