@vanilla-bean/log 7.0.0
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/LICENSE +21 -0
- package/README.md +311 -0
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/package.json +52 -0
- package/src/log.d.ts +57 -0
- package/src/log.js +193 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 justfatlard
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# Log
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
|
|
5
|
+
Zero-dependency console logging with tags, verbosity control, and unlimited custom log levels.
|
|
6
|
+
|
|
7
|
+
## Why Log
|
|
8
|
+
|
|
9
|
+
Log follows Unix philosophy: do logging well, let the system handle output routing. Writes to stdout as plain text; containers, process supervisors, and log tails handle the rest.
|
|
10
|
+
|
|
11
|
+
- Zero runtime dependencies
|
|
12
|
+
- Custom methods (`log.success()`, `log.database()`) alongside native console methods, no configuration needed
|
|
13
|
+
- Tagged loggers, verbosity filtering, color, and global registry in ~1KB, easy to read, easy to fork
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @vanilla-bean/log
|
|
19
|
+
# or
|
|
20
|
+
bun add @vanilla-bean/log
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
Create tagged loggers that share instances and support unlimited custom methods:
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
import Log from '@vanilla-bean/log';
|
|
29
|
+
|
|
30
|
+
// Create tagged logger instances
|
|
31
|
+
const api = new Log({ tag: 'api', verbosity: 2, color: true });
|
|
32
|
+
const db = new Log({ tag: 'database', verbosity: 1 });
|
|
33
|
+
|
|
34
|
+
// Use all console methods
|
|
35
|
+
api.info('Server started');
|
|
36
|
+
api.warn('High memory usage');
|
|
37
|
+
api.error('Connection failed');
|
|
38
|
+
|
|
39
|
+
// Custom methods work automatically
|
|
40
|
+
api.success('Request completed');
|
|
41
|
+
db.query('SELECT * FROM users');
|
|
42
|
+
|
|
43
|
+
// Verbosity-gated calls: log(n)('msg') shows when logger verbosity > n
|
|
44
|
+
api(1)('Debug info'); // shows when verbosity > 1 (visible here, verbosity is 2)
|
|
45
|
+
api(3)('Trace data'); // shows when verbosity > 3 (hidden here, verbosity is 2)
|
|
46
|
+
|
|
47
|
+
// log('msg') is shorthand for log(0)('msg') — shows when verbosity > 0
|
|
48
|
+
api('Request received');
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Core Features
|
|
52
|
+
|
|
53
|
+
### Tagged Logger Registry
|
|
54
|
+
|
|
55
|
+
Identical tags return the same logger instance, preventing duplicates and enabling cross-module coordination:
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
// Module A
|
|
59
|
+
const log = new Log({ tag: 'app', verbosity: 1 });
|
|
60
|
+
|
|
61
|
+
// Module B - gets same logger instance
|
|
62
|
+
const log = new Log({ tag: 'app' }); // Inherits verbosity: 1
|
|
63
|
+
|
|
64
|
+
// Access any logger globally
|
|
65
|
+
import { loggers } from '@vanilla-bean/log';
|
|
66
|
+
loggers.app.options.verbosity = 3; // Updates everywhere
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
> **Note:** Calling `new Log()` without a tag uses the shared `__default` singleton. Two unrelated modules both doing `new Log()` will share the same logger instance and options. Always pass a tag when you want an independent logger.
|
|
70
|
+
|
|
71
|
+
### Verbosity Control
|
|
72
|
+
|
|
73
|
+
The primary form is `log(n)('msg')`: logs when the logger's verbosity is greater than `n`, silent otherwise. Calling `log('msg')` directly is shorthand for `log(0)('msg')`: shows when verbosity is greater than 0.
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
const log = new Log({ verbosity: 3 });
|
|
77
|
+
|
|
78
|
+
log(1)('High priority'); // shows when verbosity > 1
|
|
79
|
+
log(2)('Debug info'); // shows when verbosity > 2
|
|
80
|
+
log(5)('Trace data'); // shows when verbosity > 5 (hidden here)
|
|
81
|
+
|
|
82
|
+
log('Shorthand'); // shorthand for log(0)('Shorthand') — shows when verbosity > 0
|
|
83
|
+
|
|
84
|
+
// Works the same on any method
|
|
85
|
+
log.warn(1)('Important warning');
|
|
86
|
+
log.error(0)('Critical error');
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
> **Gotcha:** A lone number is always treated as a verbosity level, never as a message. `log(42)` returns a curried function; it does not log the number 42. To log a bare number, pass a second argument: `log(42, '')`.
|
|
90
|
+
|
|
91
|
+
### Unlimited Log Methods
|
|
92
|
+
|
|
93
|
+
Create unlimited custom log methods alongside native console methods:
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
const log = new Log({ tag: 'app' });
|
|
97
|
+
|
|
98
|
+
// Native console methods
|
|
99
|
+
log.info('Information');
|
|
100
|
+
log.warn('Warning');
|
|
101
|
+
log.error('Error');
|
|
102
|
+
log.debug('Debug data');
|
|
103
|
+
|
|
104
|
+
// Custom methods work automatically
|
|
105
|
+
log.success('Operation completed');
|
|
106
|
+
log.database('Query executed');
|
|
107
|
+
log.network('Request sent');
|
|
108
|
+
log.cache('Cache hit');
|
|
109
|
+
log.security('Auth failed');
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Color Output
|
|
113
|
+
|
|
114
|
+
Add color support for enhanced terminal readability:
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
const log = new Log({ color: true });
|
|
118
|
+
|
|
119
|
+
log.info('Blue text'); // Built-in blue
|
|
120
|
+
log.warn('Yellow text'); // Built-in yellow
|
|
121
|
+
log.error('Red text'); // Built-in red
|
|
122
|
+
|
|
123
|
+
// Custom colors per tag or method
|
|
124
|
+
const colorLog = new Log({
|
|
125
|
+
color: true,
|
|
126
|
+
colorMap: {
|
|
127
|
+
success: '\x1b[32m', // Green
|
|
128
|
+
critical: '\x1b[91m', // Bright red
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Configuration Options
|
|
134
|
+
|
|
135
|
+
```javascript
|
|
136
|
+
const log = new Log({
|
|
137
|
+
tag: 'myapp', // Logger identifier (omit to share the global __default singleton)
|
|
138
|
+
verbosity: 0, // Default: 0 (silent). log(n)() shows when n < verbosity; log('msg') shows when verbosity > 0
|
|
139
|
+
color: false, // Disable color output
|
|
140
|
+
silentTag: false, // Hide [tag] prefix
|
|
141
|
+
methodTag: false, // Show [method] label for native methods (info/warn/error); custom methods always show their label
|
|
142
|
+
colorMap: {}, // Custom ANSI color codes
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Advanced Usage
|
|
147
|
+
|
|
148
|
+
### Dynamic Configuration
|
|
149
|
+
|
|
150
|
+
```javascript
|
|
151
|
+
const log = new Log({ tag: 'api', verbosity: 1 });
|
|
152
|
+
|
|
153
|
+
log(2)('This is hidden');
|
|
154
|
+
|
|
155
|
+
// Increase verbosity at runtime
|
|
156
|
+
log.options.verbosity = 3;
|
|
157
|
+
|
|
158
|
+
log(2)('Now visible');
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Tag Management
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
const log = new Log({ tag: 'init' });
|
|
165
|
+
log('Starting up');
|
|
166
|
+
|
|
167
|
+
// Switch to different tag
|
|
168
|
+
log.setTag('runtime');
|
|
169
|
+
log('Now running');
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Global Configuration
|
|
173
|
+
|
|
174
|
+
Set process-wide defaults before creating loggers, or map custom methods to console methods:
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
import Log, { setDefaults, setMethodMap } from '@vanilla-bean/log';
|
|
178
|
+
|
|
179
|
+
setDefaults({ verbosity: 2, color: true });
|
|
180
|
+
setMethodMap({ critical: 'error', trace: 'debug' });
|
|
181
|
+
|
|
182
|
+
const log = new Log({ tag: 'app' }); // inherits verbosity: 2, color: true
|
|
183
|
+
log.critical('Fatal error'); // routes to console.error
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Cross-Module Coordination
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
// logger.js
|
|
190
|
+
import Log from '@vanilla-bean/log';
|
|
191
|
+
export const appLog = new Log({ tag: 'app', verbosity: 1 });
|
|
192
|
+
|
|
193
|
+
// module1.js
|
|
194
|
+
import { appLog } from './logger.js';
|
|
195
|
+
appLog.info('Module 1 loaded');
|
|
196
|
+
|
|
197
|
+
// module2.js
|
|
198
|
+
import { loggers } from '@vanilla-bean/log';
|
|
199
|
+
loggers.app.warn('Module 2 warning'); // Same logger instance
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Production Deployment
|
|
203
|
+
|
|
204
|
+
### Log Aggregation
|
|
205
|
+
|
|
206
|
+
Log writes human-readable text to stdout. For log aggregation pipelines that expect JSON (ELK, Fluentd, Datadog), you'll need a JSON logger. Log is the right tool when a human is reading the output — development, debugging, process supervision, or simple production tails.
|
|
207
|
+
|
|
208
|
+
For plain-text pipelines, disable colors and keep tags for pattern matching:
|
|
209
|
+
|
|
210
|
+
```javascript
|
|
211
|
+
const log = new Log({
|
|
212
|
+
tag: 'api',
|
|
213
|
+
silentTag: false, // keep [api] prefix for grep/awk
|
|
214
|
+
color: false, // no ANSI codes in log files
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
log.info('user_login', { userId: 123, ip: '1.2.3.4' });
|
|
218
|
+
// Output: [api] user_login { userId: 123, ip: '1.2.3.4' }
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Environment Configuration
|
|
222
|
+
|
|
223
|
+
```javascript
|
|
224
|
+
const log = new Log({
|
|
225
|
+
tag: process.env.SERVICE_NAME || 'app',
|
|
226
|
+
verbosity: process.env.LOG_LEVEL || 1,
|
|
227
|
+
color: process.env.NODE_ENV === 'development',
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Comparison
|
|
232
|
+
|
|
233
|
+
| Feature | Log | Winston | Pino | Debug |
|
|
234
|
+
| ----------------- | --------------------- | ---------------------- | ---------------- | ----------- |
|
|
235
|
+
| Bundle Size | ~1KB | ~2MB | ~1MB | ~8KB |
|
|
236
|
+
| Dependencies | 0 | Many | Some | 1 |
|
|
237
|
+
| Log Levels | Unlimited | 6 + custom | 6 standard | 1 |
|
|
238
|
+
| Verbosity Control | ✓ Core | ✓ | ✓ | Environment |
|
|
239
|
+
| Tags/Namespaces | ✓ Singleton | ✓ | ✓ | ✓ |
|
|
240
|
+
| Transports | stdout only | 20+ built-in | Plugin ecosystem | stdout only |
|
|
241
|
+
| Container-Native | ✓ | Configuration required | ✓ | ✓ |
|
|
242
|
+
|
|
243
|
+
## API Reference
|
|
244
|
+
|
|
245
|
+
### Constructor
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
new Log(options?: {
|
|
249
|
+
tag?: string; // Default: '__default'
|
|
250
|
+
verbosity?: number; // Default: 0
|
|
251
|
+
color?: boolean; // Default: false
|
|
252
|
+
silentTag?: boolean; // Default: false
|
|
253
|
+
methodTag?: boolean; // Default: false — shows [method] for native methods; custom methods always show it
|
|
254
|
+
colorMap?: object; // Custom colors
|
|
255
|
+
})
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Logger Methods
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
// Primary form: verbosity-gated, curried
|
|
262
|
+
log(verbosity: number): (message: any, ...args: any[]) => void;
|
|
263
|
+
log.methodName(verbosity: number): (message: any, ...args: any[]) => void;
|
|
264
|
+
|
|
265
|
+
// Shorthand: equivalent to log(0)('msg') — shows when verbosity > 0
|
|
266
|
+
log(message: any, ...args: any[]): void;
|
|
267
|
+
log.methodName(message: any, ...args: any[]): void;
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Instance Methods
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
log.setTag(newTag: string): void;
|
|
274
|
+
log.options: object; // Mutable configuration
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Global Configuration
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
setDefaults(options: Partial<LogOptions>): void;
|
|
281
|
+
setMethodMap(map: Record<string, string>): void;
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Exports
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import Log, { loggers, defaults, methodMap, setDefaults, setMethodMap } from '@vanilla-bean/log';
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Contributing
|
|
291
|
+
|
|
292
|
+
Contributions welcome! Submit Pull Requests for improvements. Open issues first for major changes.
|
|
293
|
+
|
|
294
|
+
### Development Setup
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
# Clone and setup
|
|
298
|
+
git clone https://github.com/fatlard1993/log.git
|
|
299
|
+
cd log
|
|
300
|
+
bun install
|
|
301
|
+
|
|
302
|
+
# Development workflow
|
|
303
|
+
bun test # Run tests
|
|
304
|
+
bun run demo # Run examples
|
|
305
|
+
bun run lint # Check code style
|
|
306
|
+
bun test --watch # Watch mode
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## License
|
|
310
|
+
|
|
311
|
+
MIT License - see the [LICENSE](LICENSE) file for details.
|
package/index.d.ts
ADDED
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vanilla-bean/log",
|
|
3
|
+
"version": "7.0.0",
|
|
4
|
+
"exports": {
|
|
5
|
+
".": "./index.js"
|
|
6
|
+
},
|
|
7
|
+
"types": "./index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"/src/**/*",
|
|
10
|
+
"!/src/**/*.test.js",
|
|
11
|
+
"index.js",
|
|
12
|
+
"index.d.ts"
|
|
13
|
+
],
|
|
14
|
+
"description": "A console log wrapper with tags, colors, and verbosity",
|
|
15
|
+
"author": "justfatlard",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"demo": "bun ./demo/index.js",
|
|
19
|
+
"lint": "bun --bun eslint",
|
|
20
|
+
"lint:fix": "bun --bun eslint --fix",
|
|
21
|
+
"format": "bun run lint:fix && bun --bun prettier --write . | grep -q unchanged",
|
|
22
|
+
"test": "bun test",
|
|
23
|
+
"test:watch": "bun test --watch",
|
|
24
|
+
"version": "npm version"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/fatlard1993/log.git"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/fatlard1993/log/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/fatlard1993/log#readme",
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@eslint/compat": "^1.4.1",
|
|
40
|
+
"@eslint/js": "^9.39.4",
|
|
41
|
+
"bun-types": "^1.3.14",
|
|
42
|
+
"eslint": "^9.39.4",
|
|
43
|
+
"eslint-plugin-compat": "^7.0.2",
|
|
44
|
+
"eslint-plugin-import": "^2.32.0",
|
|
45
|
+
"eslint-plugin-jsdoc": "^63.0.2",
|
|
46
|
+
"eslint-plugin-spellcheck": "0.0.20",
|
|
47
|
+
"eslint-plugin-unicorn": "^64.0.0",
|
|
48
|
+
"eslint-plugin-write-good-comments": "^0.2.0",
|
|
49
|
+
"globals": "^16.5.0",
|
|
50
|
+
"prettier": "^3.8.3"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/log.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export interface LogOptions {
|
|
2
|
+
/** Unique identifier for this logger */
|
|
3
|
+
tag?: string;
|
|
4
|
+
/** log(n) calls show when n < verbosity; direct calls show when verbosity > 0 */
|
|
5
|
+
verbosity?: number;
|
|
6
|
+
/** Enable ANSI color output */
|
|
7
|
+
color?: boolean;
|
|
8
|
+
/** Suppress [tag] prefix in output */
|
|
9
|
+
silentTag?: boolean;
|
|
10
|
+
/** Show [method] label for native console methods; custom methods always show their label */
|
|
11
|
+
methodTag?: boolean;
|
|
12
|
+
/** Custom ANSI color code mappings keyed by tag or method name */
|
|
13
|
+
colorMap?: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type ResolvedLogOptions = Required<LogOptions>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Callable directly or with a verbosity number for curried calls:
|
|
20
|
+
* log.info('message') → immediate log
|
|
21
|
+
* log.info(2)('message') → logs only when logger verbosity > 2
|
|
22
|
+
*/
|
|
23
|
+
export interface LogMethod {
|
|
24
|
+
(verbosity: number): (...args: any[]) => void;
|
|
25
|
+
(...args: any[]): void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Logger instance returned by `new Log()` */
|
|
29
|
+
export interface Logger extends LogMethod {
|
|
30
|
+
options: ResolvedLogOptions;
|
|
31
|
+
setTag(newTag: string): void;
|
|
32
|
+
info: LogMethod;
|
|
33
|
+
warn: LogMethod;
|
|
34
|
+
error: LogMethod;
|
|
35
|
+
debug: LogMethod;
|
|
36
|
+
/** Any custom method name works via Proxy */
|
|
37
|
+
[method: string]: any;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface LogConstructor {
|
|
41
|
+
new(options?: LogOptions): Logger;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
declare const Log: LogConstructor;
|
|
45
|
+
export default Log;
|
|
46
|
+
|
|
47
|
+
/** Global default options applied to all new logger instances */
|
|
48
|
+
export declare const defaults: ResolvedLogOptions;
|
|
49
|
+
/** Global map of custom method names to console method names */
|
|
50
|
+
export declare const methodMap: Record<string, string>;
|
|
51
|
+
/** Registry of all tagged logger instances */
|
|
52
|
+
export declare const loggers: Record<string, Logger>;
|
|
53
|
+
|
|
54
|
+
/** Merges options into the global defaults applied to all new logger instances */
|
|
55
|
+
export declare function setDefaults(options: Partial<ResolvedLogOptions>): void;
|
|
56
|
+
/** Merges entries into the global method map used by all loggers */
|
|
57
|
+
export declare function setMethodMap(map: Record<string, string>): void;
|
package/src/log.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stores all logger instances indexed by tag name.
|
|
3
|
+
* @type {Record<string, Function>}
|
|
4
|
+
*/
|
|
5
|
+
export const loggers = {};
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Default configuration options for all logger instances.
|
|
9
|
+
* @type {object}
|
|
10
|
+
* @property {number} verbosity - Verbosity threshold; log(n) shows when n < verbosity, direct calls show when verbosity > 0
|
|
11
|
+
* @property {boolean} color - Enable ANSI color output
|
|
12
|
+
* @property {boolean} silentTag - Suppress tag labels in output
|
|
13
|
+
* @property {boolean} methodTag - Show [method] label for native console methods; custom methods always show their label
|
|
14
|
+
* @property {string} tag - Default tag identifier
|
|
15
|
+
* @property {Record<string, string>} colorMap - Custom ANSI color code mappings
|
|
16
|
+
*/
|
|
17
|
+
export const defaults = {
|
|
18
|
+
verbosity: 0,
|
|
19
|
+
color: false,
|
|
20
|
+
silentTag: false,
|
|
21
|
+
methodTag: false,
|
|
22
|
+
tag: '__default',
|
|
23
|
+
colorMap: {},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Maps custom method names to console methods.
|
|
28
|
+
* @type {Record<string, string>}
|
|
29
|
+
*/
|
|
30
|
+
export const methodMap = {};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Default ANSI color codes for console output.
|
|
34
|
+
* @type {Record<string, string>}
|
|
35
|
+
*/
|
|
36
|
+
const DEFAULT_COLOR_MAP = {
|
|
37
|
+
__reset: '\u001B[0m',
|
|
38
|
+
info: '\u001B[34m',
|
|
39
|
+
warn: '\u001B[33m',
|
|
40
|
+
error: '\u001B[31m',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Creates flexible logging utility with tagging, verbosity levels, and color output.
|
|
45
|
+
* Implements singleton pattern where instances with identical tags share the same logger.
|
|
46
|
+
*/
|
|
47
|
+
class Log {
|
|
48
|
+
/**
|
|
49
|
+
* Creates new Log instance or returns existing instance for duplicate tags.
|
|
50
|
+
* @param {object} [options] - Configuration options
|
|
51
|
+
* @param {string} [options.tag] - Unique identifier for this logger
|
|
52
|
+
* @param {number} [options.verbosity] - Verbosity threshold to display messages
|
|
53
|
+
* @param {boolean} [options.color] - Enable ANSI color output
|
|
54
|
+
* @param {boolean} [options.silentTag] - Suppress tag labels from output
|
|
55
|
+
* @param {boolean} [options.methodTag] - Show [method] label for native console methods; custom methods always show their label
|
|
56
|
+
* @param {Record<string, string>} [options.colorMap] - Custom ANSI color code mappings
|
|
57
|
+
* @returns {Function} Proxy-wrapped logger function with dynamic method access
|
|
58
|
+
*/
|
|
59
|
+
constructor(options = {}) {
|
|
60
|
+
this.options = { ...defaults, ...options };
|
|
61
|
+
|
|
62
|
+
const { tag } = this.options;
|
|
63
|
+
|
|
64
|
+
if (loggers[tag]) {
|
|
65
|
+
loggers[tag].options = { ...loggers[tag].options, ...options };
|
|
66
|
+
|
|
67
|
+
return loggers[tag];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this._explicit = options;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Proxy that intercepts property access to dynamically create logger methods.
|
|
74
|
+
* Enables calling log.info(), log.warn(), etc. without pre-defining methods.
|
|
75
|
+
*/
|
|
76
|
+
const defaultLogger = new Proxy(this.makeLogger('log'), {
|
|
77
|
+
get: (target, key) => {
|
|
78
|
+
if (typeof key !== 'string') return Reflect.get(target, key);
|
|
79
|
+
return Reflect.get(target, key) ?? this[key] ?? this.makeLogger(key);
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
defaultLogger.options = this.options;
|
|
84
|
+
|
|
85
|
+
loggers[tag] = defaultLogger;
|
|
86
|
+
|
|
87
|
+
return defaultLogger;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Creates logger function for specified console method.
|
|
92
|
+
* @param {string} method - Console method name (log, info, warn, error, etc.)
|
|
93
|
+
* @returns {Function} Logger function supporting direct calls and verbosity-specific curried calls
|
|
94
|
+
*/
|
|
95
|
+
makeLogger(method) {
|
|
96
|
+
const { tag } = this.options;
|
|
97
|
+
|
|
98
|
+
return (...args) => {
|
|
99
|
+
const {
|
|
100
|
+
colorMap,
|
|
101
|
+
silentTag,
|
|
102
|
+
methodTag,
|
|
103
|
+
verbosity,
|
|
104
|
+
color: useColor,
|
|
105
|
+
} = loggers[tag]?.options || this.options;
|
|
106
|
+
|
|
107
|
+
const isVerbosityCall = args.length === 1 && typeof args[0] === 'number';
|
|
108
|
+
const thisVerbosity = isVerbosityCall ? args.shift() : 0;
|
|
109
|
+
if (!isVerbosityCall && args.length === 0) return;
|
|
110
|
+
const willShow = !!console && thisVerbosity < verbosity;
|
|
111
|
+
const isNativeConsoleMethod = !!console && typeof console[method] === 'function';
|
|
112
|
+
const resolvedMethod = isNativeConsoleMethod ? method : (methodMap[method] || 'log');
|
|
113
|
+
|
|
114
|
+
let colorReset;
|
|
115
|
+
|
|
116
|
+
if (willShow) {
|
|
117
|
+
const colors = { ...DEFAULT_COLOR_MAP, ...colorMap };
|
|
118
|
+
const color = colors[tag] || colors[method] || colors[methodMap[method]];
|
|
119
|
+
|
|
120
|
+
if (!silentTag) {
|
|
121
|
+
let tagText = '';
|
|
122
|
+
|
|
123
|
+
if (tag !== '__default') tagText += `[${tag}]`;
|
|
124
|
+
if (methodTag || !isNativeConsoleMethod) tagText += `[${method}]`;
|
|
125
|
+
|
|
126
|
+
if (tagText) args.unshift(tagText);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (useColor && color) {
|
|
130
|
+
args.unshift(color);
|
|
131
|
+
colorReset = colors.__reset;
|
|
132
|
+
if (!isVerbosityCall) args.push(colorReset);
|
|
133
|
+
} else if (method === 'error') {
|
|
134
|
+
args.unshift('[ERROR]');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (isVerbosityCall) {
|
|
139
|
+
if (!willShow || !console) return () => {};
|
|
140
|
+
return colorReset
|
|
141
|
+
? (...messageArgs) => console[resolvedMethod](...args, ...messageArgs, colorReset)
|
|
142
|
+
: (...messageArgs) => console[resolvedMethod](...args, ...messageArgs);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (willShow) console[resolvedMethod](...args);
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Updates logger tag and relocates instance in global registry.
|
|
151
|
+
* When newTag already exists, options are merged: constructor-time options take priority over the
|
|
152
|
+
* existing logger's options. Runtime mutations to this.options after construction are not tracked
|
|
153
|
+
* and may be overwritten by the existing logger's values during the merge.
|
|
154
|
+
* @param {string} newTag - New tag identifier
|
|
155
|
+
*/
|
|
156
|
+
setTag(newTag) {
|
|
157
|
+
const { tag } = this.options;
|
|
158
|
+
|
|
159
|
+
if (newTag === tag) return;
|
|
160
|
+
|
|
161
|
+
if (loggers[newTag]) {
|
|
162
|
+
Object.assign(this.options, loggers[newTag].options, this._explicit);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
loggers[newTag] = loggers[tag];
|
|
166
|
+
this.options.tag = newTag;
|
|
167
|
+
|
|
168
|
+
const { colorMap } = this.options;
|
|
169
|
+
if (colorMap[tag]) {
|
|
170
|
+
colorMap[newTag] = colorMap[tag];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
delete loggers[tag];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Merges options into the global defaults applied to all new logger instances.
|
|
179
|
+
* @param {Partial<typeof defaults>} options - Default settings to apply
|
|
180
|
+
*/
|
|
181
|
+
export function setDefaults(options) {
|
|
182
|
+
Object.assign(defaults, options);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Merges entries into the global method map used by all loggers.
|
|
187
|
+
* @param {Record<string, string>} map - Custom method names mapped to console methods
|
|
188
|
+
*/
|
|
189
|
+
export function setMethodMap(map) {
|
|
190
|
+
Object.assign(methodMap, map);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export default Log;
|