Files
xevion.dev/web/src/lib/logger.ts
Xevion a6cc0b8e66 feat: add request ID propagation from Rust to Bun with structured logging
- Forward x-request-id header through proxy and API calls
- Store RequestId in request extensions for downstream access
- Add AsyncLocalStorage context to correlate logs across async boundaries
- Improve migration logging to show pending changes before applying
- Reduce noise in logs (common OG images, health checks)
2026-01-13 16:42:14 -06:00

73 lines
1.9 KiB
TypeScript

import { dev } from "$app/environment";
import { configure, getConsoleSink, type LogRecord } from "@logtape/logtape";
import { requestContext } from "$lib/server/context";
interface RailwayLogEntry {
timestamp: string;
level: string;
message: string;
target: string;
[key: string]: unknown;
}
function railwayFormatter(record: LogRecord): string {
const ctx = requestContext.getStore();
const categoryTarget = record.category.join(":");
const entry: RailwayLogEntry = {
timestamp: new Date().toISOString(),
level: record.level.toLowerCase(),
message: record.message.join(" "),
target: categoryTarget ? `bun:${categoryTarget}` : "bun",
...(ctx?.requestId && { req_id: ctx.requestId }),
};
if (record.properties && Object.keys(record.properties).length > 0) {
Object.assign(entry, record.properties);
}
return JSON.stringify(entry) + "\n";
}
export async function initLogger() {
const useJsonLogs =
process.env.LOG_JSON === "true" || process.env.LOG_JSON === "1";
const logLevel = (process.env.LOG_LEVEL?.toLowerCase() ??
(dev ? "debug" : "info")) as "debug" | "info" | "warning" | "error";
const jsonSink = (record: LogRecord) => {
process.stdout.write(railwayFormatter(record));
};
const consoleSink = getConsoleSink();
try {
await configure({
sinks: {
json: useJsonLogs ? jsonSink : consoleSink,
console: useJsonLogs ? jsonSink : consoleSink,
},
filters: {},
loggers: [
{
category: ["logtape", "meta"],
lowestLevel: "warning",
sinks: [useJsonLogs ? "json" : "console"],
},
{
category: [],
lowestLevel: logLevel,
sinks: [useJsonLogs ? "json" : "console"],
},
],
});
} catch (error) {
if (
error instanceof Error &&
error.message.includes("Already configured")
) {
return;
}
throw error;
}
}