From 40c29db8c40ce0982127fdfb158b8a4e6de92929 Mon Sep 17 00:00:00 2001 From: "LocalAI [bot]" <139863280+localai-bot@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:13:52 +0200 Subject: [PATCH] fix(logs): capture backend logs by default in single mode (#10742) Backend log capture into the per-model BackendLogStore (which feeds the UI "Backend Logs" page and /api/backend-logs) was opt-in and off by default in single mode, while worker/distributed mode force-enables it via SetBackendLoggingEnabled(true). There was no CLI flag either, so the only way to populate the store was the Settings UI toggle - and the page was silently empty out of the box. Distributed "just worked"; single mode looked broken. Default EnableBackendLogging to true in NewApplicationConfig so single mode matches worker mode. The store is a small in-memory ring buffer, so the cost is negligible. Now that the default is on, loadRuntimeSettingsFromFile's usual "only flip false->true" merge would ignore a persisted false and revert the UI toggle-off on every restart. There is no env var/CLI flag for this setting, so an explicit persisted value is now authoritative in both directions, letting the toggle-off survive a restart. Signed-off-by: Ettore Di Giacinto Co-authored-by: Ettore Di Giacinto --- .../runtime_settings_branding_test.go | 31 +++++++++++++++++++ core/application/startup.go | 8 +++-- core/config/application_config.go | 13 ++++++-- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/core/application/runtime_settings_branding_test.go b/core/application/runtime_settings_branding_test.go index 3e19901bd..5dc51bf66 100644 --- a/core/application/runtime_settings_branding_test.go +++ b/core/application/runtime_settings_branding_test.go @@ -261,4 +261,35 @@ var _ = Describe("loadRuntimeSettingsFromFile", func() { Expect(cfg.AgentPool.AgentHubURL).To(Equal("https://hub.acme.io")) }) }) + + // Backend logging capture. Worker/distributed mode force-enables it + // (core/services/worker.SetBackendLoggingEnabled(true)); single mode used + // to leave it off by default with no CLI flag, so the UI "Backend Logs" + // page was silently empty unless the operator found the Settings toggle. + // It now defaults on in single mode too. Because the default is on, the + // loader must let a persisted enable_backend_logging=false (the UI + // toggle-off) win over the default - the sticky "only flip false->true" + // merge used for env-backed flags would otherwise ignore it and revert + // the toggle on every restart. + Describe("backend logging capture", func() { + It("captures backend logs by default in single mode", func() { + cfg := config.NewApplicationConfig() + Expect(cfg.EnableBackendLogging).To(BeTrue(), + "single mode should capture backend logs out of the box, matching worker mode") + }) + + It("honors a persisted enable_backend_logging=false across restart (toggle-off wins over default-on)", func() { + cfg := config.NewApplicationConfig() // default-on boot state + cfg.DynamicConfigsDir = seedSettings(`{"enable_backend_logging": false}`) + loadRuntimeSettingsFromFile(cfg) + Expect(cfg.EnableBackendLogging).To(BeFalse(), + "a UI toggle-off persisted to runtime_settings.json must survive a restart") + }) + + It("loads a persisted enable_backend_logging=true", func() { + cfg := &config.ApplicationConfig{DynamicConfigsDir: seedSettings(`{"enable_backend_logging": true}`)} + loadRuntimeSettingsFromFile(cfg) + Expect(cfg.EnableBackendLogging).To(BeTrue()) + }) + }) }) diff --git a/core/application/startup.go b/core/application/startup.go index 1cf323d34..47445e117 100644 --- a/core/application/startup.go +++ b/core/application/startup.go @@ -698,10 +698,12 @@ func loadRuntimeSettingsFromFile(options *config.ApplicationConfig) { } } + // Backend logging defaults on in single mode (NewApplicationConfig), so + // the usual "only flip false->true" merge would ignore a persisted false + // and revert the UI toggle-off on every restart. There is no env var/CLI + // flag for it, so an explicit persisted value is authoritative here. if settings.EnableBackendLogging != nil { - if !options.EnableBackendLogging { - options.EnableBackendLogging = *settings.EnableBackendLogging - } + options.EnableBackendLogging = *settings.EnableBackendLogging } // Tracing settings diff --git a/core/config/application_config.go b/core/config/application_config.go index 042c9d826..2c863beb6 100644 --- a/core/config/application_config.go +++ b/core/config/application_config.go @@ -246,9 +246,16 @@ type AppOption func(*ApplicationConfig) func NewApplicationConfig(o ...AppOption) *ApplicationConfig { opt := &ApplicationConfig{ - Context: context.Background(), - UploadLimitMB: 15, - Debug: true, + Context: context.Background(), + UploadLimitMB: 15, + Debug: true, + // Capture backend process stdout/stderr into the per-model + // BackendLogStore by default so the UI "Backend Logs" page works out + // of the box in single mode, matching worker/distributed mode (which + // force-enables it). It's a small in-memory ring buffer; the Settings + // toggle can still turn it off (a persisted false wins - see + // loadRuntimeSettingsFromFile). + EnableBackendLogging: true, AgentJobRetentionDays: 30, // Default: 30 days LRUEvictionMaxRetries: 30, // Default: 30 retries LRUEvictionRetryInterval: 1 * time.Second, // Default: 1 second