cf_observability

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 8, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

README

caerus-framework-observability

CI codecov License

Caerus Framework — observability component.

Exposes:

  • Kubernetes health-check endpoints aggregating every registered component that implements the optional caerusframework.HealthProvider interface. Components that do not implement it are simply not included, so supporting health checks is entirely optional.
  • A /metrics endpoint (Prometheus text format) with Go runtime metrics plus the live state of every registered component implementing the optional cf.MetricsProvider interface.
  • OpenTelemetry tracing: when an OTLP endpoint is configured, the component builds a tracer provider, installs it as the global provider (components trace via otel.Tracer), and flushes it at Shutdown.

Endpoints

Endpoint Probe Behaviour
/healthz, /livez liveness 200 ok while the process is alive; component health is deliberately excluded (a dependency outage should make a pod unready, not restartable).
/readyz readiness (and startup) 200 ok when every registered HealthProvider component is healthy, 503 otherwise, with one fail: <component>: <reason> line per failing check.
/metrics scrape Prometheus text format: Go runtime + process metrics and one caerus_<name> sample per MetricsProvider component that has something to report.

Kubernetes only inspects the status code: 2xx = healthy, anything else = unhealthy. Configure the probes in the pod spec, e.g.:

livenessProbe:
  httpGet: { path: /healthz, port: 9090 }
  initialDelaySeconds: 3
  periodSeconds: 10
readinessProbe:
  httpGet: { path: /readyz, port: 9090 }
  initialDelaySeconds: 3
  periodSeconds: 5

Point a Prometheus scraper at /metrics, and a collector (e.g. otel-collector) at the configured trace_endpoint for OTLP/gRPC spans.

Usage

fw := caerusframework.New() // observability is a built-in bootstrap stage
fw.AddComponent(cf_logs.New(cf_logs.WithWriter(os.Stdout)))
fw.AddComponent(cf_observability.New()) // health checks + metrics on :9090 by default
// ... register the rest of the components ...
fw.Run(ctx)
Optional component health checks

A component opts in by implementing cf.HealthProvider; the observability component discovers it and folds its health into /readyz:

// Health implements cf.HealthProvider. nil = healthy.
func (c *CFMongoDB) Health(ctx context.Context) error {
    return c.pool.Ping(ctx) // or any other liveness/readiness signal
}

Every check is bounded by the health-check timeout (default 2s); a component that misses its deadline is reported as timed out so a hung check can never hang the probe.

Optional component metrics (lazy pickup)

A component opts in by implementing cf.MetricsProvider; observability registers a collector for it and reads Metrics() on every /metrics scrape, so the values are always live:

// Metrics implements cf.MetricsProvider.
func (l *Logs) Metrics() []cf.Metric {
    return []cf.Metric{{
        Name:  "logs_info", // served as caerus_logs_info
        Value: 1,
        Labels: map[string]string{"format": l.format.String(), "level": l.Level().String()},
    }}
}

Return nil while the component is not initialized (or has nothing to report): the collector then skips it, and the sample appears on the scrape after the component initializes — a lazy pickup with no subscription. Components that do not implement the interface contribute nothing.

Tracing

With an endpoint configured, Init creates an OTLP/gRPC tracer provider (AlwaysSample, service.name = WithServiceName) and installs it as the global provider; components trace through otel.Tracer. Shutdown flushes pending spans. The transport is insecure — run the collector in-cluster.

Configuration

ObservabilityConfig is file/env-drivable: load it through the configuration component and pass it via WithConfig:

observability:
  health_checks: true          # enable the health-check endpoints
  metrics: true                # enable the /metrics endpoint
  tracing: true                # enable OTLP trace export (needs trace_endpoint)
  address: ":9090"             # bind address of the HTTP server
  health_check_timeout_sec: 2  # per-component health check deadline
  trace_endpoint: "otel-collector:4317"
  service_name: myapp          # service.name on exported spans
Option Default Purpose
WithHealthChecks(bool) true Enable the Kubernetes health-check endpoints.
WithMetrics(bool) true Enable the /metrics endpoint.
WithTracing(bool) false Enable trace export (active once an endpoint is set).
WithAddress(string) ":9090" Bind address of the HTTP server.
WithHealthCheckTimeout(d) 2s Deadline for each component health check.
WithTraceEndpoint(string) "" (tracing latent) OTLP/gRPC collector endpoint.
WithServiceName(string) "caerus" service.name attribute on exported spans.
WithConfig(ObservabilityConfig) Loaded config; non-zero fields override the options.
WithConfigSource(string) "" Bind a configuration source; Init applies its current value and OnConfigReload applies later changes live (tracing) or logs restart-required (bind/metrics/health toggles).
WithLogger(*slog.Logger) framework logs logger (re-delivered on logs Reconfigure), falling back to slog.Default() Explicit logger override.

health_checks, metrics and tracing are *bool in ObservabilityConfig so an explicit false in the file is honored (turning the feature off) instead of being treated as "unset".

Component contract

Implements caerusframework.CaerusComponent:

  • Name()"observability" (cf_observability.ComponentName)
  • GetInitOrderStage()caerusframework.ObservabilityStage (third bootstrap stage, after logs and configuration)
  • GetDependencies()[logs]
  • Init sets up the Prometheus registry (Go/process collectors + one collector per registered MetricsProvider), builds and installs the tracer provider when tracing is enabled and an endpoint is configured, and binds the HTTP server (fail-fast on an unusable address) when health checks or metrics are enabled. With everything disabled it is a no-op.
  • Shutdown stops the server, waiting for in-flight requests up to ctx, and flushes the tracer provider.
  • Address() returns the bound address (empty when disabled) for building probe configs at runtime.
  • TracerProvider() returns the configured provider (nil when tracing is inactive).

Docs

License

Apache License 2.0 — see LICENSE.

Documentation

Index

Constants

View Source
const ComponentName = "observability"

ComponentName is the framework component name for the observability component. It is the identifier other components use in GetDependencies to require observability.

Variables

This section is empty.

Functions

This section is empty.

Types

type Metric

type Metric struct {
	// Name is the metric name without the "caerus_" prefix. It must not be
	// empty for the sample to be emitted.
	Name string
	// Help describes the metric for /metrics consumers.
	Help string
	// Value is the sample value. State/info samples typically use 1 and carry
	// their meaning in Labels.
	Value float64
	// Labels annotate the sample, e.g. {"format": "json", "level": "info"}.
	Labels map[string]string
	// Type selects how the sample is scraped. The zero value (MetricTypeGauge)
	// preserves the pre-existing behavior; set MetricTypeCounter for
	// monotonically increasing event counts.
	Type MetricType
}

Metric is one runtime-state sample a component exposes for the /metrics endpoint. The observability component serves the sample prefixed with "caerus_" (Name "logs_info" becomes "caerus_logs_info").

type MetricType

type MetricType int

MetricType distinguishes how a Metric sample is scraped. The zero value (MetricTypeGauge) preserves backward compatibility with existing samples.

const (
	// MetricTypeGauge is the default. The sample is emitted as a Prometheus
	// gauge (current value, goes up and down).
	MetricTypeGauge MetricType = iota
	// MetricTypeCounter marks a monotonically increasing counter. The sample
	// is emitted as a Prometheus counter (only resets on process restart).
	// Components must ensure counter values only increase for the process
	// lifetime.
	MetricTypeCounter
)

type MetricsProvider

type MetricsProvider interface {
	// Metrics returns the component's current runtime state. Return nil while
	// the component is not initialized or has nothing to report.
	Metrics() []Metric
}

MetricsProvider is an optional interface for components that expose runtime state as metrics. The observability component discovers components implementing it and calls Metrics on every /metrics scrape, so the values are always live: a component that is not initialized yet returns nil and is skipped until it does — a lazy pickup that needs no subscription. A component that does not implement MetricsProvider contributes nothing to /metrics.

Bootstrap components (logs, configuration) do not implement this interface to avoid import cycles; observability scrapes their state directly via cf.Get + exported state helpers.

type Observability

type Observability struct {
	// contains filtered or unexported fields
}

Observability is the caerus-framework-observability component. It exposes:

  • Kubernetes health-check endpoints (liveness /healthz + /livez, readiness /readyz) that aggregate the health of every registered component implementing cf.HealthProvider. Components that do not implement it are simply not included, so supporting health checks is entirely optional.
  • a /metrics endpoint (Prometheus text format) with Go runtime metrics plus the live state of every registered component implementing cf.MetricsProvider. State is read on every scrape, so a component that is not initialized yet returns nil and is skipped until it does (lazy pickup); components that do not implement the interface contribute nothing.
  • OpenTelemetry tracing: when an OTLP endpoint is configured, the component builds a tracer provider, installs it as the global provider (components trace via otel.Tracer), and flushes it at Shutdown.

func New

func New(opts ...Option) *Observability

New creates an observability component. Health checks and metrics are on by default; tracing is off until explicitly enabled with a config value or option. The HTTP server binds at Init, only when at least one of health checks or metrics is enabled.

func (*Observability) Address

func (c *Observability) Address() string

Address returns the bound address of the HTTP server, or "" if neither health checks nor metrics are enabled or Init has not run. It is useful for building Kubernetes probe configs at runtime.

func (*Observability) CoreConfigSource

func (c *Observability) CoreConfigSource() ([]cf.ConfigSourceValue, error)

CoreConfigSource implements cf.CoreConfigSource. It declares the observability component's own configuration source; the observability module cannot import the configuration module (the configuration module imports it), so the framework discovers it among registered components during argv absorption and registers the declaration on the component's behalf.

The source is owned by the component: default file config/<name>.json, owner cf_observability. An argv redeclaration wins: the --<name> file-path flag ParseFlags registers overrides where the file is read from, and the loaded value reaches the component through OnConfigReload (see WithConfigSource). No source is declared when WithConfigSource was not given.

func (*Observability) GetDependencies

func (c *Observability) GetDependencies() []string

GetDependencies implements cf.Dependencies. The component logs through the framework logs component, and depends on configuration when WithConfigSource is set (it reads its own source through the configuration component).

func (*Observability) GetInitOrderStage

func (c *Observability) GetInitOrderStage() cf.Stage

GetInitOrderStage implements cf.CaerusComponent. Observability is part of the bootstrap prefix and initializes after logs and configuration.

func (*Observability) Init

Init implements cf.CaerusComponent. It sets up metrics (Prometheus registry with Go runtime collectors and one collector per registered component implementing cf.MetricsProvider) and tracing (OTLP tracer provider, when an endpoint is configured and tracing is enabled). When health checks or metrics are enabled it binds the HTTP server (fail-fast on an unusable address) and discovers the registered components implementing cf.HealthProvider.

func (*Observability) Name

func (c *Observability) Name() string

Name implements cf.CaerusComponent.

func (*Observability) OnConfigReload

func (c *Observability) OnConfigReload(source string, cfg any)

OnConfigReload implements cf.ConfigReloader. It applies a freshly loaded ObservabilityConfig from the source named by WithConfigSource. Tracing toggle/endpoint changes take effect immediately (provider swap); HTTP endpoint changes (bind address, health-check/metrics toggles) are logged as restart-required and the last-good server keeps running. The initial value delivered by configuration before this component's Init is ignored (Init reads the source itself and must not build providers early).

func (*Observability) Shutdown

func (c *Observability) Shutdown(ctx context.Context) error

Shutdown implements cf.CaerusComponent. It stops the HTTP server (waiting for in-flight requests up to ctx) and flushes the tracer provider. Safe to call even if Init never ran or the features are disabled.

func (*Observability) TracerProvider

func (c *Observability) TracerProvider() oteltrace.TracerProvider

TracerProvider returns the OpenTelemetry tracer provider configured at Init, or nil when tracing is disabled or no endpoint was configured. Components trace through otel.Tracer (the provider is installed globally); the accessor is for embedding and for building composite providers.

type ObservabilityConfig

type ObservabilityConfig struct {
	// HealthChecks enables the Kubernetes health-check HTTP endpoints. It is a
	// *bool so an absent key is distinguishable from an explicit
	// health_checks: false, which is required to turn the endpoints off (the
	// component's default is enabled).
	HealthChecks *bool `json:"health_checks,omitempty" yaml:"health_checks,omitempty"`
	// Metrics enables the /metrics endpoint (Prometheus text format; default
	// enabled). It is a *bool so an explicit metrics: false is honored.
	Metrics *bool `json:"metrics,omitempty" yaml:"metrics,omitempty"`
	// Tracing enables OpenTelemetry trace export over OTLP/gRPC (default off;
	// internal tracing mechanics stay dark until enabled). It is a *bool so an
	// explicit tracing: false is honored.
	Tracing *bool `json:"tracing,omitempty" yaml:"tracing,omitempty"`
	// Address is the bind address for the HTTP server (default ":9090").
	Address string `json:"address,omitempty" yaml:"address,omitempty"`
	// HealthCheckTimeoutSec bounds each component health check (default 2).
	HealthCheckTimeoutSec int `json:"health_check_timeout_sec,omitempty" yaml:"health_check_timeout_sec,omitempty"`
	// TraceEndpoint is the OTLP/gRPC collector endpoint (e.g.
	// "otel-collector:4317"). When set and tracing is enabled, spans are
	// exported to it (insecure transport).
	TraceEndpoint string `json:"trace_endpoint,omitempty" yaml:"trace_endpoint,omitempty"`
	// ServiceName is the OpenTelemetry service.name attribute attached to
	// exported spans (default "caerus").
	ServiceName string `json:"service_name,omitempty" yaml:"service_name,omitempty"`
}

ObservabilityConfig is the file/env-drivable observability configuration. Load it through the configuration component and pass it via WithConfig; both JSON and YAML tags are provided.

type Option

type Option func(*options)

Option configures the observability component at construction time.

func WithAddress

func WithAddress(addr string) Option

WithAddress sets the bind address for the HTTP server (default ":9090").

func WithConfig

func WithConfig(cfg ObservabilityConfig) Option

WithConfig sets the configuration loaded from the configuration component. Non-zero fields of cfg override the values set by the convenience options, which act as in-code defaults:

cfg, _ := cf_configuration.Lookup[cf_observability.ObservabilityConfig](conf, "observability")
o := cf_observability.New(cf_observability.WithConfig(*cfg))

func WithConfigSource

func WithConfigSource(name string) Option

WithConfigSource names the configuration source (caerus-framework- configuration) whose ObservabilityConfig is applied to the component at Init and again on every validated reload via OnConfigReload. Prefer this over a WithConfig snapshot when the component is framework-managed: the source stays the live options plane. The component self-registers the source during argv absorption (default file config/<name>.json, owner cf_observability); an argv --<name> file-path override wins, and the app may also register its own Source[ObservabilityConfig] for a custom default. Until the source loads, construction-time defaults apply.

func WithHealthCheckTimeout

func WithHealthCheckTimeout(d time.Duration) Option

WithHealthCheckTimeout sets the deadline for each component health check (default 2s). A component that misses its deadline is reported as not ready.

func WithHealthChecks

func WithHealthChecks(enabled bool) Option

WithHealthChecks enables (default) or disables the Kubernetes health-check HTTP endpoints.

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger overrides the logger used for component diagnostics. By default the component logs through the framework logs component (declared in GetDependencies); WithLogger is an explicit override for tests and embedded use and wins over the framework logger. slog.Default() remains the fallback only when neither is available.

func WithMetrics

func WithMetrics(enabled bool) Option

WithMetrics enables (default) or disables the /metrics endpoint.

func WithServiceName

func WithServiceName(name string) Option

WithServiceName sets the OpenTelemetry service.name attribute (default "caerus").

func WithTraceEndpoint

func WithTraceEndpoint(endpoint string) Option

WithTraceEndpoint sets the OTLP/gRPC collector endpoint. The connection is insecure and only created when tracing is enabled.

func WithTracing

func WithTracing(enabled bool) Option

WithTracing enables (default off) OpenTelemetry trace export. Tracing is off by default and only active once enabled and an endpoint is set with WithTraceEndpoint (or the loaded config's trace_endpoint).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL