UNPKG

lynkr

Version:

Self-hosted LLM gateway and tier-routing proxy for Claude Code, Cursor, and Codex. Routes across Ollama, AWS Bedrock, OpenRouter, Databricks, Azure OpenAI, llama.cpp, and LM Studio with prompt caching, MCP tools, and 60-80% cost savings.

115 lines (106 loc) 3.1 kB
const pino = require("pino"); const fs = require("fs"); const path = require("path"); const config = require("../config"); const { createOversizedErrorStream } = require("./oversized-error-stream"); /** * Application logger using Pino * * Standard Network Logging Fields: * When logging network requests/responses, use these consistent field names: * * - destinationUrl: string - Full URL being requested (e.g., "https://api.example.com/v1/endpoint") * - destinationHostname: string - Hostname only (e.g., "api.example.com") * - destinationIp: string - Resolved IP address (logged by DNS logger at debug level) * - ipFamily: number - IP version (4 or 6) - logged by DNS logger * - protocol: string - Protocol used ("http" or "https") * - status: number - HTTP status code * - provider: string - Service/provider label (e.g., "OpenAI", "HTTP", "HTTPS") * - duration: number - Request duration in milliseconds * * DNS Resolution Logging: * DNS resolution is logged at debug level via the dns-logger module. * To see DNS logs, set LOG_LEVEL=debug. DNS logs correlate with application * logs via the destinationHostname field. * * Example DNS log: * { * "level": "debug", * "provider": "HTTPS", * "hostname": "api.openai.com", * "resolvedIp": "104.18.23.45", * "ipFamily": 4, * "duration": 23, * "msg": "DNS resolution completed" * } * * Example API request log: * { * "level": "debug", * "provider": "OpenAI", * "status": 200, * "destinationUrl": "https://api.openai.com/v1/chat/completions", * "destinationHostname": "api.openai.com", * "responseLength": 1523, * "msg": "OpenAI API response" * } */ // Create array of streams for multistream setup const streams = []; // Main console output stream streams.push({ level: config.logger.level, stream: config.env === "development" ? pino.transport({ target: "pino-pretty", options: { translateTime: "SYS:standard", ignore: "pid,hostname", colorize: true, }, }) : process.stdout, }); // File rotation stream (if enabled via LOG_FILE_ENABLED=true) if (config.logger.file?.enabled) { const fileConfig = config.logger.file; // Ensure log directory exists const logDir = path.dirname(fileConfig.path); fs.mkdirSync(logDir, { recursive: true }); streams.push({ level: fileConfig.level, stream: pino.transport({ target: "pino-roll", options: { file: fileConfig.path, frequency: fileConfig.frequency, limit: { count: fileConfig.maxFiles }, mkdir: true, }, }), }); } // Oversized error stream (if enabled) if (config.oversizedErrorLogging?.enabled) { streams.push({ level: "warn", // Only capture WARN and ERROR stream: createOversizedErrorStream(config.oversizedErrorLogging), }); } // Create logger with multistream const logger = pino( { level: config.logger.level, name: "claude-backend", base: { env: config.env, }, redact: { paths: ["req.headers.authorization", "req.headers.cookie"], censor: "***redacted***", }, }, pino.multistream(streams), ); module.exports = logger;