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);
}
}