@outfitter/logging 0.1.0 → 0.3.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 +30 -0
- package/dist/index.d.ts +38 -4
- package/dist/index.js +89 -21
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -184,6 +184,7 @@ Routes logs to stdout/stderr based on level:
|
|
|
184
184
|
|
|
185
185
|
- `trace`, `debug`, `info` -> stdout
|
|
186
186
|
- `warn`, `error`, `fatal` -> stderr
|
|
187
|
+
- Falls back to `console.*` when process streams are unavailable (edge/serverless)
|
|
187
188
|
|
|
188
189
|
```typescript
|
|
189
190
|
import { createConsoleSink } from "@outfitter/logging";
|
|
@@ -303,6 +304,34 @@ await flush();
|
|
|
303
304
|
process.exit(0);
|
|
304
305
|
```
|
|
305
306
|
|
|
307
|
+
## Environment-Aware Log Level
|
|
308
|
+
|
|
309
|
+
### `resolveLogLevel(level?)`
|
|
310
|
+
|
|
311
|
+
Resolve the log level from environment configuration. Use this instead of hardcoding levels so your app responds to `OUTFITTER_ENV` and `OUTFITTER_LOG_LEVEL` automatically.
|
|
312
|
+
|
|
313
|
+
**Precedence** (highest wins):
|
|
314
|
+
1. `OUTFITTER_LOG_LEVEL` environment variable
|
|
315
|
+
2. Explicit `level` parameter
|
|
316
|
+
3. `OUTFITTER_ENV` profile defaults (`"debug"` in development)
|
|
317
|
+
4. `"info"` (default)
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
import { createLogger, resolveLogLevel } from "@outfitter/logging";
|
|
321
|
+
|
|
322
|
+
const logger = createLogger({
|
|
323
|
+
name: "my-app",
|
|
324
|
+
level: resolveLogLevel(),
|
|
325
|
+
sinks: [createConsoleSink()],
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// With OUTFITTER_ENV=development → "debug"
|
|
329
|
+
// With OUTFITTER_LOG_LEVEL=error → "error" (overrides everything)
|
|
330
|
+
// With nothing set → "info"
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
MCP-style level names are mapped automatically: `warning` to `warn`, `emergency`/`critical`/`alert` to `fatal`, `notice` to `info`.
|
|
334
|
+
|
|
306
335
|
## API Reference
|
|
307
336
|
|
|
308
337
|
### Functions
|
|
@@ -311,6 +340,7 @@ process.exit(0);
|
|
|
311
340
|
| ----------------------- | --------------------------------------------------- |
|
|
312
341
|
| `createLogger` | Create a configured logger instance |
|
|
313
342
|
| `createChildLogger` | Create a child logger with merged context |
|
|
343
|
+
| `resolveLogLevel` | Resolve log level from env vars and profile |
|
|
314
344
|
| `configureRedaction` | Configure global redaction patterns and keys |
|
|
315
345
|
| `flush` | Flush all pending log writes across all sinks |
|
|
316
346
|
| `createJsonFormatter` | Create a JSON formatter for structured output |
|
package/dist/index.d.ts
CHANGED
|
@@ -208,36 +208,42 @@ interface LoggerInstance {
|
|
|
208
208
|
* @param metadata - Optional structured metadata
|
|
209
209
|
*/
|
|
210
210
|
trace(message: string, metadata?: Record<string, unknown>): void;
|
|
211
|
+
trace(metadata: Record<string, unknown>, message: string): never;
|
|
211
212
|
/**
|
|
212
213
|
* Log at debug level (development debugging).
|
|
213
214
|
* @param message - Human-readable log message
|
|
214
215
|
* @param metadata - Optional structured metadata
|
|
215
216
|
*/
|
|
216
217
|
debug(message: string, metadata?: Record<string, unknown>): void;
|
|
218
|
+
debug(metadata: Record<string, unknown>, message: string): never;
|
|
217
219
|
/**
|
|
218
220
|
* Log at info level (normal operations).
|
|
219
221
|
* @param message - Human-readable log message
|
|
220
222
|
* @param metadata - Optional structured metadata
|
|
221
223
|
*/
|
|
222
224
|
info(message: string, metadata?: Record<string, unknown>): void;
|
|
225
|
+
info(metadata: Record<string, unknown>, message: string): never;
|
|
223
226
|
/**
|
|
224
227
|
* Log at warn level (unexpected but handled situations).
|
|
225
228
|
* @param message - Human-readable log message
|
|
226
229
|
* @param metadata - Optional structured metadata
|
|
227
230
|
*/
|
|
228
231
|
warn(message: string, metadata?: Record<string, unknown>): void;
|
|
232
|
+
warn(metadata: Record<string, unknown>, message: string): never;
|
|
229
233
|
/**
|
|
230
234
|
* Log at error level (failures requiring attention).
|
|
231
235
|
* @param message - Human-readable log message
|
|
232
236
|
* @param metadata - Optional structured metadata
|
|
233
237
|
*/
|
|
234
238
|
error(message: string, metadata?: Record<string, unknown>): void;
|
|
239
|
+
error(metadata: Record<string, unknown>, message: string): never;
|
|
235
240
|
/**
|
|
236
241
|
* Log at fatal level (unrecoverable failures).
|
|
237
242
|
* @param message - Human-readable log message
|
|
238
243
|
* @param metadata - Optional structured metadata
|
|
239
244
|
*/
|
|
240
245
|
fatal(message: string, metadata?: Record<string, unknown>): void;
|
|
246
|
+
fatal(metadata: Record<string, unknown>, message: string): never;
|
|
241
247
|
/**
|
|
242
248
|
* Get the current context metadata attached to this logger.
|
|
243
249
|
* @returns Copy of the logger's context object
|
|
@@ -353,7 +359,7 @@ interface FileSinkOptions {
|
|
|
353
359
|
/** Absolute path to the log file */
|
|
354
360
|
path: string;
|
|
355
361
|
/**
|
|
356
|
-
* Append to existing file or truncate
|
|
362
|
+
* Append to existing file or truncate before the first write.
|
|
357
363
|
* @defaultValue true
|
|
358
364
|
*/
|
|
359
365
|
append?: boolean;
|
|
@@ -471,8 +477,8 @@ declare function createJsonFormatter(): Formatter;
|
|
|
471
477
|
*/
|
|
472
478
|
declare function createPrettyFormatter(options?: PrettyFormatterOptions): Formatter;
|
|
473
479
|
/**
|
|
474
|
-
* Create a console sink that writes
|
|
475
|
-
* Info and below go to
|
|
480
|
+
* Create a console sink that writes via console methods.
|
|
481
|
+
* Info and below go to console.info/debug, warn and above go to console.warn/error.
|
|
476
482
|
*
|
|
477
483
|
* @param options - Console sink options
|
|
478
484
|
* @returns Sink configured for console output
|
|
@@ -535,4 +541,32 @@ declare function configureRedaction(config: GlobalRedactionConfig): void;
|
|
|
535
541
|
* ```
|
|
536
542
|
*/
|
|
537
543
|
declare function flush(): Promise<void>;
|
|
538
|
-
|
|
544
|
+
/**
|
|
545
|
+
* Resolve the log level from environment configuration.
|
|
546
|
+
*
|
|
547
|
+
* Precedence (highest wins):
|
|
548
|
+
* 1. `OUTFITTER_LOG_LEVEL` environment variable
|
|
549
|
+
* 2. Explicit `level` parameter
|
|
550
|
+
* 3. `OUTFITTER_ENV` environment profile defaults
|
|
551
|
+
* 4. `"info"` (default)
|
|
552
|
+
*
|
|
553
|
+
* @param level - Optional explicit log level (overridden by env var)
|
|
554
|
+
* @returns Resolved LogLevel
|
|
555
|
+
*
|
|
556
|
+
* @example
|
|
557
|
+
* ```typescript
|
|
558
|
+
* import { createLogger, resolveLogLevel } from "@outfitter/logging";
|
|
559
|
+
*
|
|
560
|
+
* // Auto-resolve from environment
|
|
561
|
+
* const logger = createLogger({
|
|
562
|
+
* name: "my-app",
|
|
563
|
+
* level: resolveLogLevel(),
|
|
564
|
+
* });
|
|
565
|
+
*
|
|
566
|
+
* // With OUTFITTER_ENV=development → "debug"
|
|
567
|
+
* // With OUTFITTER_LOG_LEVEL=error → "error" (overrides everything)
|
|
568
|
+
* // With nothing set → "info"
|
|
569
|
+
* ```
|
|
570
|
+
*/
|
|
571
|
+
declare function resolveLogLevel(level?: LogLevel): LogLevel;
|
|
572
|
+
export { resolveLogLevel, flush, createPrettyFormatter, createLogger, createJsonFormatter, createFileSink, createConsoleSink, createChildLogger, configureRedaction, Sink, RedactionConfig, PrettyFormatterOptions, LoggerInstance, LoggerConfig, LogRecord, LogLevel, GlobalRedactionConfig, Formatter, FileSinkOptions, DEFAULT_PATTERNS, ConsoleSinkOptions };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getEnvironment as _getEnvironment,
|
|
4
|
+
getEnvironmentDefaults as _getEnvironmentDefaults
|
|
5
|
+
} from "@outfitter/config";
|
|
3
6
|
var LEVEL_PRIORITY = {
|
|
4
7
|
trace: 0,
|
|
5
8
|
debug: 1,
|
|
@@ -215,12 +218,30 @@ function createChildLogger(parent, context) {
|
|
|
215
218
|
const parentContext = parent.getContext();
|
|
216
219
|
const mergedContext = { ...parentContext, ...context };
|
|
217
220
|
const childLogger = {
|
|
218
|
-
trace: (message, metadata) => parent.trace(message, {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
trace: (message, metadata) => parent.trace(message, {
|
|
222
|
+
...context,
|
|
223
|
+
...metadata
|
|
224
|
+
}),
|
|
225
|
+
debug: (message, metadata) => parent.debug(message, {
|
|
226
|
+
...context,
|
|
227
|
+
...metadata
|
|
228
|
+
}),
|
|
229
|
+
info: (message, metadata) => parent.info(message, {
|
|
230
|
+
...context,
|
|
231
|
+
...metadata
|
|
232
|
+
}),
|
|
233
|
+
warn: (message, metadata) => parent.warn(message, {
|
|
234
|
+
...context,
|
|
235
|
+
...metadata
|
|
236
|
+
}),
|
|
237
|
+
error: (message, metadata) => parent.error(message, {
|
|
238
|
+
...context,
|
|
239
|
+
...metadata
|
|
240
|
+
}),
|
|
241
|
+
fatal: (message, metadata) => parent.fatal(message, {
|
|
242
|
+
...context,
|
|
243
|
+
...metadata
|
|
244
|
+
}),
|
|
224
245
|
getContext: () => mergedContext,
|
|
225
246
|
setLevel: (level) => parent.setLevel(level),
|
|
226
247
|
addSink: (sink) => parent.addSink(sink),
|
|
@@ -287,20 +308,28 @@ function createPrettyFormatter(options) {
|
|
|
287
308
|
};
|
|
288
309
|
}
|
|
289
310
|
function createConsoleSink(options) {
|
|
290
|
-
const useColors = options?.colors ?? process.stdout
|
|
311
|
+
const useColors = options?.colors ?? (typeof process !== "undefined" ? Boolean(process.stdout?.isTTY) : false);
|
|
291
312
|
const formatter = createPrettyFormatter({ colors: useColors });
|
|
292
313
|
const sink = {
|
|
293
314
|
formatter,
|
|
294
315
|
write(record, formatted) {
|
|
295
316
|
const output = formatted ?? formatter.format(record);
|
|
296
|
-
const
|
|
297
|
-
`) ? output :
|
|
298
|
-
|
|
299
|
-
if (
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
process.stdout.write(outputWithNewline);
|
|
317
|
+
const outputLine = output.endsWith(`
|
|
318
|
+
`) ? output.slice(0, -1) : output;
|
|
319
|
+
const runtimeConsole = globalThis["console"];
|
|
320
|
+
if (record.level === "fatal" || record.level === "error") {
|
|
321
|
+
runtimeConsole.error(outputLine);
|
|
322
|
+
return;
|
|
303
323
|
}
|
|
324
|
+
if (record.level === "warn") {
|
|
325
|
+
runtimeConsole.warn(outputLine);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (record.level === "debug" || record.level === "trace") {
|
|
329
|
+
runtimeConsole.debug(outputLine);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
runtimeConsole.info(outputLine);
|
|
304
333
|
}
|
|
305
334
|
};
|
|
306
335
|
registeredSinks.add(sink);
|
|
@@ -311,9 +340,7 @@ function createFileSink(options) {
|
|
|
311
340
|
const buffer = [];
|
|
312
341
|
const { path } = options;
|
|
313
342
|
const append = options.append ?? true;
|
|
314
|
-
|
|
315
|
-
writeFileSync(path, "");
|
|
316
|
-
}
|
|
343
|
+
let cachedContent = append ? null : "";
|
|
317
344
|
const sink = {
|
|
318
345
|
formatter,
|
|
319
346
|
write(record, formatted) {
|
|
@@ -327,9 +354,14 @@ function createFileSink(options) {
|
|
|
327
354
|
if (buffer.length > 0) {
|
|
328
355
|
const content = buffer.join("");
|
|
329
356
|
buffer.length = 0;
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
357
|
+
if (append) {
|
|
358
|
+
const file = Bun.file(path);
|
|
359
|
+
const existing = await file.exists() ? await file.text() : "";
|
|
360
|
+
await Bun.write(path, existing + content);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
cachedContent = (cachedContent ?? "") + content;
|
|
364
|
+
await Bun.write(path, cachedContent);
|
|
333
365
|
}
|
|
334
366
|
}
|
|
335
367
|
};
|
|
@@ -359,7 +391,43 @@ async function flush() {
|
|
|
359
391
|
}
|
|
360
392
|
await Promise.all(flushPromises);
|
|
361
393
|
}
|
|
394
|
+
var ENV_LEVEL_MAP = {
|
|
395
|
+
trace: "trace",
|
|
396
|
+
debug: "debug",
|
|
397
|
+
info: "info",
|
|
398
|
+
notice: "info",
|
|
399
|
+
warn: "warn",
|
|
400
|
+
warning: "warn",
|
|
401
|
+
error: "error",
|
|
402
|
+
critical: "fatal",
|
|
403
|
+
alert: "fatal",
|
|
404
|
+
emergency: "fatal",
|
|
405
|
+
fatal: "fatal",
|
|
406
|
+
silent: "silent"
|
|
407
|
+
};
|
|
408
|
+
function resolveLogLevel(level) {
|
|
409
|
+
const envLogLevel = process.env["OUTFITTER_LOG_LEVEL"];
|
|
410
|
+
if (envLogLevel !== undefined) {
|
|
411
|
+
const mapped = ENV_LEVEL_MAP[envLogLevel];
|
|
412
|
+
if (mapped !== undefined) {
|
|
413
|
+
return mapped;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if (level !== undefined) {
|
|
417
|
+
return level;
|
|
418
|
+
}
|
|
419
|
+
const env = _getEnvironment();
|
|
420
|
+
const defaults = _getEnvironmentDefaults(env);
|
|
421
|
+
if (defaults.logLevel !== null) {
|
|
422
|
+
const mapped = ENV_LEVEL_MAP[defaults.logLevel];
|
|
423
|
+
if (mapped !== undefined) {
|
|
424
|
+
return mapped;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return "info";
|
|
428
|
+
}
|
|
362
429
|
export {
|
|
430
|
+
resolveLogLevel,
|
|
363
431
|
flush,
|
|
364
432
|
createPrettyFormatter,
|
|
365
433
|
createLogger,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outfitter/logging",
|
|
3
3
|
"description": "Structured logging via logtape with redaction support for Outfitter",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
"clean": "rm -rf dist"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@outfitter/
|
|
30
|
+
"@outfitter/config": "0.3.0",
|
|
31
|
+
"@outfitter/contracts": "0.2.0",
|
|
31
32
|
"@logtape/logtape": "^2.0.0"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|