File size: 1,417 Bytes
bd28470
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import pino from "pino";
import { getEnv } from "../config/env";

// PII fields that will be redacted in logs
const PII_FIELDS = ["email", "full_name", "first_name", "last_name", "phone", "linkedin_url"];

function redactPii(obj: Record<string, unknown>): Record<string, unknown> {
  const result: Record<string, unknown> = {};
  for (const [key, value] of Object.entries(obj)) {
    if (PII_FIELDS.includes(key) && typeof value === "string") {
      // Show first 3 chars + *** e.g. "joh***"
      result[key] = value.length > 3 ? `${value.slice(0, 3)}***` : "***";
    } else if (value && typeof value === "object" && !Array.isArray(value)) {
      result[key] = redactPii(value as Record<string, unknown>);
    } else {
      result[key] = value;
    }
  }
  return result;
}

const env = getEnv();

export const logger = pino({
  level: env.LOG_LEVEL,
  transport:
    env.NODE_ENV === "development"
      ? { target: "pino-pretty", options: { colorize: true } }
      : undefined,
  serializers: {
    // Auto-redact PII in any "contact" or "data" field
    contact: (val: Record<string, unknown>) => redactPii(val),
    data: (val: Record<string, unknown>) => redactPii(val),
  },
});

// Convenience method for audit-safe logging
export function auditLog(action: string, entity: string, details: Record<string, unknown>) {
  logger.info({ action, entity, details: redactPii(details) }, `[AUDIT] ${action}`);
}