plslog 1.1.1 → 1.2.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/README.md ADDED
@@ -0,0 +1,336 @@
1
+ # plslog
2
+
3
+ A powerful, browser-first logging library with structured logging, namespaces, and environment-aware behavior.
4
+
5
+ ## Features
6
+
7
+ - **Log levels** - Hierarchical levels (`debug`, `info`, `warn`, `error`) with configurable visibility.
8
+ - **Namespaces** - Scoped loggers with pattern matching and filtering.
9
+ - **Structured logging** - JSON-first with pretty-format for objects and arrays.
10
+ - **Deep nested objects** - Configurable `maxDepth` for safe, readable traversal of nested data.
11
+ - **Pick / omit fields** - Whitelist or blacklist fields (dot notation, wildcards); redact, hide, or show type/length.
12
+ - **Dedup & once** - Collapse repeated identical logs; `once()` for one-time messages, `flushDedup()` to flush buffer.
13
+ - **Conditional logging** - `when()` / `unless()` with booleans or predicates for conditional log execution.
14
+ - **Assert** - `assert(condition, message, ...args)` logs error only when condition is falsy.
15
+ - **Truncate** - Limit object fields, string length, and array items to keep output concise.
16
+ - **Transports** - Forward structured log entries to external destinations (remote services, files, etc.).
17
+ - **Pretty console** - Styled, readable output in development.
18
+ - **Environment-aware** - Adjust behavior by environment (for example `NODE_ENV`).
19
+
20
+ ## Guide
21
+
22
+ ### Log Levels
23
+
24
+ plslog supports a hierarchical log level system with five levels:
25
+
26
+ - `debug` - priority `0`
27
+ - `info` - priority `1`
28
+ - `warn` - priority `2`
29
+ - `error` - priority `3`
30
+ - `none` - priority `4`
31
+
32
+ When a log level is set, only messages at that level or higher priority are logged.
33
+
34
+ ```typescript
35
+ import plslog from 'plslog';
36
+
37
+ plslog.configure({ level: 'warn' });
38
+
39
+ const logger = plslog('app');
40
+
41
+ logger.debug('This will not be logged');
42
+ logger.info('This will not be logged');
43
+ logger.warn('This will be logged');
44
+ logger.error('This will be logged');
45
+ ```
46
+
47
+ You can also specify active levels explicitly:
48
+
49
+ ```typescript
50
+ plslog.configure({
51
+ activeLevels: ['warn', 'error'],
52
+ });
53
+ ```
54
+
55
+ ### Namespaces
56
+
57
+ Organize logs with namespaces and filter them globally.
58
+
59
+ ```typescript
60
+ const apiLogger = plslog('api');
61
+ const dbLogger = plslog('db');
62
+ const authLogger = plslog('auth');
63
+
64
+ apiLogger.info('API request received');
65
+ dbLogger.debug('Query executed');
66
+ authLogger.warn('Invalid token');
67
+ ```
68
+
69
+ ```typescript
70
+ plslog.configure({ namespaces: 'api,db' });
71
+ plslog.configure({ namespaces: 'app:*' });
72
+ plslog.configure({ namespaces: '*,-app:db:*' });
73
+ plslog.configure({ namespaces: '*' });
74
+ ```
75
+
76
+ Supported patterns:
77
+
78
+ - `*` matches all namespaces
79
+ - `app:*` matches namespaces starting with `app:`
80
+ - `app:api,app:db` matches specific namespaces
81
+ - `-app:db:*` excludes a matching namespace pattern
82
+
83
+ ### Truncate Large Data
84
+
85
+ Prevent runaway output from large objects, arrays, or strings.
86
+
87
+ ```typescript
88
+ const logger = plslog('api');
89
+
90
+ logger.truncate(5).debug('User', largeUser);
91
+ logger.truncate({ fields: 5, string: 80, array: 3 }).debug('Order', order);
92
+ ```
93
+
94
+ ```typescript
95
+ plslog.configure({ truncate: { fields: 10, string: 200, array: 5 } });
96
+
97
+ const logger = plslog({ namespace: 'api', truncate: { fields: 5 } });
98
+
99
+ logger.truncate(3).debug('Compact view', bigObject);
100
+ ```
101
+
102
+ Truncation indicators:
103
+
104
+ - Fields: `{ a: 1, b: 2, '...': '+8 more fields' }`
105
+ - Array: `[1, 2, 3, '... +7 more']`
106
+ - String: `'first 80 chars... [200 chars]'`
107
+
108
+ Priority order: per-log `truncate()` > instance option > global config
109
+
110
+ ### Structured Logging
111
+
112
+ Objects and arrays are automatically serialized and formatted.
113
+
114
+ ```typescript
115
+ const logger = plslog('api');
116
+
117
+ logger.info('User data', {
118
+ id: 123,
119
+ name: 'John',
120
+ email: 'john@example.com',
121
+ });
122
+
123
+ logger.debug('Items', [1, 2, 3, { nested: 'value' }]);
124
+
125
+ logger.info('Complex data', {
126
+ user: {
127
+ profile: {
128
+ settings: { theme: 'dark' },
129
+ },
130
+ },
131
+ });
132
+ ```
133
+
134
+ Special handling includes:
135
+
136
+ - `Blob` and `File` objects with metadata
137
+ - base64 strings with preview and length information
138
+ - deep nesting with configurable `maxDepth`
139
+
140
+ ```typescript
141
+ plslog.configure({ maxDepth: 5 });
142
+
143
+ const logger = plslog({ namespace: 'app', maxDepth: 10 });
144
+ logger.maxDepth(3).debug('Deep object', veryDeepObject);
145
+ ```
146
+
147
+ ### Pretty Console Output
148
+
149
+ In development, plslog provides:
150
+
151
+ - color-coded log levels
152
+ - structured formatting for arrays and objects
153
+ - namespace badges
154
+ - browser console integration with matching console methods
155
+
156
+ Example:
157
+
158
+ ```text
159
+ [app] DEBUG
160
+ User data {
161
+ id: 123,
162
+ name: "John",
163
+ email: "john@example.com"
164
+ }
165
+ ```
166
+
167
+ ### Environment-Aware Behavior
168
+
169
+ ```typescript
170
+ import plslog from 'plslog';
171
+
172
+ const isProduction = import.meta.env.PROD;
173
+
174
+ const LOG_LEVELS_PROD = ['warn', 'error'];
175
+ const LOG_LEVELS_DEV = ['debug', 'info', 'warn', 'error'];
176
+
177
+ plslog.configure({
178
+ activeLevels: isProduction ? LOG_LEVELS_PROD : LOG_LEVELS_DEV,
179
+ namespaces: import.meta.env.VITE_LOG_NAMESPACES,
180
+ maxDepth: isProduction ? 3 : 10,
181
+ });
182
+ ```
183
+
184
+ Example environment variables:
185
+
186
+ ```bash
187
+ # .env.development
188
+ VITE_LOG_NAMESPACES=*
189
+
190
+ # .env.production
191
+ VITE_LOG_NAMESPACES=app:api,app:db
192
+ ```
193
+
194
+ ### Transports
195
+
196
+ Send structured log data to external destinations alongside normal console output.
197
+
198
+ ```typescript
199
+ plslog.configure({
200
+ transports: [
201
+ {
202
+ write(entry) {
203
+ fetch('/logs', {
204
+ method: 'POST',
205
+ body: JSON.stringify(entry),
206
+ });
207
+ },
208
+ },
209
+ ],
210
+ });
211
+ ```
212
+
213
+ Filter transport delivery by level:
214
+
215
+ ```typescript
216
+ plslog.configure({
217
+ transports: [
218
+ {
219
+ levels: ['warn', 'error'],
220
+ write(entry) {
221
+ sendToErrorTracker(entry);
222
+ },
223
+ },
224
+ ],
225
+ });
226
+ ```
227
+
228
+ Multiple transports:
229
+
230
+ ```typescript
231
+ plslog.configure({
232
+ transports: [
233
+ {
234
+ levels: ['error'],
235
+ write(entry) {
236
+ errorTracker.capture(entry);
237
+ },
238
+ },
239
+ {
240
+ write(entry) {
241
+ analyticsService.log(entry);
242
+ },
243
+ },
244
+ ],
245
+ });
246
+ ```
247
+
248
+ `TransportEntry` fields:
249
+
250
+ | Field | Type | Description |
251
+ | --- | --- | --- |
252
+ | `namespace` | `string` | Logger namespace |
253
+ | `level` | `LogLevel` | Log level |
254
+ | `message` | `string` | Log message |
255
+ | `args` | `unknown[]` | Raw arguments |
256
+ | `timestamp` | `number` | Unix timestamp in milliseconds |
257
+
258
+ Transport errors are swallowed to avoid crashing the app.
259
+
260
+ ## Installation
261
+
262
+ ```bash
263
+ npm install plslog
264
+ # or
265
+ pnpm add plslog
266
+ # or
267
+ yarn add plslog
268
+ ```
269
+
270
+ ## Quick Start
271
+
272
+ ```typescript
273
+ import plslog from 'plslog';
274
+
275
+ plslog.configure({
276
+ level: 'debug',
277
+ namespaces: '*',
278
+ });
279
+
280
+ const logger = plslog('my-app');
281
+
282
+ logger.debug('Debug message');
283
+ logger.info('Info message', { data: 'value' });
284
+ logger.warn('Warning message');
285
+ logger.error('Error message', new Error('Something went wrong'));
286
+ ```
287
+
288
+ ## API Reference
289
+
290
+ ### `plslog(options?: LoggerOptions | string): Logger`
291
+
292
+ Creates a new logger instance.
293
+
294
+ Parameters:
295
+
296
+ - `options`: a namespace string or a `LoggerOptions` object
297
+ - `namespace?: string` - logger namespace, default `'app'`
298
+ - `level?: LogLevel` - instance log level
299
+ - `maxDepth?: number` - maximum object serialization depth
300
+ - `truncate?: TruncateConfig` - default truncation config for this instance
301
+
302
+ Returns a `Logger` instance.
303
+
304
+ ### `plslog.configure(options: GlobalConfig): void`
305
+
306
+ Configures global logging behavior.
307
+
308
+ Parameters:
309
+
310
+ - `namespaces?: string` - comma-separated namespace filter
311
+ - `level?: LogLevel` - global hierarchical log level
312
+ - `activeLevels?: LogLevel[]` - explicit active levels
313
+ - `maxDepth?: number` - global max serialization depth
314
+ - `truncate?: TruncateConfig` - global truncation config
315
+ - `transports?: Transport[]` - structured log transports
316
+
317
+ ### `Logger` methods
318
+
319
+ - `debug(message: string, ...args: unknown[]): Logger`
320
+ - `info(message: string, ...args: unknown[]): Logger`
321
+ - `warn(message: string, ...args: unknown[]): Logger`
322
+ - `error(message: string, ...args: unknown[]): Logger`
323
+ - `setLevel(level: LogLevel): void`
324
+ - `maxDepth(maxDepth: number): Logger`
325
+ - `truncate(config: number | TruncateConfig): Logger`
326
+ - `pick(fields: string[]): Logger`
327
+ - `omit(fields: FilterFieldConfig[]): Logger`
328
+ - `when(condition: Condition): Logger`
329
+ - `unless(condition: Condition): Logger`
330
+ - `assert(condition: unknown, message: string, ...args: unknown[]): Logger`
331
+ - `once(enabled?: boolean): Logger`
332
+ - `flushDedup(): Logger`
333
+
334
+ ## License
335
+
336
+ MIT
package/dist/index.d.mts CHANGED
@@ -2,6 +2,11 @@ type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';
2
2
  type FilterMode = 'redact' | 'hide' | 'type' | 'length';
3
3
  type FilterFieldPredicate = (key: string, value: any, path: string[]) => boolean;
4
4
  type FilterFieldConfig = string | RegExp | FilterFieldPredicate;
5
+ interface TruncateConfig {
6
+ fields?: number;
7
+ string?: number;
8
+ array?: number;
9
+ }
5
10
  interface DedupConfig {
6
11
  enabled: boolean;
7
12
  flushInterval?: number;
@@ -22,6 +27,7 @@ interface LogFilterOptions {
22
27
  omit?: FilterFieldConfig[];
23
28
  filterMode?: FilterMode;
24
29
  filterReplacement?: string;
30
+ truncate?: TruncateConfig;
25
31
  }
26
32
  interface ErrorSerializationConfig {
27
33
  includeStack?: boolean;
@@ -44,6 +50,7 @@ interface LoggerOptions {
44
50
  maxDepth?: number;
45
51
  dedup?: Partial<DedupConfig>;
46
52
  context?: Record<string, unknown>;
53
+ truncate?: TruncateConfig;
47
54
  }
48
55
  type GlobalConfig = {
49
56
  namespaces?: string;
@@ -55,6 +62,8 @@ type GlobalConfig = {
55
62
  filterMode?: FilterMode;
56
63
  filterReplacement?: string;
57
64
  errorSerialization?: ErrorSerializationConfig;
65
+ transports?: Transport[];
66
+ truncate?: TruncateConfig;
58
67
  };
59
68
  interface ConditionContext {
60
69
  namespace: string;
@@ -62,11 +71,23 @@ interface ConditionContext {
62
71
  [key: string]: unknown;
63
72
  }
64
73
  type Condition = boolean | ((ctx: ConditionContext) => boolean);
74
+ interface TransportEntry {
75
+ namespace: string;
76
+ level: LogLevel;
77
+ message: string;
78
+ args: unknown[];
79
+ timestamp: number;
80
+ }
81
+ interface Transport {
82
+ write: (entry: TransportEntry) => void;
83
+ levels?: LogLevel[];
84
+ }
65
85
 
66
86
  declare class Logger {
67
87
  private _namespace;
68
88
  private _level;
69
89
  private _maxDepth;
90
+ private _truncateConfig?;
70
91
  private _dedupConfig;
71
92
  private _dedupBuffer;
72
93
  private _tempFilterOptions;
@@ -111,6 +132,21 @@ declare class Logger {
111
132
  */
112
133
  namespace(namespace: string): this;
113
134
  maxDepth(maxDepth: number): this;
135
+ /**
136
+ * Truncate large objects/arrays/strings before logging.
137
+ * Accepts a number (shorthand for fields limit) or a config object.
138
+ *
139
+ * @param config - Max field count (number) or { fields, string, array } limits
140
+ * @returns this for chaining
141
+ *
142
+ * @example
143
+ * // Limit to 5 fields per object
144
+ * logger.truncate(5).debug('User', user);
145
+ *
146
+ * // Fine-grained control
147
+ * logger.truncate({ fields: 5, string: 80, array: 3 }).debug('Order', order);
148
+ */
149
+ truncate(config: number | TruncateConfig): this;
114
150
  /**
115
151
  * Pick only specific fields to log (whitelist approach)
116
152
  * Supports dot notation for nested fields: 'user.name', 'user.email'
@@ -176,4 +212,4 @@ type Plslog = {
176
212
  };
177
213
  declare const plslog: Plslog;
178
214
 
179
- export { type Condition, type ConditionContext, type DedupConfig, type DedupEntry, type ErrorSerializationConfig, type FilterFieldConfig, type FilterFieldPredicate, type FilterMode, type FormatterOptions, type GlobalConfig, type LogFilterOptions, type LogLevel, type LoggerOptions, plslog as default };
215
+ export { type Condition, type ConditionContext, type DedupConfig, type DedupEntry, type ErrorSerializationConfig, type FilterFieldConfig, type FilterFieldPredicate, type FilterMode, type FormatterOptions, type GlobalConfig, type LogFilterOptions, type LogLevel, type LoggerOptions, type Transport, type TransportEntry, type TruncateConfig, plslog as default };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,11 @@ type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';
2
2
  type FilterMode = 'redact' | 'hide' | 'type' | 'length';
3
3
  type FilterFieldPredicate = (key: string, value: any, path: string[]) => boolean;
4
4
  type FilterFieldConfig = string | RegExp | FilterFieldPredicate;
5
+ interface TruncateConfig {
6
+ fields?: number;
7
+ string?: number;
8
+ array?: number;
9
+ }
5
10
  interface DedupConfig {
6
11
  enabled: boolean;
7
12
  flushInterval?: number;
@@ -22,6 +27,7 @@ interface LogFilterOptions {
22
27
  omit?: FilterFieldConfig[];
23
28
  filterMode?: FilterMode;
24
29
  filterReplacement?: string;
30
+ truncate?: TruncateConfig;
25
31
  }
26
32
  interface ErrorSerializationConfig {
27
33
  includeStack?: boolean;
@@ -44,6 +50,7 @@ interface LoggerOptions {
44
50
  maxDepth?: number;
45
51
  dedup?: Partial<DedupConfig>;
46
52
  context?: Record<string, unknown>;
53
+ truncate?: TruncateConfig;
47
54
  }
48
55
  type GlobalConfig = {
49
56
  namespaces?: string;
@@ -55,6 +62,8 @@ type GlobalConfig = {
55
62
  filterMode?: FilterMode;
56
63
  filterReplacement?: string;
57
64
  errorSerialization?: ErrorSerializationConfig;
65
+ transports?: Transport[];
66
+ truncate?: TruncateConfig;
58
67
  };
59
68
  interface ConditionContext {
60
69
  namespace: string;
@@ -62,11 +71,23 @@ interface ConditionContext {
62
71
  [key: string]: unknown;
63
72
  }
64
73
  type Condition = boolean | ((ctx: ConditionContext) => boolean);
74
+ interface TransportEntry {
75
+ namespace: string;
76
+ level: LogLevel;
77
+ message: string;
78
+ args: unknown[];
79
+ timestamp: number;
80
+ }
81
+ interface Transport {
82
+ write: (entry: TransportEntry) => void;
83
+ levels?: LogLevel[];
84
+ }
65
85
 
66
86
  declare class Logger {
67
87
  private _namespace;
68
88
  private _level;
69
89
  private _maxDepth;
90
+ private _truncateConfig?;
70
91
  private _dedupConfig;
71
92
  private _dedupBuffer;
72
93
  private _tempFilterOptions;
@@ -111,6 +132,21 @@ declare class Logger {
111
132
  */
112
133
  namespace(namespace: string): this;
113
134
  maxDepth(maxDepth: number): this;
135
+ /**
136
+ * Truncate large objects/arrays/strings before logging.
137
+ * Accepts a number (shorthand for fields limit) or a config object.
138
+ *
139
+ * @param config - Max field count (number) or { fields, string, array } limits
140
+ * @returns this for chaining
141
+ *
142
+ * @example
143
+ * // Limit to 5 fields per object
144
+ * logger.truncate(5).debug('User', user);
145
+ *
146
+ * // Fine-grained control
147
+ * logger.truncate({ fields: 5, string: 80, array: 3 }).debug('Order', order);
148
+ */
149
+ truncate(config: number | TruncateConfig): this;
114
150
  /**
115
151
  * Pick only specific fields to log (whitelist approach)
116
152
  * Supports dot notation for nested fields: 'user.name', 'user.email'
@@ -176,4 +212,4 @@ type Plslog = {
176
212
  };
177
213
  declare const plslog: Plslog;
178
214
 
179
- export { type Condition, type ConditionContext, type DedupConfig, type DedupEntry, type ErrorSerializationConfig, type FilterFieldConfig, type FilterFieldPredicate, type FilterMode, type FormatterOptions, type GlobalConfig, type LogFilterOptions, type LogLevel, type LoggerOptions, plslog as default };
215
+ export { type Condition, type ConditionContext, type DedupConfig, type DedupEntry, type ErrorSerializationConfig, type FilterFieldConfig, type FilterFieldPredicate, type FilterMode, type FormatterOptions, type GlobalConfig, type LogFilterOptions, type LogLevel, type LoggerOptions, type Transport, type TransportEntry, type TruncateConfig, plslog as default };