From d9bf4c9e59e2bd49696537d0bf5cd765aa064ffb Mon Sep 17 00:00:00 2001 From: zx07 Date: Fri, 13 Feb 2026 20:37:54 +0800 Subject: [PATCH] feat(observability): add toggle for request detail recording (#122) * feat(frontend): add toggle logic for observability configuration (cherry picked from commit 71cef26df6160290c980710ff4a0d6e7aa926105) * feat(backend): add toggle logic for observability configuration (cherry picked from commit fb1a8d90e24f041c41b3039f7189791458b87f87) --------- Co-authored-by: zx --- .env.example | 1 + src/app/(dashboard)/dashboard/profile/page.js | 41 +++++++++++++++++-- src/lib/localDb.js | 2 + src/lib/requestDetailsDb.js | 26 ++++++++++-- 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/.env.example b/.env.example index 584bafb6..afac229a 100644 --- a/.env.example +++ b/.env.example @@ -14,6 +14,7 @@ NODE_ENV=production API_KEY_SECRET=endpoint-proxy-api-key-secret MACHINE_ID_SALT=endpoint-proxy-salt ENABLE_REQUEST_LOGS=false +OBSERVABILITY_ENABLED=true AUTH_COOKIE_SECURE=false REQUIRE_API_KEY=false diff --git a/src/app/(dashboard)/dashboard/profile/page.js b/src/app/(dashboard)/dashboard/profile/page.js index 32bae261..14ec932e 100644 --- a/src/app/(dashboard)/dashboard/profile/page.js +++ b/src/app/(dashboard)/dashboard/profile/page.js @@ -128,6 +128,23 @@ export default function ProfilePage() { } }; + const updateObservabilityEnabled = async (enabled) => { + try { + const res = await fetch("/api/settings", { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ observabilityEnabled: enabled }), + }); + if (res.ok) { + setSettings(prev => ({ ...prev, observabilityEnabled: enabled })); + } + } catch (err) { + console.error("Failed to update observabilityEnabled:", err); + } + }; + + const observabilityEnabled = settings.observabilityEnabled !== false; + return (
@@ -358,6 +375,21 @@ export default function ProfilePage() {

Observability

+
+
+

Enable Observability

+

+ Turn request detail recording on/off globally +

+
+ +
+ +

Max Records

@@ -372,7 +404,7 @@ export default function ProfilePage() { step="100" value={settings.observabilityMaxRecords || 1000} onChange={(e) => updateObservabilitySetting("observabilityMaxRecords", parseInt(e.target.value))} - disabled={loading} + disabled={loading || !observabilityEnabled} className="w-28 text-center" />
@@ -391,7 +423,7 @@ export default function ProfilePage() { step="5" value={settings.observabilityBatchSize || 20} onChange={(e) => updateObservabilitySetting("observabilityBatchSize", parseInt(e.target.value))} - disabled={loading} + disabled={loading || !observabilityEnabled} className="w-28 text-center" />
@@ -410,7 +442,7 @@ export default function ProfilePage() { step="1000" value={settings.observabilityFlushIntervalMs || 5000} onChange={(e) => updateObservabilitySetting("observabilityFlushIntervalMs", parseInt(e.target.value))} - disabled={loading} + disabled={loading || !observabilityEnabled} className="w-28 text-center" />
@@ -429,7 +461,7 @@ export default function ProfilePage() { step="100" value={settings.observabilityMaxJsonSize || 1024} onChange={(e) => updateObservabilitySetting("observabilityMaxJsonSize", parseInt(e.target.value))} - disabled={loading} + disabled={loading || !observabilityEnabled} className="w-28 text-center" />
@@ -437,6 +469,7 @@ export default function ProfilePage() {

Current: Keeps {settings.observabilityMaxRecords || 1000} records, batches every {settings.observabilityBatchSize || 20} requests, max {settings.observabilityMaxJsonSize || 1024}KB per field

+
diff --git a/src/lib/localDb.js b/src/lib/localDb.js index fd20dec5..54624851 100644 --- a/src/lib/localDb.js +++ b/src/lib/localDb.js @@ -51,6 +51,7 @@ const defaultData = { cloudEnabled: false, stickyRoundRobinLimit: 3, requireLogin: true, + observabilityEnabled: true, observabilityMaxRecords: 1000, observabilityBatchSize: 20, observabilityFlushIntervalMs: 5000, @@ -71,6 +72,7 @@ function cloneDefaultData() { cloudEnabled: false, stickyRoundRobinLimit: 3, requireLogin: true, + observabilityEnabled: true, observabilityMaxRecords: 1000, observabilityBatchSize: 20, observabilityFlushIntervalMs: 5000, diff --git a/src/lib/requestDetailsDb.js b/src/lib/requestDetailsDb.js index 85b86c59..c7f9d438 100644 --- a/src/lib/requestDetailsDb.js +++ b/src/lib/requestDetailsDb.js @@ -17,8 +17,13 @@ async function getObservabilityConfig() { try { const { getSettings } = await import("@/lib/localDb"); const settings = await getSettings(); + const envEnabled = process.env.OBSERVABILITY_ENABLED !== "false"; + const enabled = typeof settings.observabilityEnabled === "boolean" + ? settings.observabilityEnabled + : envEnabled; return { + enabled, maxRecords: settings.observabilityMaxRecords || parseInt(process.env.OBSERVABILITY_MAX_RECORDS || '1000', 10), batchSize: settings.observabilityBatchSize || parseInt(process.env.OBSERVABILITY_BATCH_SIZE || '20', 10), flushIntervalMs: settings.observabilityFlushIntervalMs || parseInt(process.env.OBSERVABILITY_FLUSH_INTERVAL_MS || '5000', 10), @@ -27,6 +32,7 @@ async function getObservabilityConfig() { } catch (error) { console.error("[requestDetailsDb] Failed to load observability config:", error); return { + enabled: true, maxRecords: 1000, batchSize: 20, flushIntervalMs: 5000, @@ -37,6 +43,17 @@ async function getObservabilityConfig() { // Cache config to avoid repeated database reads let cachedConfig = null; +let cachedConfigTs = 0; +const CONFIG_CACHE_TTL_MS = 5000; + +async function getCachedObservabilityConfig() { + if (!cachedConfig || (Date.now() - cachedConfigTs) > CONFIG_CACHE_TTL_MS) { + cachedConfig = await getObservabilityConfig(); + cachedConfigTs = Date.now(); + } + + return cachedConfig; +} let dbInstance = null; @@ -317,13 +334,14 @@ function sanitizeHeaders(headers) { export async function saveRequestDetail(detail) { if (isCloud) return; - if (!cachedConfig) { - cachedConfig = await getObservabilityConfig(); + const config = await getCachedObservabilityConfig(); + if (!config.enabled) { + return; } writeBuffer.push(detail); - if (writeBuffer.length >= cachedConfig.batchSize) { + if (writeBuffer.length >= config.batchSize) { await flushToDatabase(); if (flushTimer) { @@ -334,7 +352,7 @@ export async function saveRequestDetail(detail) { flushTimer = setTimeout(() => { flushToDatabase().catch(() => {}); flushTimer = null; - }, cachedConfig.flushIntervalMs); + }, config.flushIntervalMs); } }