Spaces:
Running
Running
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}`);
}
|