@ricdotnet/logger 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,284 +1,287 @@
1
- # @ricdotnet/logger
2
-
3
- A lightweight, flexible logging library for TypeScript/JavaScript projects with support for custom formatters and transports.
4
-
5
- ## Features
6
-
7
- - 🎯 **Simple API** - Easy to use with sensible defaults
8
- - 📦 **Modular** - Swap formatters and transports to suit your needs
9
- - 🎨 **Multiple Formatters** - JSON and Pretty formatters included
10
- - 📤 **Extensible Transports** - Console transport included, create your own
11
- - 🌳 **Contextual Logging** - Add context that propagates through child loggers
12
- - 📝 **Full TypeScript Support** - Built with TypeScript for type safety
13
-
14
- ## Installation
15
-
16
- ```bash
17
- npm install @ricdotnet/logger
18
- ```
19
-
20
- ## Quick Start
21
-
22
- ```typescript
23
- import { Logger } from '@ricdotnet/logger';
24
-
25
- const logger = new Logger();
26
-
27
- logger.info('Application started');
28
- logger.error('Something went wrong!');
29
- ```
30
-
31
- ## Usage
32
-
33
- ### Basic Logging
34
-
35
- The logger provides two main methods for logging:
36
-
37
- ```typescript
38
- import { Logger } from '@ricdotnet/logger';
39
-
40
- const logger = new Logger();
41
-
42
- // Log at info level
43
- logger.info('User logged in');
44
-
45
- // Log at error level
46
- logger.error('Database connection failed');
47
-
48
- // Generic log method with level specification
49
- logger.log('warn', 'High memory usage detected');
50
- ```
51
-
52
- ### Log Levels
53
-
54
- The logger supports the following log levels:
55
-
56
- - `debug` - Detailed debug information
57
- - `info` - General informational messages
58
- - `warn` - Warning messages
59
- - `error` - Error messages
60
- - `fatal` - Fatal error messages (rarely recoverable)
61
-
62
- ### Adding Context
63
-
64
- You can pass an object as the second argument to add context to your logs:
65
-
66
- ```typescript
67
- logger.info('User action', {
68
- userId: 123,
69
- action: 'profile_update',
70
- ip: '192.168.1.1',
71
- });
72
- ```
73
-
74
- ### Configuration
75
-
76
- The logger accepts an options object in its constructor:
77
-
78
- ```typescript
79
- import { Logger } from '@ricdotnet/logger';
80
- import { PrettyFormatter } from '@ricdotnet/logger';
81
-
82
- const logger = new Logger({
83
- // Set the minimum log level
84
- level: 'debug',
85
-
86
- // Specify custom formatters (defaults to JsonFormatter)
87
- formatter: new PrettyFormatter(),
88
-
89
- // Add custom transports (defaults to Console transport)
90
- transports: [
91
- // Your custom transports here
92
- ],
93
-
94
- // Add default context that's included in all logs
95
- context: {
96
- service: 'api',
97
- version: '1.0.0',
98
- },
99
- });
100
- ```
101
-
102
- ### Available Formatters
103
-
104
- #### JsonFormatter (default)
105
-
106
- Outputs logs as JSON, perfect for log aggregation services:
107
-
108
- ```typescript
109
- import { Logger } from '@ricdotnet/logger';
110
-
111
- const logger = new Logger();
112
- // Output: {"timestamp":"2024-11-01T10:30:45.123Z","level":"info","message":"User logged in"}
113
- ```
114
-
115
- #### PrettyFormatter
116
-
117
- Human-readable format, great for development:
118
-
119
- ```typescript
120
- import { Logger, PrettyFormatter } from '@ricdotnet/logger';
121
-
122
- const logger = new Logger({
123
- formatter: new PrettyFormatter(),
124
- });
125
-
126
- logger.info('User logged in', { userId: 123 });
127
- // Output: [11/1/2024, 10:30:45 AM] [INFO] User logged in {"userId":123}
128
- ```
129
-
130
- ### Using Transports
131
-
132
- Transports determine where logs are written. The default console transport writes to `stdout` for info/debug/warn and `stderr` for error/fatal messages.
133
-
134
- #### Console Transport (default)
135
-
136
- ```typescript
137
- import { Logger, ConsoleTransport } from '@ricdotnet/logger';
138
-
139
- const logger = new Logger({
140
- transports: [new ConsoleTransport()],
141
- });
142
- ```
143
-
144
- #### Creating Custom Transports
145
-
146
- Implement the `Transport` interface to create your own:
147
-
148
- ```typescript
149
- import { Logger, Transport, LogEntry } from '@ricdotnet/logger';
150
-
151
- class FileTransport implements Transport {
152
- constructor(private filepath: string) {}
153
-
154
- write(entry: LogEntry, formatted: string): void {
155
- // Write to file
156
- fs.appendFileSync(this.filepath, formatted + '\n');
157
- }
158
-
159
- close(): void {
160
- // Cleanup if needed
161
- }
162
- }
163
-
164
- const logger = new Logger({
165
- transports: [new ConsoleTransport(), new FileTransport('./logs/app.log')],
166
- });
167
- ```
168
-
169
- ### Child Loggers
170
-
171
- Create child loggers that inherit configuration and context from their parent:
172
-
173
- ```typescript
174
- const logger = new Logger({
175
- context: { service: 'api' },
176
- });
177
-
178
- // Child logger inherits parent context
179
- const requestLogger = logger.child({
180
- requestId: 'abc-123',
181
- userId: 456,
182
- });
183
-
184
- requestLogger.info('Request received');
185
- // Logs include: { service: 'api', requestId: 'abc-123', userId: 456, ... }
186
- ```
187
-
188
- This is useful for associating logs with specific requests, operations, or components.
189
-
190
- ## Complete Example
191
-
192
- ```typescript
193
- import { Logger, PrettyFormatter, type LogEntry } from '@ricdotnet/logger';
194
-
195
- // Create a custom file transport
196
- class FileTransport {
197
- constructor(private filepath: string) {}
198
-
199
- write(entry: LogEntry, formatted: string): void {
200
- // Implementation here
201
- }
202
- }
203
-
204
- // Initialize logger
205
- const logger = new Logger({
206
- level: 'debug',
207
- formatter: new PrettyFormatter(),
208
- transports: [new FileTransport('./logs/app.log')],
209
- context: {
210
- app: 'my-app',
211
- environment: process.env.NODE_ENV,
212
- },
213
- });
214
-
215
- // Use in your application
216
- const userLogger = logger.child({ module: 'users' });
217
- userLogger.info('Processing user registration', { email: 'user@example.com' });
218
-
219
- try {
220
- // Some operation
221
- } catch (error) {
222
- userLogger.error('Registration failed', { error: error.message });
223
- }
224
- ```
225
-
226
- ## API Reference
227
-
228
- ### Logger
229
-
230
- #### Constructor
231
-
232
- ```typescript
233
- constructor(options?: {
234
- level?: 'debug' | 'info' | 'warn' | 'error';
235
- formatter?: Formatter;
236
- transports?: Transport[];
237
- context?: Record<string, unknown>;
238
- })
239
- ```
240
-
241
- #### Methods
242
-
243
- - `info(message: string, context?: Record<string, unknown>): void` - Log at info level
244
- - `error(message: string, context?: Record<string, unknown>): void` - Log at error level
245
- - `log(level: TLevel, message: string, context?: Record<string, unknown>): void` - Generic log method
246
- - `child(context?: Record<string, unknown>): Logger` - Create a child logger with inherited context
247
-
248
- ### Exports
249
-
250
- ```typescript
251
- import {
252
- Logger, // Main logger class
253
- PrettyFormatter, // Human-readable formatter
254
- JsonFormatter, // JSON formatter
255
- ConsoleTransport, // Console transport
256
- // Types
257
- type LogEntry,
258
- type LogLevel,
259
- type Formatter,
260
- type Transport,
261
- type TLevel,
262
- } from '@ricdotnet/logger';
263
- ```
264
-
265
- ## TypeScript Support
266
-
267
- All types are exported from the main package entry point:
268
-
269
- ```typescript
270
- import type {
271
- LogEntry, // The log entry object
272
- LogLevel, // Union type of all levels
273
- Formatter, // Transport interface
274
- Transport, // Transport interface
275
- } from '@ricdotnet/logger';
276
- ```
277
-
278
- ## License
279
-
280
- ISC
281
-
282
- ## Author
283
-
284
- ricdotnet - [ricr.dev](https://ricr.dev)
1
+ # @ricdotnet/logger
2
+
3
+ A lightweight, flexible logging library for TypeScript/JavaScript projects with support for custom formatters and transports.
4
+
5
+ ## Features
6
+
7
+ - 🎯 **Simple API** - Easy to use with sensible defaults
8
+ - 📦 **Modular** - Swap formatters and transports to suit your needs
9
+ - 🎨 **Multiple Formatters** - JSON and Pretty formatters included
10
+ - 📤 **Extensible Transports** - Console transport included, create your own
11
+ - 🌳 **Contextual Logging** - Add context that propagates through child loggers
12
+ - 📝 **Full TypeScript Support** - Built with TypeScript for type safety
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @ricdotnet/logger
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```typescript
23
+ import { Logger } from '@ricdotnet/logger';
24
+
25
+ const logger = new Logger();
26
+
27
+ logger.info('Application started');
28
+ logger.error('Something went wrong!');
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ### Basic Logging
34
+
35
+ The logger provides two main methods for logging:
36
+
37
+ ```typescript
38
+ import { Logger } from '@ricdotnet/logger';
39
+
40
+ const logger = new Logger();
41
+
42
+ // Log at info level
43
+ logger.info('User logged in');
44
+
45
+ // Log at error level
46
+ logger.error('Database connection failed');
47
+
48
+ // Generic log method with level specification
49
+ logger.log('warn', 'High memory usage detected');
50
+ ```
51
+
52
+ ### Log Levels
53
+
54
+ The logger supports the following log levels:
55
+
56
+ - `0 - TRACE` - Low-level debugging information
57
+ - `10 - DEBUG` - Detailed debug information
58
+ - `20 - INFO` - General informational messages
59
+ - `30 - WARN` - Warning messages
60
+ - `40 - ERROR` - Error messages
61
+ - `50 - FATAL` - Fatal error messages (rarely recoverable)
62
+
63
+ ### Adding Context
64
+
65
+ You can pass an object as the second argument to add context to your logs:
66
+
67
+ ```typescript
68
+ logger.info('User action', {
69
+ userId: 123,
70
+ action: 'profile_update',
71
+ ip: '192.168.1.1',
72
+ });
73
+ ```
74
+
75
+ ### Configuration
76
+
77
+ The logger accepts an options object in its constructor:
78
+
79
+ ```typescript
80
+ import { Logger, LogLevel, PrettyFormatter } from '@ricdotnet/logger';
81
+
82
+ const logger = new Logger({
83
+ // Set the minimum log level
84
+ level: LogLevel.DEBUG,
85
+
86
+ // Specify custom formatters (defaults to JsonFormatter)
87
+ formatter: new PrettyFormatter(),
88
+
89
+ // Add custom transports (defaults to Console transport)
90
+ transports: [
91
+ // Your custom transports here
92
+ ],
93
+
94
+ // Add default context that's included in all logs
95
+ context: {
96
+ service: 'api',
97
+ version: '1.0.0',
98
+ },
99
+ });
100
+ ```
101
+
102
+ ### Available Formatters
103
+
104
+ #### JsonFormatter (default)
105
+
106
+ Outputs logs as JSON, perfect for log aggregation services:
107
+
108
+ ```typescript
109
+ import { Logger } from '@ricdotnet/logger';
110
+
111
+ const logger = new Logger();
112
+ // Output: {"timestamp":"2024-11-01T10:30:45.123Z","level":"info","message":"User logged in"}
113
+ ```
114
+
115
+ #### PrettyFormatter
116
+
117
+ Human-readable format, great for development:
118
+
119
+ ```typescript
120
+ import { Logger, PrettyFormatter } from '@ricdotnet/logger';
121
+
122
+ const logger = new Logger({
123
+ formatter: new PrettyFormatter(),
124
+ });
125
+
126
+ logger.info('User logged in', { userId: 123 });
127
+ // Output:
128
+ // [11/1/2024, 10:30:45 AM] [INFO] User logged in
129
+ // userId=123
130
+ ```
131
+
132
+ ### Using Transports
133
+
134
+ Transports determine where logs are written. The default console transport writes to `stdout` for info/debug/warn and `stderr` for error/fatal messages.
135
+
136
+ #### Console Transport (default)
137
+
138
+ ```typescript
139
+ import { Logger, ConsoleTransport } from '@ricdotnet/logger';
140
+
141
+ const logger = new Logger({
142
+ transports: [new ConsoleTransport()],
143
+ });
144
+ ```
145
+
146
+ #### Creating Custom Transports
147
+
148
+ Implement the `Transport` interface to create your own:
149
+
150
+ ```typescript
151
+ import { Logger, Transport, LogEntry } from '@ricdotnet/logger';
152
+
153
+ class FileTransport implements Transport {
154
+ constructor(private filepath: string) {}
155
+
156
+ write(entry: LogEntry, formatted: string): void {
157
+ // Write to file
158
+ fs.appendFileSync(this.filepath, formatted + '\n');
159
+ }
160
+
161
+ close(): void {
162
+ // Cleanup if needed
163
+ }
164
+ }
165
+
166
+ const logger = new Logger({
167
+ transports: [new ConsoleTransport(), new FileTransport('./logs/app.log')],
168
+ });
169
+ ```
170
+
171
+ ### Child Loggers
172
+
173
+ Create child loggers that inherit configuration and context from their parent:
174
+
175
+ ```typescript
176
+ const logger = new Logger({
177
+ context: { service: 'api' },
178
+ });
179
+
180
+ // Child logger inherits parent context
181
+ const requestLogger = logger.child({
182
+ requestId: 'abc-123',
183
+ userId: 456,
184
+ });
185
+
186
+ requestLogger.info('Request received');
187
+ // Logs include: { service: 'api', requestId: 'abc-123', userId: 456, ... }
188
+ ```
189
+
190
+ This is useful for associating logs with specific requests, operations, or components.
191
+
192
+ ## Complete Example
193
+
194
+ ```typescript
195
+ import { Logger, PrettyFormatter, type LogEntry } from '@ricdotnet/logger';
196
+
197
+ // Create a custom file transport
198
+ class FileTransport {
199
+ constructor(private filepath: string) {}
200
+
201
+ write(entry: LogEntry, formatted: string): void {
202
+ // Implementation here
203
+ }
204
+ }
205
+
206
+ // Initialize logger
207
+ const logger = new Logger({
208
+ level: 'debug',
209
+ formatter: new PrettyFormatter(),
210
+ transports: [new FileTransport('./logs/app.log')],
211
+ context: {
212
+ app: 'my-app',
213
+ environment: process.env.NODE_ENV,
214
+ },
215
+ });
216
+
217
+ // Use in your application
218
+ const userLogger = logger.child({ module: 'users' });
219
+ userLogger.info('Processing user registration', { email: 'user@example.com' });
220
+
221
+ try {
222
+ // Some operation
223
+ } catch (error) {
224
+ userLogger.error('Registration failed', { error: error.message });
225
+ }
226
+ ```
227
+
228
+ ## API Reference
229
+
230
+ ### Logger
231
+
232
+ #### Constructor
233
+
234
+ ```typescript
235
+ constructor(options?: {
236
+ level?: LogLevel;
237
+ formatter?: Formatter;
238
+ transports?: Transport[];
239
+ context?: Record<string, unknown>;
240
+ })
241
+ ```
242
+
243
+ #### Methods
244
+
245
+ - `info(message: string, context?: Record<string, unknown>): void` - Log at info level
246
+ - `error(message: string, context?: Record<string, unknown>): void` - Log at error level
247
+ - `log(level: TLevel, message: string, context?: Record<string, unknown>): void` - Generic log method
248
+ - `child(context?: Record<string, unknown>): Logger` - Create a child logger with inherited context
249
+
250
+ ### Exports
251
+
252
+ ```typescript
253
+ import {
254
+ Logger, // Main logger class
255
+ LogLevel, // LogLevel enum containing 5 levels
256
+ PrettyFormatter, // Human-readable formatter
257
+ JsonFormatter, // JSON formatter
258
+ ConsoleTransport, // Console transport
259
+ LogLevelNames, // Map of log level names corresponding to their enum value
260
+ // Types
261
+ type LogEntry,
262
+ type Formatter,
263
+ type Transport,
264
+ type TLevel,
265
+ } from '@ricdotnet/logger';
266
+ ```
267
+
268
+ ## TypeScript Support
269
+
270
+ All types are exported from the main package entry point:
271
+
272
+ ```typescript
273
+ import type {
274
+ LogEntry, // The log entry object
275
+ LogLevel, // Union type of all levels
276
+ Formatter, // Transport interface
277
+ Transport, // Transport interface
278
+ } from '@ricdotnet/logger';
279
+ ```
280
+
281
+ ## License
282
+
283
+ ISC
284
+
285
+ ## Author
286
+
287
+ ricdotnet - [ricr.dev](https://ricr.dev)
@@ -1,4 +1,6 @@
1
- import type { Formatter, LogEntry } from '../types';
1
+ import { type Formatter, type LogEntry } from '../types';
2
2
  export declare class PrettyFormatter implements Formatter {
3
+ private indent;
3
4
  format(entry: LogEntry): string;
5
+ private unwrapContext;
4
6
  }
@@ -1,7 +1,20 @@
1
+ import { LogLevelNames } from '../types';
1
2
  export class PrettyFormatter {
3
+ indent = 2;
2
4
  format(entry) {
3
5
  const timestamp = new Date(entry.timestamp).toLocaleString();
4
- const context = entry.context ? JSON.stringify(entry.context) : '';
5
- return `[${timestamp}] [${entry.level.toUpperCase()}] ${entry.message} ${context}`;
6
+ const context = this.unwrapContext(entry.context ?? {}).join('\n');
7
+ return `[${timestamp}] [${LogLevelNames[entry.level].toLocaleUpperCase()}] ${entry.message}\n${context}`;
8
+ }
9
+ unwrapContext(context, unwrapped = [], level = 1) {
10
+ for (const key in context) {
11
+ if (typeof context[key] === 'object') {
12
+ unwrapped.push(`${' '.repeat(level * this.indent)}${key}`);
13
+ this.unwrapContext(context[key], unwrapped, level + 1);
14
+ continue;
15
+ }
16
+ unwrapped.push(`${' '.repeat(level * this.indent)}${key}=${context[key]}`);
17
+ }
18
+ return unwrapped;
6
19
  }
7
20
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { Logger } from './logger';
2
- export type { LogEntry, LogLevel, Formatter, Transport } from './types';
2
+ export { LogLevel, LogLevelNames } from './types';
3
+ export type { LogEntry, Formatter, Transport } from './types';
3
4
  export { JsonFormatter } from './formatters/json';
4
5
  export { PrettyFormatter } from './formatters/pretty';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // Main exports
2
2
  export { Logger } from './logger';
3
+ export { LogLevel, LogLevelNames } from './types';
3
4
  export { JsonFormatter } from './formatters/json';
4
5
  export { PrettyFormatter } from './formatters/pretty';
package/dist/logger.js CHANGED
@@ -1,37 +1,38 @@
1
1
  import { JsonFormatter } from './formatters/json';
2
2
  import { ConsoleTransport } from './transports/console';
3
+ import { LogLevel } from './types';
3
4
  export class Logger {
4
5
  name;
5
- level = 'info';
6
+ level;
6
7
  transports = [new ConsoleTransport()];
7
8
  context;
8
9
  formatter;
9
10
  redact;
10
11
  constructor(options = {}) {
11
12
  this.name = options.name || 'default';
12
- this.level = options.level || 'info';
13
+ this.level = options.level || LogLevel.INFO;
13
14
  this.transports = [...this.transports, ...(options.transports || [])];
14
15
  this.context = options.context || {};
15
16
  this.formatter = options.formatter || new JsonFormatter();
16
17
  this.redact = options.redact || [];
17
18
  }
18
19
  trace(message, context = {}) {
19
- this.log('trace', message, context);
20
+ this.log(LogLevel.TRACE, message, context);
20
21
  }
21
22
  debug(message, context = {}) {
22
- this.log('debug', message, context);
23
+ this.log(LogLevel.DEBUG, message, context);
23
24
  }
24
25
  info(message, context = {}) {
25
- this.log('info', message, context);
26
+ this.log(LogLevel.INFO, message, context);
26
27
  }
27
28
  warn(message, context = {}) {
28
- this.log('warn', message, context);
29
+ this.log(LogLevel.WARN, message, context);
29
30
  }
30
31
  error(message, context = {}) {
31
- this.log('error', message, context);
32
+ this.log(LogLevel.ERROR, message, context);
32
33
  }
33
34
  fatal(message, context = {}) {
34
- this.log('fatal', message, context);
35
+ this.log(LogLevel.FATAL, message, context);
35
36
  }
36
37
  log(level, message, context = {}) {
37
38
  if (!this.shouldLog(level)) {
@@ -51,7 +52,7 @@ export class Logger {
51
52
  timestamp: new Date().toISOString(),
52
53
  level,
53
54
  message,
54
- ...mergedContext,
55
+ context: mergedContext,
55
56
  };
56
57
  const output = this.formatter.format(entry);
57
58
  for (const transport of this.transports) {
@@ -90,6 +91,6 @@ export class Logger {
90
91
  return this.redact;
91
92
  }
92
93
  shouldLog(level) {
93
- return this.level === 'debug' || level === this.level;
94
+ return level >= this.level;
94
95
  }
95
96
  }
@@ -1,6 +1,7 @@
1
+ import { LogLevel } from '../types';
1
2
  export class ConsoleTransport {
2
3
  write(entry, formatted) {
3
- const output = entry.level === 'error' || entry.level === 'fatal'
4
+ const output = entry.level === LogLevel.ERROR || entry.level === LogLevel.FATAL
4
5
  ? process.stderr
5
6
  : process.stdout;
6
7
  output.write(`${formatted}\n`);
package/dist/types.d.ts CHANGED
@@ -1,4 +1,12 @@
1
- export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
1
+ export declare enum LogLevel {
2
+ TRACE = 0,
3
+ DEBUG = 10,
4
+ INFO = 20,
5
+ WARN = 30,
6
+ ERROR = 40,
7
+ FATAL = 50
8
+ }
9
+ export declare const LogLevelNames: Record<LogLevel, string>;
2
10
  export interface LogEntry {
3
11
  timestamp: string;
4
12
  level: LogLevel;
package/dist/types.js CHANGED
@@ -1 +1,17 @@
1
- export {};
1
+ export var LogLevel;
2
+ (function (LogLevel) {
3
+ LogLevel[LogLevel["TRACE"] = 0] = "TRACE";
4
+ LogLevel[LogLevel["DEBUG"] = 10] = "DEBUG";
5
+ LogLevel[LogLevel["INFO"] = 20] = "INFO";
6
+ LogLevel[LogLevel["WARN"] = 30] = "WARN";
7
+ LogLevel[LogLevel["ERROR"] = 40] = "ERROR";
8
+ LogLevel[LogLevel["FATAL"] = 50] = "FATAL";
9
+ })(LogLevel || (LogLevel = {}));
10
+ export const LogLevelNames = {
11
+ [LogLevel.TRACE]: 'trace',
12
+ [LogLevel.DEBUG]: 'debug',
13
+ [LogLevel.INFO]: 'info',
14
+ [LogLevel.WARN]: 'warn',
15
+ [LogLevel.ERROR]: 'error',
16
+ [LogLevel.FATAL]: 'fatal',
17
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricdotnet/logger",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "A logger utility for my personal projects",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",