diff --git a/knip.json b/knip.json index 0bbb15ea..54b3bee5 100644 --- a/knip.json +++ b/knip.json @@ -2,8 +2,6 @@ "$schema": "https://unpkg.com/knip@6/schema.json", "entry": ["drizzle.config.ts", "src/lib/editor/grammar/patch-generated.mjs"], "ignoreDependencies": [ - // Loaded dynamically by pino via a string transport target, not a static import. - "pino-pretty", // Invoked as a standalone CLI in docker/Dockerfile, not referenced from source. "@cyclonedx/cyclonedx-npm" ], diff --git a/src/lib/server/logging/logger.ts b/src/lib/server/logging/logger.ts index 28cbdada..e18ff4b8 100644 --- a/src/lib/server/logging/logger.ts +++ b/src/lib/server/logging/logger.ts @@ -1,3 +1,4 @@ +import { createRequire } from 'node:module'; import pino from 'pino'; import { redactionPaths, redactionCensor } from './redaction.js'; import type { LoggingConfig } from './types.js'; @@ -8,6 +9,7 @@ import type { LoggingConfig } from './types.js'; // This is safe because the file lives under $lib/server/ which SvelteKit // already prevents from being imported client-side. const dev = process.env.NODE_ENV !== 'production'; +const require = createRequire(import.meta.url); function resolveConfig(): LoggingConfig { const level = process.env.LOG_LEVEL ?? (dev ? 'trace' : 'info'); @@ -37,19 +39,29 @@ function createLogger(): pino.Logger { const targets: pino.TransportTargetOptions[] = []; if (config.pretty) { - targets.push({ target: 'pino-pretty', level: config.level }); + // Resolve to an absolute path: since pino 10.4.0 the caller is detected via + // util.getCallSites(), which returns an empty script name for modules + // evaluated by Vite's SSR module runner, so pino cannot resolve 'pino-pretty'. + targets.push({ target: require.resolve('pino-pretty') }); } else { - targets.push({ target: 'pino/file', level: config.level }); + targets.push({ target: 'pino/file' }); } if (config.jsonLogFile) { targets.push({ target: 'pino/file', - options: { destination: config.jsonLogFile, mkdir: true }, - level: config.level + options: { destination: config.jsonLogFile, mkdir: true } }); } + // A per-target `level` routes logs through pino's multistream, which warns about + // the custom `formatters.level` above (PINO_TRANSPORT_MULTI_TARGETS_LEVEL_FORMATTER). + // A single target needs no level as the logger already filters; multiple targets + // do, as multistream defaults targets without one to 'info'. + if (targets.length > 1) { + for (const target of targets) target.level = config.level; + } + return pino(options, pino.transport({ targets })); }