Documentation
¶
Overview ¶
Package obs is the instrumentation tier: ECS-shaped structured logging and a standard-library metrics registry rendered in Prometheus text format.
Nothing here changes what the service does. Logging goes to stdout as JSON so a log shipper (Filebeat / Elastic Agent) can pick it straight off the container, with a text mode for local runs — see docs/OBSERVABILITY.md.
Index ¶
- Variables
- func Err(err error) slog.Attr
- func FromContext(ctx context.Context, component string) *slog.Logger
- func Handler() http.Handler
- func Logger(component string) *slog.Logger
- func RequestLogger(requestID string) *slog.Logger
- func Setup(o LogOptions)
- func Stack(buf []byte) slog.Attr
- func StdLogger(component string, level slog.Level) *log.Logger
- func WithLogger(ctx context.Context, l *slog.Logger) context.Context
- type Counter
- type Gauge
- type Histogram
- type LogOptions
Constants ¶
This section is empty.
Variables ¶
var ( // NATSUp separates "the distribution plane is down" from "the process is // down". A NATS outage no longer stops the service booting, so without a // gauge the degraded start is visible only in a log line at startup. NATSUp = NewGauge("centralconfig_nats_up", "1 when the KV buckets are provisioned and NATS is connected, 0 otherwise.") KVPublishAttempts = NewCounter("centralconfig_kv_publish_attempts_total", "KV publish attempts, by bucket.") KVPublishSuccess = NewCounter("centralconfig_kv_publish_success_total", "KV publishes that reached JetStream, by bucket.") KVPublishFailures = NewCounter("centralconfig_kv_publish_failures_total", "KV publishes that failed and were left to the reconciler, by bucket.") KVPublishSkipped = NewCounter("centralconfig_kv_publish_skipped_total", "KV publishes skipped because the stored value was already identical, by bucket.") KVDeleteFailures = NewCounter("centralconfig_kv_delete_failures_total", "KV key deletions that failed, by bucket.") )
KV distribution. A publish failure is the drift the reconciler exists to heal; it is silent from the caller's side, so it has to be countable.
var ( ReconcileCycles = NewCounter("centralconfig_reconcile_cycles_total", "Reconcile cycles completed, by sweep (full/incremental) and result.") ReconcileDuration = NewHistogram("centralconfig_reconcile_duration_seconds", "Wall time of a reconcile cycle, by sweep.") ReconcileKeysRepublished = NewCounter("centralconfig_reconcile_keys_republished_total", "Keys republished from the database to KV, by source.") ReconcileKeysPruned = NewCounter("centralconfig_reconcile_keys_pruned_total", "KV keys deleted because their database row is gone, by bucket.") // ReconcilePruneRefused fires when a sweep proposed deleting more of a // bucket than the ceiling allows. It means KV and the database disagree // wholesale, which is worth an alert on the first occurrence. ReconcilePruneRefused = NewCounter("centralconfig_reconcile_prune_refused_total", "Prune passes refused because they would have deleted too much of a bucket, by bucket.") ReconcileSourceFailures = NewCounter("centralconfig_reconcile_source_failures_total", "Reconcile sources that failed to resync, by source.") ReconcileLastSuccess = NewGauge("centralconfig_reconcile_last_success_timestamp_seconds", "Unix time of the last reconcile cycle in which every source succeeded.") )
Reconciler. Its whole job is to converge KV on the database, so what matters is that cycles keep completing and how much drift each one had to repair.
var ( HTTPRequests = NewCounter("centralconfig_http_requests_total", "HTTP requests, by method, matched route and status code.") HTTPDuration = NewHistogram("centralconfig_http_request_duration_seconds", "HTTP request duration, by method and matched route.") HTTPPanics = NewCounter("centralconfig_http_panics_total", "Requests whose handler panicked.") )
HTTP admin API.
var ( DBUp = NewGauge("centralconfig_db_up", "1 when the last health-check ping succeeded, 0 otherwise.") DBPingFailures = NewCounter("centralconfig_db_ping_failures_total", "Health-check database pings that failed.") )
Database — PostgreSQL in production, SQLite in the local stack.
Functions ¶
func Err ¶
Err renders an error as ECS fields. Attach it rather than formatting the error into the message, so failures can be aggregated by type in Kibana.
func FromContext ¶
FromContext returns the request's logger, or the component logger when the call did not come from a request (the reconciler, startup).
func Logger ¶
Logger returns the logger for a component, tagged with its event.dataset. It is safe to call from a package-level var: the returned logger follows Setup.
func RequestLogger ¶
RequestLogger returns a logger carrying the request correlation id and nothing else: the component's event.dataset is added by FromContext at the log site, so it appears exactly once.
func Setup ¶
func Setup(o LogOptions)
Setup installs the configured handler and makes it the slog default. It is called once, from the app layer, before anything else is wired.
Types ¶
type Counter ¶
type Counter struct {
// contains filtered or unexported fields
}
Counter is a monotonic counter, optionally broken down by labels. Values live in an expvar.Map keyed by the rendered label set, which keeps the increment path lock-free per series.
func NewCounter ¶
type Gauge ¶
type Gauge struct {
// contains filtered or unexported fields
}
Gauge is a single value that goes up and down (database reachable, time of the last successful reconcile).
type Histogram ¶
type Histogram struct {
// contains filtered or unexported fields
}
Histogram is a Prometheus histogram: bucket counts plus sum and count, so quantiles can be computed at query time.
func NewHistogram ¶
type LogOptions ¶
type LogOptions struct {
Level string // debug | info | warn | error (default info)
Format string // json (ECS) | text (local development)
Service string // service.name
Version string // service.version
Output io.Writer // defaults to os.Stdout
}
LogOptions is the logging configuration, read from the environment by the app layer. Zero values fall back to the ECS/JSON production shape.