Documentation
¶
Overview ¶
Package observability wires OpenTelemetry metric instruments and the OTLP exporter for the dotvault daemon.
The package is designed to be safe to use unconditionally: when no provider has been initialised (Init was not called, or the observability config block is absent/disabled) the instruments back off to the global no-op meter, so every call site can record without nil-checking. Initialise once at daemon start, defer Shutdown.
Architecture: lower-level packages (auth, sync, vault, enrol, web) import this one and call package-level Record* helpers directly, rather than receiving a callback from the daemon entrypoint. This matches dotvault's convention for cross-cutting concerns (slog is imported at every layer in the same way); both rely on a well-behaved no-op default for tests that don't initialise the global, and both keep the call sites free of plumbing. Init mutates two process-wide globals (otel.SetMeterProvider + global.SetLoggerProvider) and rebinds the metric instruments (rebindInstruments under instrMu) — it's expected to run exactly once per process at startup. Log helpers resolve the logger per-call from global.GetLoggerProvider() so no cached handle needs rebinding. The test suite in this package does not run subtests with t.Parallel(), so the sequential invocations of Init in tests do not race; do not add t.Parallel() to any test that installs a MeterProvider or LoggerProvider (newTestReader / newTestLogProcessor) without also serialising through a sync.Once or test-scoped lock.
Attribute conventions:
- Outcomes use a small fixed vocabulary ({ok, error, renewed, reauth_required, failed, completed, denied, …}) so the exported series stay bounded. See the per-instrument RecordXxx godoc for the exact set each instrument emits.
- We never attach usernames, Vault paths, secret keys, repo URLs, or JFrog server hostnames to instruments — the same scrubbing discipline the slog handlers follow.
Index ¶
- func LogRegistryConfigManaged(ctx context.Context, path string)
- func RecordConfigReload(ctx context.Context, outcome string)
- func RecordEnrolAttempt(ctx context.Context, engine, outcome string)
- func RecordRemoteConfigFetch(ctx context.Context, outcome string)
- func RecordSIGHUP(ctx context.Context)
- func RecordSyncDuration(ctx context.Context, d time.Duration, outcome string)
- func RecordSyncTick(ctx context.Context, outcome string)
- func RecordTokenRenewal(ctx context.Context, outcome string)
- func RecordTokenTTL(ctx context.Context, ttl time.Duration)
- func RecordVaultCall(ctx context.Context, op, status string)
- func RecordWebRequest(ctx context.Context, route string, statusClass string)
- type Config
- type Provider
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func LogRegistryConfigManaged ¶ added in v0.19.0
LogRegistryConfigManaged emits a WARN-severity OTel log record signalling that the daemon's configuration came from the Windows Registry (Group Policy) and the file at path is being ignored. Routed through the OTel logger rather than slog because the message surfaces a deployment fact an operator cares about (GPO mode is active) but is *not* something an end-user running the CLI should see on stdout/stderr — slog there leaks an INFO line out of every CLI invocation on a GPO-managed Windows box. When observability is disabled the global LoggerProvider is a no-op and the record is silently dropped, which is exactly the desired behaviour.
Resolves the logger from the current global LoggerProvider on every call rather than caching a handle behind a mutex. The previous cached-handle design carried an exported RebindGlobalLogger purely for tests; this is called once per daemon/sync startup so a single global lookup per emit is fine and removes that test-only API from the production surface.
func RecordConfigReload ¶
RecordConfigReload records a config reload attempt. Outcomes: "no_change", "applied", "error".
func RecordEnrolAttempt ¶
RecordEnrolAttempt records an enrolment attempt by engine and outcome. Engine values pass through classifyEngine in internal/enrol, so the label is one of {"copy","databricks","github","jfrog","ssh","unknown"}. Outcomes emitted today: "completed", "error".
func RecordRemoteConfigFetch ¶ added in v0.22.0
RecordRemoteConfigFetch records a remote-config fetch attempt and where the document resolved from. Outcomes: "fresh" (200), "not_modified" (304 validating the cache), "cache_fallback" (fetch failed, last-known-good used), "base_only" (fetch failed, no usable cache).
func RecordSIGHUP ¶
RecordSIGHUP records a SIGHUP receipt. Each SIGHUP triggers an immediate ~/.dotvault-token re-read via LifecycleManager.Reload plus an immediate config-refresh pass; the counter surfaces how often that path fires.
func RecordSyncDuration ¶
RecordSyncDuration records a sync-cycle duration in seconds.
func RecordSyncTick ¶
RecordSyncTick increments the sync-tick counter with the outcome attribute. The sync engine emits "ok" (every rule succeeded) or "error" (at least one rule failed); per-rule skip cases roll up into "ok" at the cycle level so there's no separate "skipped" outcome to forecast.
func RecordTokenRenewal ¶
RecordTokenRenewal records the outcome of a token renewal attempt. Outcomes emitted today: "renewed", "reauth_required", "failed".
func RecordTokenTTL ¶
RecordTokenTTL records the observed token TTL in seconds. Recorded on every lifecycle check so the histogram captures the renewal-driven sawtooth pattern.
func RecordVaultCall ¶
RecordVaultCall records a single Vault API call with bounded op/status attributes. Pass concrete strings only (no formatted error messages) or the time-series cardinality will explode.
func RecordWebRequest ¶
RecordWebRequest records a web-UI HTTP request. Route is the static route template (e.g. "/api/v1/status"), not the request path — dynamic segments would unbound the cardinality. Status class is the 1xx/2xx/3xx/4xx/5xx bucket; full status codes would similarly inflate cardinality.
Types ¶
type Config ¶
type Config struct {
// Enabled is the master switch. When false, Init returns a no-op
// Provider and the global instruments remain backed by the OTel
// no-op meter.
Enabled bool
// Endpoint is the OTLP collector address shared between metric
// and log exports. For gRPC: "host:port" (e.g. "localhost:4317").
// For HTTP: a *base* URL with no signal-specific path (e.g.
// "https://otel.example") — the exporters append "/v1/metrics"
// and "/v1/logs" themselves. Passing a URL that already ends in
// "/v1/metrics" (or any other signal-specific path) routes both
// signals to the same wrong path on the collector. When empty the
// SDK falls through to OTEL_EXPORTER_OTLP_ENDPOINT.
Endpoint string
// Protocol selects the exporter implementation: "grpc" (default) or
// "http/protobuf".
Protocol string
// Insecure disables transport security for the gRPC exporter
// (HTTP/protobuf carries this via the endpoint scheme).
Insecure bool
// Headers are attached to every export request — useful for
// authenticating to a collector that fronts a vendor backend.
Headers map[string]string
// ExportInterval is the periodic exporter cadence. Zero means the
// SDK default (currently 60s).
ExportInterval time.Duration
// ServiceVersion is the resource attribute used for service.version.
// Pass main.version so the exported series can be partitioned by
// release.
ServiceVersion string
}
Config controls observability wiring. Mirrors config.ObservabilityConfig but is local to this package so the SDK can be initialised without importing the top-level config (avoiding a circular dependency).
type Provider ¶
type Provider struct {
// contains filtered or unexported fields
}
Provider is a thin wrapper over the SDK MeterProvider and LoggerProvider, holding the state needed to flush and shut them down cleanly. The zero value represents an inactive provider whose Shutdown is a no-op — that's what callers get when observability is disabled, so the daemon shutdown path doesn't have to branch on whether Init succeeded.
func Init ¶
Init initialises the OTLP metric and log exporters and installs the global MeterProvider and LoggerProvider. Subsequent calls to package-level instruments (Sync, Vault, Token, …) record into the MeterProvider, and Log* helpers emit through the LoggerProvider. When cfg.Enabled is false, Init returns an inactive Provider whose Shutdown is a no-op and leaves the global meter and logger unchanged (so instruments back off to the OTel no-op meter and log emissions go to the no-op global logger).
func (*Provider) ForceFlush ¶
ForceFlush blocks until the periodic reader and log processor have exported any in-flight records, up to the deadline on ctx. Available for callers that want to flush mid-flight without tearing the provider down; Shutdown already invokes ForceFlush internally, so the one-shot `dotvault sync` and `dotvault run --once` paths rely on their deferred Shutdown rather than calling this directly. No-op for an inactive provider. Returns errors.Join so a collector outage affecting both signals surfaces both failures instead of masking the second one.