From 6c08d43b4c13093d7ba2be6d0caf23d017125fea Mon Sep 17 00:00:00 2001 From: Prototype0645 Date: Tue, 1 Sep 2026 09:08:13 +0200 Subject: [PATCH] fix(inputunifi): apply default_site_name_override to log entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit default_site_name_override is applied in augmentMetrics, which only the metrics path goes through. Log entries leave by collectControllerEvents, which reads its sites straight from getFilteredSites — where the override is deliberately not applied — so events, alarms, IDS records and system-log entries ship with the controller's stock site name. Four of the five site-scoped log collectors were affected; collectAnomalies already did this inline, which is what makes the omission visible. The consequence is worst for a poller watching several UniFi OS consoles: each one calls its only site "default", so their log entries are indistinguishable downstream. In Loki every stream from those consoles lands under site_name="Default (default)" no matter which console it came from, and no relabeling downstream can separate them again — the information is gone by then. That is precisely the case the option was added for, and it works for the metrics from those same consoles. Extract the check collectAnomalies was doing into overrideSiteName and call it from all five, so the two paths agree. Note that only Site.Name reaches an API path; Site.SiteName is a display name throughout the library. The override is therefore safe here, which is what applySiteNameOverride's own comment already says ("keeping default for API calls"). --- pkg/inputunifi/collectevents.go | 26 ++++++++++++-- pkg/inputunifi/collectevents_test.go | 54 ++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 pkg/inputunifi/collectevents_test.go diff --git a/pkg/inputunifi/collectevents.go b/pkg/inputunifi/collectevents.go index a62b9a15..fd654283 100644 --- a/pkg/inputunifi/collectevents.go +++ b/pkg/inputunifi/collectevents.go @@ -71,6 +71,21 @@ func (u *InputUnifi) collectControllerEvents(c *Controller) ([]any, error) { return logs, nil } +// overrideSiteName applies the controller's default_site_name_override to a +// site name that is still the controller's stock default. +// +// Log entries need this applied here, one collector at a time. Metrics get the +// override from augmentMetrics, but log entries never pass through it: they +// leave by collectControllerEvents, which reads its sites straight from +// getFilteredSites. So whatever a collector appends is what ships. +func overrideSiteName(c *Controller, siteName string) string { + if c.DefaultSiteNameOverride != "" && isDefaultSiteName(siteName) { + return c.DefaultSiteNameOverride + } + + return siteName +} + func (u *InputUnifi) collectAlarms(logs []any, sites []*unifi.Site, c *Controller) ([]any, error) { if *c.SaveAlarms { u.LogDebugf("Collecting controller alarms: %s (%s)", c.URL, c.ID) @@ -161,6 +176,7 @@ func (u *InputUnifi) collectAlarms(logs []any, sites []*unifi.Site, c *Controlle for _, e := range events { // Try to extract MAC address from alarm message and enrich with device name e.DeviceName = u.extractDeviceNameFromAlarm(e, macToName) + e.SiteName = overrideSiteName(c, e.SiteName) logs = append(logs, e) @@ -202,9 +218,7 @@ func (u *InputUnifi) collectAnomalies(logs []any, sites []*unifi.Site, c *Contro } for _, e := range events { - if c.DefaultSiteNameOverride != "" && isDefaultSiteName(e.SiteName) { - e.SiteName = c.DefaultSiteNameOverride - } + e.SiteName = overrideSiteName(c, e.SiteName) logs = append(logs, e) @@ -241,6 +255,8 @@ func (u *InputUnifi) collectEvents(logs []any, sites []*unifi.Site, c *Controlle for _, e := range events { e := redactEvent(e, c.HashPII, c.DropPII) + e.SiteName = overrideSiteName(c, e.SiteName) + logs = append(logs, e) webserver.NewInputEvent(PluginName, s.ID+"_events", &webserver.Event{ @@ -270,6 +286,8 @@ func (u *InputUnifi) collectSyslog(logs []any, sites []*unifi.Site, c *Controlle for _, e := range entries { e := redactSystemLogEntry(e, c.HashPII, c.DropPII) + e.SiteName = overrideSiteName(c, e.SiteName) + logs = append(logs, e) webserver.NewInputEvent(PluginName, e.SiteName+"_syslog", &webserver.Event{ @@ -362,6 +380,8 @@ func (u *InputUnifi) collectIDs(logs []any, sites []*unifi.Site, c *Controller) } for _, e := range events { + e.SiteName = overrideSiteName(c, e.SiteName) + logs = append(logs, e) webserver.NewInputEvent(PluginName, s.ID+"_ids", &webserver.Event{ diff --git a/pkg/inputunifi/collectevents_test.go b/pkg/inputunifi/collectevents_test.go new file mode 100644 index 00000000..59403ac8 --- /dev/null +++ b/pkg/inputunifi/collectevents_test.go @@ -0,0 +1,54 @@ +package inputunifi // nolint: testpackage + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestOverrideSiteName is what this change exists for. Every UniFi OS console +// calls its only site "default", so a poller watching several of them ships log +// entries that cannot be told apart — which is what default_site_name_override +// is meant to solve, and did not, on this path. +func TestOverrideSiteName(t *testing.T) { + t.Parallel() + + for name, tc := range map[string]struct { + override string + siteName string + want string + }{ + "stock name from a UniFi OS console": { + override: "Les_Solidarites", + siteName: "Default (default)", + want: "Les_Solidarites", + }, + "bare default": { + override: "Les_Solidarites", + siteName: "default", + want: "Les_Solidarites", + }, + "a site the operator already named is left alone": { + override: "Les_Solidarites", + siteName: "CharlHot - SweetHome", + want: "CharlHot - SweetHome", + }, + "no override configured is a no-op": { + override: "", + siteName: "Default (default)", + want: "Default (default)", + }, + "empty site name is not a default": { + override: "Les_Solidarites", + siteName: "", + want: "", + }, + } { + t.Run(name, func(t *testing.T) { + t.Parallel() + + c := &Controller{DefaultSiteNameOverride: tc.override} + assert.Equal(t, tc.want, overrideSiteName(c, tc.siteName)) + }) + } +}