feat: add OpenTelemetry metrics - #3163
Conversation
There was a problem hiding this comment.
I'm Bonk, and I've done a quick review of your PR.
This PR adds opt-in OpenTelemetry metrics (runtime + model), a metrics signal to cog.yaml, custom MeterProvider support via telemetry.py, and the parent/worker plumbing to export bounded prediction, rejection, setup, active, duration, and slot-state metrics.
Overall the design is coherent and well-tested (Go tests pass; Rust unit tests cover the registry, slot-state accounting, and protocol compat). I found two issues in the new runtime-metrics accounting worth addressing.
|
Review posted. Both inline findings were submitted successfully on PR #3163. Summary of review: I reviewed the full 5,300-line diff of this PR (Go config/dockerfile/image plumbing, Rust coglet runtime-metrics, Python SDK telemetry module, docs, and integration tests). Go tests for Two actionable findings were posted as inline comments (both with suggestions):
Everything else — the protocol |
|
I've completed a thorough review of the current state of PR #3163 (head I verified:
LGTM |
|
Verified: the only change since the previous LGTM review at LGTM |
|
LGTM |
|
I've reviewed the two commits pushed after the last LGTM review (
Docs ( No actionable issues found. LGTM |
Fix a select! busy-spin when the orchestrator handle drops, order slot state transitions before publishing permits, take a read lock on the rejection hot path, and record terminal counts for predictions that end before processing. Add value assertions for runtime metrics, fix doc inaccuracies, and dedupe the tracing extra behind observability.
|
LGTM |
Summary
This adds opt-in OpenTelemetry metrics to Cog. The Rust parent exports fixed runtime metrics, and the Python worker gets a standard
MeterProviderfor model metrics.Metrics are off by default. Existing response metrics do not change, and exporter failures do not affect predictions.
User experience
Enable metrics in
cog.yaml:The boolean form
metrics: trueis accepted as shorthand.Collector settings stay at runtime:
No Python configuration is required for the defaults.
metrics.enabled: truecreates the runtime and Python providers when an OTLP endpoint is present.Runtime metrics
The parent exports six instruments:
cog.runtime.prediction.count{prediction}operation,statuscog.runtime.prediction.rejected{prediction}operation,reasoncog.runtime.prediction.active{prediction}operationcog.runtime.prediction.durationsoperation,statuscog.runtime.setup.durationsstatuscog.runtime.slot.count{slot}stateThe attribute values are closed sets:
operation:predictortrainstatus:succeeded,failed, orcanceledreason:invalid_input,not_ready, orat_capacitystate:available,busy, orpoisonedPrediction and setup histograms use fixed bucket boundaries. Request IDs, slot IDs, model inputs and outputs, and error text are not metric attributes.
Model metrics
Cog installs the Python provider before importing the model. Model code uses the normal OpenTelemetry API:
These instruments are separate from
record_metric(), which continues to populate the prediction response.The parent and worker providers can send to the same collector, but they remain independent. A custom Python provider does not replace the parent provider, and the worker does not duplicate the runtime metrics.
Custom Python telemetry
observability.configcan customize either Python signal:Factories are optional. If a factory is missing, Cog uses its default for that signal.
Cog loads this module before model import, validates returned providers, and owns flush and shutdown. Existing zero-argument tracer factories still work. Invalid custom factories or instrumentation fail setup instead of falling back silently.
Runtime metric selection
Models can disable Cog metrics without replacing the parent provider:
RuntimeMetricsConfig(enabled=False)disables all runtime metrics but leaves the Python provider available. Runtime instruments cannot be renamed or relabeled fromtelemetry.py.The worker sends the selection to the parent in its optional
Readyfield. Older workers that omit the field use defaults. If telemetry configuration fails before producing a valid selection, the failed setup metric also uses defaults.Accounting and lifecycle
Prediction totals, durations, and active decrements run from the guarded terminal setters. This prevents racing success, failure, and cancellation paths from recording twice.
Slot counts come from an explicit parent-owned state table. Available, busy, and poisoned counts remain disjoint and sum to configured concurrency.
The prediction dispatch path now unregisters stale routing state on every early return. An unstarted permit goes back to the pool only after unregister completes. Once a socket send begins, cancellation poisons the slot instead of making it available for another request.
Providers are flushed during graceful shutdown. Missing endpoints and invalid built-in exporter settings log a warning and leave serving unaffected. Generic HTTP endpoints receive
/v1/metrics; signal-specific endpoints are used as provided.Documentation and examples
The observability guide now covers tracing and metrics together. The YAML, environment, Python, build, and runtime references have also been updated.
examples/hello-concurrencyshows custom trace and meter providers, a model counter, and runtime metric selection.OpenTelemetry log export is not part of this PR.