diff --git a/config.go b/config.go index 7fd3159..58da40e 100644 --- a/config.go +++ b/config.go @@ -16,6 +16,9 @@ type Config struct { User string `json:"user"` // basic-auth user, shared by metrics and logs (empty disables auth) Pass string `json:"pass"` // basic-auth password IntervalSec int `json:"interval_sec"` // metrics push period in seconds (default 15) + // Extra labels attached to every metric series and log stream of this node, + // e.g. {"role": "vps"}. Enables filtering in Grafana (per-role dashboards). + Labels map[string]string `json:"labels"` // Android/Termux exec workaround: the standard Go binary cannot exec // subprocesses there (seccomp blocks pidfd_open with SIGSYS). When set, the // agent reads battery JSON from this file (written by a shell helper) instead @@ -81,3 +84,17 @@ func loadConfig(path string) (Config, error) { } return cfg, nil } + +// extraLabels returns the labels attached to every series/stream: the node's +// instance plus any custom labels (e.g. role). The optional extra map is merged +// last (used to add per-stream labels like job/unit for logs). +func (cfg Config) extraLabels(extra map[string]string) map[string]string { + m := map[string]string{"instance": cfg.Node} + for k, v := range cfg.Labels { + m[k] = v + } + for k, v := range extra { + m[k] = v + } + return m +} diff --git a/logs.go b/logs.go index 9b9cb65..17cc26a 100644 --- a/logs.go +++ b/logs.go @@ -159,7 +159,7 @@ func shipJournald(ctx context.Context, cfg Config, binPath string) { ts[i] = it.ts ln[i] = it.line } - labels := map[string]string{"instance": cfg.Node, "job": "journald", "unit": unit} + labels := cfg.extraLabels(map[string]string{"job": "journald", "unit": unit}) if err := infra.PushLokiStream(cfg.LokiURL, cfg.User, cfg.Pass, labels, ts, ln); err != nil { log.Printf("logs: push error (unit=%s, %d lines): %v", unit, len(items), err) } @@ -195,7 +195,7 @@ func shipFileTail(ctx context.Context, cfg Config, path, job string) { if fi, err := os.Stat(path); err == nil { offset = fi.Size() // skip pre-existing history on first start } - labels := map[string]string{"instance": cfg.Node, "job": job} + labels := cfg.extraLabels(map[string]string{"job": job}) ticker := time.NewTicker(3 * time.Second) defer ticker.Stop() diff --git a/main.go b/main.go index 3797964..ac9fa13 100644 --- a/main.go +++ b/main.go @@ -130,7 +130,7 @@ func pushOnce(cfg Config) error { // cfg.BatteryFile and we parse that here; elsewhere we collect directly. samples = append(samples, batterySamples(cfg)...) body := infra.FormatPromExposition(samples, time.Now().UnixMilli()) - if err := infra.PushPromRemote(cfg.HubURL, cfg.User, cfg.Pass, body, map[string]string{"instance": cfg.Node}); err != nil { + if err := infra.PushPromRemote(cfg.HubURL, cfg.User, cfg.Pass, body, cfg.extraLabels(nil)); err != nil { return err } log.Printf("pushed %d samples", len(samples))