UNPKG

logsdx

Version:

log streaming with dx on the 🧠

137 lines (124 loc) • 3.38 kB
import fs from "fs"; import JSON5 from "json5"; import { type LineParser, type LineParseResult, type JSONRule, type LogLevel, } from "@/src/types"; import { logger } from "@/src/utils/logger"; // Default rules for JSON parsing export const DEFAULT_JSON_RULES: JSONRule[] = [ { match: "\\{.*\\}", lang: "json", level: "info", meta: { service: "service", timestamp: "timestamp", message: "message", level: "level", }, }, ]; /** * Maps various log level formats to our standard log levels * @param level The log level to map * @returns A standardized log level */ export function mapLogLevel(level: string): LogLevel { const levelMap: Record<string, LogLevel> = { // Standard levels debug: "debug", info: "info", warn: "warn", warning: "warn", error: "error", err: "error", success: "success", trace: "trace", // Common variations fatal: "error", critical: "error", notice: "info", verbose: "debug", silly: "debug", // Status-based levels fail: "error", failed: "error", failure: "error", pending: "warn", in_progress: "info", completed: "success", }; const normalizedLevel = level.toLowerCase().trim(); return levelMap[normalizedLevel] || "info"; } /** * Loads JSON rules from a file or uses default rules * @param filePath Optional path to a custom rules file * @returns A line parser function */ export async function loadJsonRules(filePath?: string): Promise<LineParser> { let rules: JSONRule[] = DEFAULT_JSON_RULES; // If a file path is provided, try to load custom rules if (filePath) { try { const data = fs.readFileSync(filePath, "utf-8"); const customRules = JSON5.parse(data); if (Array.isArray(customRules) && customRules.length > 0) { rules = customRules; } } catch (error) { logger.warn( `Failed to load custom JSON rules: ${JSON5.stringify(error)}`, ); logger.info("Using default JSON rules instead"); } } // Create a direct JSON parser using JSON5 return (line: string): LineParseResult | undefined => { try { // Check if the line looks like JSON if (!line.trim().startsWith("{") || !line.trim().endsWith("}")) { return undefined; } // Parse the JSON using JSON5 const json = JSON5.parse(line); // Extract common log fields const result: LineParseResult = { level: mapLogLevel( json.level || json.status || json.severity || "info", ), timestamp: json.timestamp || json.time || json.date || json["@timestamp"], message: json.message || json.msg || json.log || json.text, language: "json", }; // Add all other fields as metadata for (const [key, value] of Object.entries(json)) { if ( ![ "level", "status", "severity", "timestamp", "time", "date", "@timestamp", "message", "msg", "log", "text", ].includes(key) ) { result[key] = value; } } return result; } catch (error) { // If parsing fails, return undefined to let other parsers handle it return undefined; } }; }