Skip to content

Commit 2664a8c

Browse files
feat(node): pluggable logging class (support different strategies)
1 parent 714f447 commit 2664a8c

File tree

4 files changed

+84
-45
lines changed

4 files changed

+84
-45
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import type { Log, ErrorLog, LoggerStrategy } from './logger';
2+
3+
/**
4+
* Implementation of the console logger strategy.
5+
*/
6+
export default class ConsoleLogger implements LoggerStrategy {
7+
/**
8+
* This method takes a level of the log and a message and formats it to the unified log format
9+
* @returns A formatted log message
10+
*/
11+
// eslint-disable-next-line class-methods-use-this
12+
private formatMessage(level: 'DEBUG' | 'ERROR' | 'TRACE', message: string): string[] {
13+
return [`${level} ${new Date().toISOString()} [readmeio] ${message}`];
14+
}
15+
16+
/**
17+
* Logs a trace message to the console.
18+
* @param log The trace log entry. Contains the message as required field and optional args.
19+
*/
20+
trace({ message, args }: Log): void {
21+
const params: unknown[] = this.formatMessage('TRACE', message);
22+
console.trace(...params);
23+
if (args) {
24+
console.dir(args, { depth: null });
25+
console.log('\n');
26+
}
27+
}
28+
29+
/**
30+
* Logs a debug message to the console.
31+
* @param log The debug log entry. Contains the message as required field and optional args.
32+
*/
33+
debug({ message, args }: Log): void {
34+
const params: unknown[] = this.formatMessage('DEBUG', message);
35+
if (args) {
36+
params.push('\nArguments:', args);
37+
}
38+
console.debug(...params, '\n');
39+
}
40+
41+
/**
42+
* Logs an error message to the console.
43+
* @param log The error log entry. Contains the message and error object as required fields and optional args.
44+
*/
45+
error({ message, args, err }: ErrorLog): void {
46+
const params: unknown[] = this.formatMessage('ERROR', message);
47+
if (args) {
48+
params.push('\nArguments:', args);
49+
}
50+
if (err) {
51+
params.push('\n', err);
52+
}
53+
console.error(...params, '\n');
54+
}
55+
}

packages/node/src/lib/construct-payload.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { LoggerStrategy } from './logger';
12
import type { OutgoingLogBody } from './metrics-log';
23
import type { UUID } from 'node:crypto';
34
import type { IncomingMessage, ServerResponse } from 'node:http';
@@ -55,9 +56,9 @@ export interface LogOptions {
5556
fireAndForget?: boolean;
5657

5758
/**
58-
* If true, the errors and other logs will be displayed in console.
59+
* If true, the errors and other logs will be displayed in console. Your own logger strategy can be passed.
5960
*/
60-
logger?: boolean;
61+
logger?: LoggerStrategy | boolean;
6162

6263
/**
6364
* @deprecated use `allowList` instead

packages/node/src/lib/log.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,10 @@ export function log(
107107
) {
108108
if (!readmeApiKey) throw new Error('You must provide your ReadMe API key');
109109
if (!group) throw new Error('You must provide a group');
110-
if (options.logger) logger.configure({ isLoggingEnabled: true });
110+
if (options.logger) {
111+
if (typeof options.logger === 'boolean') logger.configure({ isLoggingEnabled: true });
112+
else logger.configure({ isLoggingEnabled: true, strategy: options.logger });
113+
}
111114

112115
// Ensures the buffer length is between 1 and 30
113116
const bufferLength = clamp(options.bufferLength || config.bufferLength, 1, 30);

packages/node/src/lib/logger.ts

Lines changed: 22 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
1+
import ConsoleLogger from './console-logger';
2+
13
interface LoggerConfig {
24
isLoggingEnabled: boolean;
3-
logPrefix: string;
5+
strategy: LoggerStrategy;
46
}
57

6-
interface Log {
8+
export interface Log {
79
args?: Record<string, unknown>;
810
message: string;
911
}
1012

11-
type ErrorLog = Log & { err?: Error };
13+
export type ErrorLog = Log & { err?: Error };
1214

13-
export interface Logger {
14-
configure(config: Partial<LoggerConfig>): void;
15+
export interface LoggerStrategy {
1516
debug(log: Log): void;
1617
error(log: ErrorLog): void;
1718
trace(log: Log): void;
1819
}
1920

21+
export interface Logger extends LoggerStrategy {
22+
configure(config: Partial<LoggerConfig>): void;
23+
}
24+
2025
/**
21-
* Default implementation of the Logger interface. Represents a signleton class of console logger.
26+
* Default implementation of the Logger interface. Represents a signleton class of logger with selected strategy.
2227
*/
2328
class DefaultLogger implements Logger {
2429
private static instance: Logger;
@@ -37,7 +42,7 @@ class DefaultLogger implements Logger {
3742
if (!DefaultLogger.instance) {
3843
const defaultConfig: LoggerConfig = {
3944
isLoggingEnabled: false,
40-
logPrefix: '[readmeio]',
45+
strategy: new ConsoleLogger(),
4146
};
4247
DefaultLogger.instance = new DefaultLogger(defaultConfig);
4348
}
@@ -53,55 +58,30 @@ class DefaultLogger implements Logger {
5358
}
5459

5560
/**
56-
* This method takes a level of the log and a message and formats it to the unified log format
57-
* @returns A formatted log message
58-
*/
59-
60-
private formatMessage(level: 'DEBUG' | 'ERROR' | 'TRACE', message: string): string[] {
61-
return [`${level} ${new Date().toISOString()} ${this.config.logPrefix} ${message}`];
62-
}
63-
64-
/**
65-
* Logs a trace message.
66-
* @param log The trace log entry. Contains the message as required field and optional args.
61+
* Logs an error message.
62+
* @param log The error log entry. Contains the message and error object as required fields and optional args.
6763
*/
68-
trace({ message, args }: Log): void {
64+
error(log: ErrorLog): void {
6965
if (!this.config.isLoggingEnabled) return;
70-
const params: unknown[] = this.formatMessage('TRACE', message);
71-
console.log(...params);
72-
if (args) {
73-
console.dir(args, { depth: null });
74-
console.log('\n');
75-
}
66+
this.config.strategy.error(log);
7667
}
7768

7869
/**
7970
* Logs a debug message.
8071
* @param log The debug log entry. Contains the message as required field and optional args.
8172
*/
82-
debug({ message, args }: Log): void {
73+
debug(log: Log): void {
8374
if (!this.config.isLoggingEnabled) return;
84-
const params: unknown[] = this.formatMessage('DEBUG', message);
85-
if (args) {
86-
params.push('\nArguments:', args);
87-
}
88-
console.debug(...params, '\n');
75+
this.config.strategy.debug(log);
8976
}
9077

9178
/**
92-
* Logs an error message.
93-
* @param log The error log entry. Contains the message and error object as required fields and optional args.
79+
* Logs a trace message.
80+
* @param log The trace log entry. Contains the message as required field and optional args.
9481
*/
95-
error({ message, args, err }: ErrorLog): void {
82+
trace(log: Log): void {
9683
if (!this.config.isLoggingEnabled) return;
97-
const params: unknown[] = this.formatMessage('ERROR', message);
98-
if (args) {
99-
params.push('\nArguments:', args);
100-
}
101-
if (err) {
102-
params.push('\n', err);
103-
}
104-
console.error(...params, '\n');
84+
this.config.strategy.trace(log);
10585
}
10686
}
10787

0 commit comments

Comments
 (0)