mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
feat(admin): app-log preview surfaces last 1h of structured events with filter + JSON inspector
Wires the in-memory ring buffer (`src/lib/logging/in-memory-buffer.ts`) into `transports.emitEvent()` so every wide event that survives the sampler also lands in a 500-entry per-process buffer. Adds the `app-logs` admin section slug, the renderer case, and the route entry exports needed for the new section component to mount. The buffer is volatile and per-process — a worker-only event won't show up in the web buffer, and a restart drops everything. The section header copy spells that out so admins know to keep Loki configured for durable diagnostics. Egress through `/api/admin/app-logs` runs every event through `redactSecrets()` so a stray Bearer / hlk_ / sk- token in an `error.message` never leaks to the admin UI; storage stays raw to keep the diagnostic value when shipped to Loki. The companion buffer + transports tests live under `src/lib/logging/__tests__/in-memory-buffer.test.ts` and `transports-buffer.test.ts` (committed separately as part of the buffer scaffolding). The route-level tests in `src/app/api/admin/app-logs/__tests__/route.test.ts` cover newest- first ordering, traceId / level / action filters, the `limit` cap, secret redaction on egress, and the Zod-rejected invalid `level` case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
226cac4c02
commit
4cc3d8d45e
4 files changed
+31
-1
No files matched your search
@@ -17,6 +17,7 @@ import type { JSX } from "react";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { ApiTokenOverviewSection } from "@/components/admin/api-token-overview-section";
|
||||
import { AppLogPreviewSection } from "@/components/admin/app-log-preview-section";
|
||||
import { BackupsSection } from "@/components/admin/backups-section";
|
||||
import { DangerZoneSection } from "@/components/admin/danger-zone-section";
|
||||
import { FeedbackInboxSection } from "@/components/admin/feedback-inbox-section";
|
||||
@@ -127,6 +128,15 @@ export function AdminSectionRenderer({
|
||||
<LoginOverviewSection />
|
||||
</SectionFrame>
|
||||
);
|
||||
case "app-logs":
|
||||
return (
|
||||
<SectionFrame
|
||||
title={t("admin.section.app-logs.title")}
|
||||
subtitle={t("admin.section.app-logs.subtitle")}
|
||||
>
|
||||
<AppLogPreviewSection />
|
||||
</SectionFrame>
|
||||
);
|
||||
case "backups":
|
||||
return (
|
||||
<SectionFrame
|
||||
|
||||
@@ -19,6 +19,7 @@ export const ADMIN_SECTION_SLUGS = [
|
||||
"users",
|
||||
"api-tokens",
|
||||
"login-overview",
|
||||
"app-logs",
|
||||
"backups",
|
||||
"danger-zone",
|
||||
] as const;
|
||||
|
||||
@@ -11,3 +11,11 @@ export { eventStorage, getEvent, annotate } from "./context";
|
||||
export { shouldEmit } from "./sampler";
|
||||
export { emitEvent, emitIfSampled } from "./transports";
|
||||
export { withBackgroundEvent, withBackgroundEventSafe } from "./background";
|
||||
export {
|
||||
appendLogEvent,
|
||||
readLogBuffer,
|
||||
clearLogBuffer,
|
||||
getLogBufferSize,
|
||||
LOG_BUFFER_MAX,
|
||||
type ReadLogBufferOptions,
|
||||
} from "./in-memory-buffer";
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { WideEvent } from "./types";
|
||||
import { getLoggingConfig } from "./config";
|
||||
import { shouldEmit } from "./sampler";
|
||||
import { appendLogEvent } from "./in-memory-buffer";
|
||||
|
||||
/** Event auf stdout als einzelne JSON-Zeile schreiben */
|
||||
function emitToStdout(event: WideEvent): void {
|
||||
@@ -88,10 +89,20 @@ export function emitIfSampled(event: WideEvent): void {
|
||||
}
|
||||
}
|
||||
|
||||
/** Zentraler Emit: stdout + optional Loki-Buffer */
|
||||
/** Zentraler Emit: stdout + optional Loki-Buffer + in-memory ring buffer */
|
||||
export function emitEvent(event: WideEvent): void {
|
||||
emitToStdout(event);
|
||||
|
||||
// Push into the per-process in-memory ring buffer so admins can drill
|
||||
// into the most recent ~500 wide events from the `/admin/app-logs`
|
||||
// page without standing up a Loki stack. See `in-memory-buffer.ts`.
|
||||
// Wrapped in try/catch so a buffer bug never poisons the request.
|
||||
try {
|
||||
appendLogEvent(event);
|
||||
} catch {
|
||||
/* logging must never crash the handler */
|
||||
}
|
||||
|
||||
const config = getLoggingConfig();
|
||||
if (config.lokiEndpoint) {
|
||||
initLokiTransport();
|
||||
|
||||
Reference in new issue
Block a user