diff --git a/README.md b/README.md index b879cb2..52b0e9a 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Features: * Clean, opinionated log output format powered by [pino-pretty](https://github.com/pinojs/pino-pretty): * Colorized output when stream is TTY and supports it * Automatically [serialize passed objects and Errors](#serializing-objects-and-errors), including [Error Cause](https://github.com/tc39/proposal-error-cause) + * Automatically redact current working directory from log output * [Bring-Your-Own settings](#additional-app-logger-configuration) * Add or use your own streams/[transports](https://getpino.io/#/docs/transports?id=known-transports) for destinations * All pino-pretty configs are exposed and extensible @@ -193,9 +194,9 @@ export interface FileOptions { ```ts export interface LoggerAppExtras { /** - * Additional pino-pretty options that are applied to the built-in console/log streams + * Additional pino-pretty options that are applied to the built-in console/log streams (including CWD redaction) * */ - pretty?: PrettyOptions + pretty?: PrettyOptionsExtra /** * Additional logging destinations to use alongside the built-in console/log stream. These can be any created by buildDestination* functions or other Pino Transports * */ diff --git a/src/destinations.ts b/src/destinations.ts index 70bd402..f88c96c 100644 --- a/src/destinations.ts +++ b/src/destinations.ts @@ -7,7 +7,7 @@ import { } from "./types.js"; import {DestinationStream, pino, destination} from "pino"; import {build} from "pino-pretty" -import {PRETTY_OPTS_CONSOLE, PRETTY_OPTS_FILE} from "./pretty.js"; +import {PRETTY_OPTS_CONSOLE, PRETTY_OPTS_FILE, prettyOptsConsoleFactory, prettyOptsFileFactory} from "./pretty.js"; import {fileOrDirectoryIsWriteable} from "./util.js"; import path from "path"; import {ErrorWithCause} from "pony-cause"; @@ -94,7 +94,7 @@ export const buildDestinationFile = (level: LogLevel | false, options: FileDesti return { level: level, - stream: build({...PRETTY_OPTS_FILE, ...rest, destination: dest}) + stream: build({...prettyOptsFileFactory(rest), ...rest, destination: dest}) }; } catch (e: any) { throw new ErrorWithCause('WILL NOT write to file due to an error while trying to access the specified directory', {cause: e as Error}); @@ -109,7 +109,7 @@ export const buildDestinationFile = (level: LogLevel | false, options: FileDesti export const buildDestinationStream = (level: LogLevel, options: StreamDestination): LogLevelStreamEntry => { return { level: level, - stream: build({...PRETTY_OPTS_CONSOLE, ...options}) + stream: build({...prettyOptsConsoleFactory(options)}) } } diff --git a/src/factory.ts b/src/factory.ts index 9481c6f..44192a7 100644 --- a/src/factory.ts +++ b/src/factory.ts @@ -3,7 +3,9 @@ import { PRETTY_OPTS_FILE, //PRETTY_LEVELS, //PRETTY_LEVELS_STR, - prettyOptsFactory + prettyOptsFactory, + prettyOptsFileFactory, + prettyOptsConsoleFactory } from "./pretty.js"; import { buildDestinationStream, @@ -24,6 +26,8 @@ export { //PRETTY_LEVELS_STR, PRETTY_ISO8601, prettyOptsFactory, + prettyOptsFileFactory, + prettyOptsConsoleFactory, buildDestinationStream, buildDestinationStdout, buildDestinationStderr, diff --git a/src/index.ts b/src/index.ts index 50da2a7..0db36f8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,7 +6,8 @@ import { LogData, LogLevel, LOG_LEVELS, - LoggerAppExtras + LoggerAppExtras, + PrettyOptionsExtra } from './types.js' import { @@ -23,7 +24,8 @@ export type { LogOptions, LogData, LogLevel, - LoggerAppExtras + LoggerAppExtras, + PrettyOptionsExtra } export { diff --git a/src/pretty.ts b/src/pretty.ts index 167d48f..bd20a1e 100644 --- a/src/pretty.ts +++ b/src/pretty.ts @@ -1,28 +1,31 @@ import {PrettyOptions} from "pino-pretty"; import {CWD} from "./util.js"; -import {PRETTY_COLORS, PRETTY_COLORS_STR, PRETTY_LEVELS, PRETTY_LEVELS_STR} from "./types.js"; +import {PRETTY_COLORS, PRETTY_COLORS_STR, PRETTY_LEVELS, PRETTY_LEVELS_STR, PrettyOptionsExtra} from "./types.js"; /** * Builds the opinionated `@foxxmd/logging` defaults for pino-pretty `PrettyOptions` and merges them with an optional user-provided `PrettyOptions` object * */ -export const prettyOptsFactory = (opts: Omit = {}): PrettyOptions => { +export const prettyOptsFactory = (opts: PrettyOptionsExtra = {}): PrettyOptions => { const { //customLevels = {}, customColors = {}, + redactCwd = true, ...rest } = opts; + let redactFunc: (content: string) => string; + if(redactCwd) { + redactFunc = (content) => content.replaceAll(CWD, 'CWD'); + } else { + redactFunc = (content) => content; + } + return { messageFormat: (log, messageKey, levelLabel, {colors}) => { const labels: string[] = log.labels as string[] ?? []; - const leaf = log.leaf as string | undefined; - const nodes = labels; - if (leaf !== null && leaf !== undefined && !nodes.includes(leaf)) { - nodes.push(leaf); - } - const labelContent = nodes.length === 0 ? '' : `${nodes.map((x: string) => colors.blackBright(`[${x}]`)).join(' ')} `; - const msg = (log[messageKey] as string).replaceAll(CWD, 'CWD'); - const stackTrace = log.err !== undefined ? `\n${(log.err as any).stack.replaceAll(CWD, 'CWD')}` : ''; + const labelContent = labels.length === 0 ? '' : `${labels.map((x: string) => colors.blackBright(`[${x}]`)).join(' ')} `; + const msg = redactFunc((log[messageKey] as string)); + const stackTrace = log.err !== undefined ? redactFunc(`\n${(log.err as any).stack}`) : ''; return `${labelContent}${msg}${stackTrace}`; }, hideObject: false, @@ -69,18 +72,36 @@ const buildColors = (userColors: PrettyOptions['customColors'] = {}): PrettyOpti return colors; } +const PRETTY_OPTS_CONSOLE_DEFAULTS: PrettyOptions = {sync: true}; + /** - * Pre-defined pino-pretty `PrettyOptions` for use with console/stream output + * Generate pino-pretty `PrettyOptions` for use with console/stream output * * @source * */ -export const PRETTY_OPTS_CONSOLE: PrettyOptions = prettyOptsFactory({sync: true}) +export const prettyOptsConsoleFactory = (opts: PrettyOptionsExtra = {}): PrettyOptions => prettyOptsFactory({...opts, ...PRETTY_OPTS_CONSOLE_DEFAULTS}); /** - * Pre-defined pino-pretty `PrettyOptions` for use with file output + * Pre-defined pino-pretty `PrettyOptions` for use with console/stream output * * @source * */ -export const PRETTY_OPTS_FILE: PrettyOptions = prettyOptsFactory({ +export const PRETTY_OPTS_CONSOLE: PrettyOptions = prettyOptsConsoleFactory(); + +const PRETTY_OPTS_FILE_DEFAULTS: PrettyOptions = { colorize: false, sync: false, -}); +} + +/** + * Generate pino-pretty `PrettyOptions` for use with file output + * + * @source + * */ +export const prettyOptsFileFactory = (opts: PrettyOptionsExtra = {}): PrettyOptions => prettyOptsFactory({...opts, ...PRETTY_OPTS_FILE_DEFAULTS}); + +/** + * Pre-defined pino-pretty `PrettyOptions` for use with file output + * + * @source + * */ +export const PRETTY_OPTS_FILE: PrettyOptions = prettyOptsFileFactory(); diff --git a/src/types.ts b/src/types.ts index 85ba28a..899cf93 100644 --- a/src/types.ts +++ b/src/types.ts @@ -124,8 +124,8 @@ export interface FileLogOptionsStrong extends FileLogOptions { export type FileLogOptionsParsed = (Omit & {level: false}) | FileLogOptionsStrong -export type FileDestination = Omit & FileOptionsParsed; -export type StreamDestination = Omit & {destination: number | DestinationStream | NodeJS.WritableStream}; +export type FileDestination = Omit & FileOptionsParsed; +export type StreamDestination = Omit & {destination: number | DestinationStream | NodeJS.WritableStream}; export type LogOptionsParsed = Omit, 'file'> & { file: FileLogOptionsParsed } @@ -133,7 +133,7 @@ export interface LoggerAppExtras { /** * Additional pino-pretty options that are applied to the built-in console/log streams * */ - pretty?: PrettyOptions + pretty?: PrettyOptionsExtra /** * Additional logging destinations to use alongside the built-in console/log stream. These can be any created by `buildDestination*` functions or other [Pino Transports](https://getpino.io/#/docs/transports?id=known-transports) * */ @@ -159,6 +159,22 @@ export const CUSTOM_LEVELS: LoggerOptions<"verbose" | "log">['customLevels'] = { * */ export type PinoLoggerOptions = Omit +/** + * pino-pretty options and additional options specific to how @foxxmd/logging uses pino-pretty + * */ +export interface PrettyOptionsExtra extends Omit { + /** + * Control whether the current working directory should be replaced with 'CWD' in log output + * + * Useful for eliminating noisy parent paths that aren't relevant during debugging -- or to protect user privacy. + * + * **NOTE:** Only applies to log message and errors. If you need to redact arbitrary properties you should use pino's [`redact`](https://getpino.io/#/docs/api?id=redact-array-object) or pino-pretty's [`customPrettifiers`](https://github.com/pinojs/pino-pretty?tab=readme-ov-file#options) + * + * @default true + * */ + redactCwd?: boolean +} + /** * Additional levels included in @foxxmd/logging as an object *