feat: labels map en config (role) → extra_label en métricas + stream labels en logs

This commit is contained in:
Egutierrez
2026-06-07 18:00:37 +02:00
parent 4cdb4b6557
commit 921ac3caa6
3 changed files with 20 additions and 3 deletions
+17
View File
@@ -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
}
+2 -2
View File
@@ -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()
+1 -1
View File
@@ -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))