logger.ts 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. /**
  2. * Structured logging via Winston.
  3. *
  4. * One root logger is created at module load. Each domain gets a child logger
  5. * that adds a `domain` field to every log entry so log lines can be filtered
  6. * and routed independently.
  7. *
  8. * A custom format reads the current request context from AsyncLocalStorage and
  9. * injects `requestId` into every log entry automatically — no manual threading needed.
  10. *
  11. * Optional env vars:
  12. * LOG_LEVEL — winston level (default: "info"; use "debug" for verbose output)
  13. * NODE_ENV — when "development", logs are pretty-printed; otherwise JSON
  14. */
  15. import { createLogger, format, transports } from 'winston';
  16. import { getContext } from '$lib/server/context';
  17. import { env } from '$env/dynamic/private';
  18. const level = env.LOG_LEVEL ?? 'info';
  19. // Injects requestId from AsyncLocalStorage into every log entry.
  20. const requestContextFormat = format((info) => {
  21. const ctx = getContext();
  22. if (ctx) info['requestId'] = ctx.requestId;
  23. return info;
  24. });
  25. const productionFormat = format.combine(
  26. requestContextFormat(),
  27. format.timestamp(),
  28. format.errors({ stack: true }),
  29. format.json(),
  30. );
  31. const developmentFormat = format.combine(
  32. requestContextFormat(),
  33. format.timestamp({ format: 'HH:mm:ss' }),
  34. format.errors({ stack: true }),
  35. format.colorize(),
  36. format.printf(({ timestamp, level, message, domain, requestId, ...rest }) => {
  37. const domainTag = domain ? `[${domain}] ` : '';
  38. const rid = requestId ? `(${requestId}) ` : '';
  39. const extra = Object.keys(rest).length ? ' ' + JSON.stringify(rest) : '';
  40. return `${timestamp} ${level} ${domainTag}${rid}${message}${extra}`;
  41. }),
  42. );
  43. const root = createLogger({
  44. level,
  45. format: env.NODE_ENV === 'development' ? developmentFormat : productionFormat,
  46. transports: [new transports.Console()],
  47. });
  48. const child = (domain: string) => root.child({ domain });
  49. // --- Domain loggers ---
  50. const serverLogger = child('server');
  51. const authLogger = child('auth');
  52. const gitLogger = child('git');
  53. const k8sLogger = child('k8s');
  54. export { authLogger, gitLogger, k8sLogger, root as logger, serverLogger };