Add the ability to log to loki

This commit is contained in:
fuomag9
2025-11-12 22:07:51 +01:00
parent 6fa1eb756b
commit ec580d2385
6 changed files with 271 additions and 9 deletions

View File

@@ -3,7 +3,13 @@ import { join } from "node:path";
import crypto from "node:crypto";
import db, { nowIso } from "./db";
import { config } from "./config";
import { getCloudflareSettings, getGeneralSettings, getMetricsSettings, setSetting } from "./settings";
import {
getCloudflareSettings,
getGeneralSettings,
getLoggingSettings,
getMetricsSettings,
setSetting
} from "./settings";
import {
accessListEntries,
certificates,
@@ -782,6 +788,60 @@ async function buildTlsAutomation(
};
}
function buildLoggingApp(loggingSettings: NonNullable<Awaited<ReturnType<typeof getLoggingSettings>>>) {
if (!loggingSettings.lokiUrl) {
return null;
}
const lokiWriterConfig: Record<string, unknown> = {
output: "loki",
url: loggingSettings.lokiUrl
};
// Add basic auth if provided
if (loggingSettings.lokiUsername && loggingSettings.lokiPassword) {
lokiWriterConfig.username = loggingSettings.lokiUsername;
lokiWriterConfig.password = loggingSettings.lokiPassword;
}
// Add custom labels if provided
if (loggingSettings.labels && Object.keys(loggingSettings.labels).length > 0) {
lokiWriterConfig.labels = loggingSettings.labels;
}
return {
logs: {
// Access log for all HTTP requests
access: {
encoder: {
format: "json",
message_key: "msg",
level_key: "level",
time_key: "ts",
name_key: "logger"
},
writer: lokiWriterConfig,
level: "INFO",
include: ["http.log.access.*"]
},
// Default log for errors and other messages
default: {
encoder: {
format: "json",
message_key: "msg",
level_key: "level",
time_key: "ts",
name_key: "logger",
caller_key: "caller",
stacktrace_key: "stacktrace"
},
writer: lokiWriterConfig,
level: "WARN"
}
}
};
}
async function buildCaddyDocument() {
const [proxyHostRecords, redirectHostRecords, deadHostRecords, certRows, accessListEntryRecords] = await Promise.all([
db
@@ -930,6 +990,11 @@ async function buildCaddyDocument() {
const metricsEnabled = metricsSettings?.enabled ?? false;
const metricsPort = metricsSettings?.port ?? 9090;
// Check if logging should be enabled
const loggingSettings = await getLoggingSettings();
const loggingEnabled = loggingSettings?.enabled ?? false;
const loggingApp = loggingEnabled && loggingSettings ? buildLoggingApp(loggingSettings) : null;
const servers: Record<string, unknown> = {};
// Main HTTP/HTTPS server for proxy hosts
@@ -940,7 +1005,9 @@ async function buildCaddyDocument() {
// Only disable automatic HTTPS if we have TLS automation policies
// This allows Caddy to handle HTTP-01 challenges for managed certificates
...(tlsApp ? {} : { automatic_https: { disable: true } }),
...(hasTls ? { tls_connection_policies: tlsConnectionPolicies } : {})
...(hasTls ? { tls_connection_policies: tlsConnectionPolicies } : {}),
// Enable access logging if configured
...(loggingEnabled ? { logs: { default_logger_name: "access" } } : {})
};
}
@@ -972,7 +1039,8 @@ async function buildCaddyDocument() {
},
apps: {
...httpApp,
...(tlsApp ? { tls: tlsApp } : {})
...(tlsApp ? { tls: tlsApp } : {}),
...(loggingApp ? { logging: loggingApp } : {})
}
};
}

View File

@@ -26,6 +26,14 @@ export type MetricsSettings = {
port?: number; // Port to expose metrics on (default: 9090, separate from admin API)
};
export type LoggingSettings = {
enabled: boolean;
lokiUrl?: string; // URL of Loki instance (e.g., http://loki:3100)
lokiUsername?: string; // Optional username for Loki authentication
lokiPassword?: string; // Optional password for Loki authentication
labels?: Record<string, string>; // Optional custom labels for logs
};
export async function getSetting<T>(key: string): Promise<SettingValue<T>> {
const setting = await db.query.settings.findFirst({
where: (table, { eq }) => eq(table.key, key)
@@ -94,3 +102,11 @@ export async function getMetricsSettings(): Promise<MetricsSettings | null> {
export async function saveMetricsSettings(settings: MetricsSettings): Promise<void> {
await setSetting("metrics", settings);
}
export async function getLoggingSettings(): Promise<LoggingSettings | null> {
return await getSetting<LoggingSettings>("logging");
}
export async function saveLoggingSettings(settings: LoggingSettings): Promise<void> {
await setSetting("logging", settings);
}