From eed63b0dfeca1e2402962b41be969fe06d79964c Mon Sep 17 00:00:00 2001 From: Matt Van Horn Date: Tue, 5 May 2026 01:58:22 -0700 Subject: [PATCH] feat(logging): add node identifier to log lines (#894) * feat(logging): add node identifier to log lines Closes #307. Adds an `os.Hostname()`-derived `node` field to every log line so multi-node deployments can be traced from log output alone. Per the issue body, `os.Hostname()` returns sensible identifiers across the relevant deployment targets: - Kubernetes: pod name (e.g. `outpost-api-5bbd447f65-x2444`) - Docker: container ID - ECS: task/container ID - Cloud VMs: instance hostname The hostname is captured once inside `makeLogger` and attached as a default zap field via `zapLogger.With(...)`. Both the main logger and the audit logger pick this up because they share the same construction path. otelzap preserves zap `With` fields, so `Ctx(ctx)` derivations also carry the node identifier. If `os.Hostname()` returns an error, the field falls back to `"unknown"`. This matches the silent-fallback pattern in `internal/redislock/redislock.go` and avoids failing logger construction on observability metadata. * feat(logging): use host.name per OTEL convention Per @alexluong's review: the hostname attribute should follow the OTEL host semantic convention to avoid confusion with k8s node semantics. --- internal/logging/logger.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/logging/logger.go b/internal/logging/logger.go index 541aa662e..77623c310 100644 --- a/internal/logging/logger.go +++ b/internal/logging/logger.go @@ -2,6 +2,7 @@ package logging import ( "context" + "os" "github.com/uptrace/opentelemetry-go-extra/otelzap" "go.uber.org/zap" @@ -69,6 +70,11 @@ func makeLogger(logLevel string) (*otelzap.Logger, error) { if err != nil { return nil, err } + hostname, err := os.Hostname() + if err != nil { + hostname = "unknown" + } + zapLogger = zapLogger.With(zap.String("host.name", hostname)) return otelzap.New(zapLogger, otelzap.WithMinLevel(level),