feat(collector): derive request metrics with trace exemplars from spans - #2805
feat(collector): derive request metrics with trace exemplars from spans#2805jordan-simonovski wants to merge 3 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: a46bf56 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
🔴 Tier 4 — CriticalTouches authentication, tenancy data models, the public API or shipped database config — or substantially changes background tasks, the OTel pipeline, image build, or release CI. Why this tier:
Additional context: touches background tasks or the delivery pipeline lightly (3 lines, under the 30-line bar for Tier 4) Review process: Deep review from a domain expert. Synchronous walkthrough may be required. Stats
|
Greptile SummaryThe PR adds opt-in span-derived request metrics with trace exemplars and an optional Prometheus remote-write sink.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| packages/api/src/config.ts | Adds opt-in spanmetrics flags and validates the optional remote-write URL before exposing it to generated collector configuration. |
| packages/api/src/opamp/controllers/opampController.ts | Generates the spanmetrics connector and metrics pipeline with bounded aggregation, exponential histograms, exemplars, and optional verified remote write. |
| packages/api/src/opamp/controllers/tests/opampController.test.ts | Covers disabled and enabled configurations, exporter wiring, component registration, bootstrap compatibility, cardinality controls, and endpoint absence. |
| packages/api/src/tests/config.test.ts | Verifies acceptance of HTTP(S) endpoints and rejection of URL userinfo, unsupported schemes, malformed values, and missing configuration. |
| packages/otel-collector/builder-config.yaml | Registers the published spanmetrics connector module in the custom collector binary. |
| packages/otel-collector/README.md | Documents rollout ordering, cardinality controls, exemplars, and optional Prometheus remote write. |
| .changeset/spanmetrics-connector.md | Records the API and collector behavior change as minor releases. |
| .gitignore | Excludes Stryker mutation-testing output from future changes. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
Spans[OTLP spans] --> Traces[Traces pipeline]
Traces --> ClickHouseTraces[ClickHouse trace export]
Traces --> SpanMetrics[spanmetrics connector]
SpanMetrics --> MetricsPipeline[metrics/spanmetrics pipeline]
MetricsPipeline --> ClickHouseMetrics[ClickHouse metrics export]
MetricsPipeline -. optional .-> Prometheus[Prometheus remote write]
SpanMetrics -. trace and span IDs .-> Exemplars[Metric exemplars]
Reviews (4): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile
E2E Test Results✅ All tests passed • 270 passed • 1 skipped • 965s
Tests ran across 4 shards in parallel. |
Adds spanmetricsconnector to the collector build and wires it into the generated OpAMP config behind ENABLE_SPAN_METRICS, off by default. The connector consumes the traces pipeline and feeds a dedicated metrics pipeline, so traces.span.metrics.* reach ClickHouse with Exemplars.* pointing back at the spans they came from. Exponential histogram buckets rather than a fixed ladder. An explicit ladder puts everything slow into one wide top bucket, so a high quantile interpolates past the slowest real request and no exemplar can ever sit on the plotted line. ENABLE_SPAN_METRICS_PROM_RW additionally remote-writes the derived metrics to a Prometheus endpoint. The endpoint is resolved API-side and inlined into the generated config, so the collector container does not need it in its own environment, and the flag stays off unless the endpoint is set. The connector key must be `spanmetrics` — that is the component type spanmetricsconnector registers. A config naming an unregistered type fails to decode as a whole, and docker/otel-collector/config.yaml supplies no pipelines of its own, so a typo here stops all ingestion rather than just disabling this feature. A test pins every generated component id against builder-config.yaml so that cannot ship again. This is deliberately separate from the UI that consumes these metrics: it changes what the collector runs and has its own rollout ordering. Roll the collector image out before enabling the flag.
3c90e0f to
fa5a66d
Compare
|
<!-- deep-review --> Deep Review✅ No critical issues found. Nothing here breaks on the happy path: both flags default off, so merging this diff is a no-op at runtime. The P2 items below are worth resolving before enabling the flag anywhere. 🟡 P2 -- recommended
🔵 P3 nitpicks (6)
Reviewers (1): orchestrator direct analysis. Seven personas (correctness, testing, adversarial, maintainability, project-standards, performance, security) were dispatched but had not returned before output was required, so every finding above was verified by the orchestrator directly against the cited lines; none are attributed to agent output. Environment caveat: the Testing gaps:
|
…pipeline stub Addresses review findings on the spanmetrics connector. Cardinality. Dimensions dropped `app.tenant_id` and raw `host.region`, and now use the stable HTTP semconv spellings — `http.method`/`http.status_code` are pre-1.23 and absent from current SDKs. Added an `aggregation_cardinality_limit`, plus `resource_metrics_key_attributes`: the limit applies per resource-cache entry, so with the default key (every resource attribute, including per-pod ones) the real ceiling was the cache size times the limit, not the limit. Pipeline processors. The first attempt moved `processors:` into the bootstrap config to respect the rule in #2351, which was wrong: the supervisor merges the bootstrap config unconditionally, the pipeline only exists when the flag is on, and the collector rejects a pipeline with no receivers or exporters — failing the whole config. With the flag off, which is the default, every agent would have failed to start. Standalone mode would have failed unconditionally. Set inline instead, as `metrics/promql` already does, and pinned by a test that every bootstrap-declared pipeline is filled in by the generated config. Remote-write exporter. Dropped `tls.insecure`: on an HTTP exporter the URL scheme decides whether TLS is used and `insecure` only means anything to gRPC, so it was inert rather than weakening anything. Keyed the exporter off the endpoint rather than the flag, so a missing endpoint cannot emit a config that fails to decode. The generated config is served from the unauthenticated OpAMP endpoint, so an endpoint URL carrying credentials is now rejected, as are non-HTTP schemes. Turned off resource-to-telemetry conversion, which happens after the cardinality limit and would send host, pod and namespace to a third party. Also tightened the component-registration test to match the component kind rather than any kind, and documented the feature in the collector README.
Deep Review
✅ No critical issues found. No P0/P1: nothing here is a guaranteed happy-path break, and the whole feature is gated off by default ( 🟡 P2 -- recommended
🔵 P3 nitpicks (5)
Reviewers (1): manual single-pass review by the orchestrator — the plugin's persona sub-agents (correctness, testing, maintainability, project-standards, plus cross-cutting) could not be dispatched usefully because Coverage limits:
Testing gaps:
|
…ector-spanmetrics
|
Closing this. The connector is not required for the exemplars feature. Nothing in #2806–#2809 depends on it — the only reference anywhere in the stack is the string The branch
|
First of five PRs replacing #2536, which grew to 85 files and four rounds of review.
Why this is on its own
This changes what the collector runs, and it has its own rollout ordering. A round-1 review on the original PR made the point directly: a config naming a component type the binary does not register fails to decode as a whole, and the shipped
docker/otel-collector/config.yamldefines no pipelines of its own — so a bad type name leaves the collector with nothing to run and stops all ingestion, not just this feature. That is not something to ship in the same revert unit as a flag-gated UI overlay.Merge and roll out the collector image before enabling the flag.
What it does
Adds
spanmetricsconnectorto the collector build and wires it into the generated OpAMP config behindENABLE_SPAN_METRICS, off by default. The connector consumes the traces pipeline and feeds a dedicated metrics pipeline, sotraces.span.metrics.*reach ClickHouse withExemplars.*pointing back at the spans they were measured from.Buckets are exponential rather than a fixed ladder. An explicit ladder puts everything slow into one wide top bucket, so a high quantile interpolates well past the slowest real request — and then no exemplar can sit on the plotted line, which defeats the point of the metrics.
ENABLE_SPAN_METRICS_PROM_RWadditionally remote-writes the derived metrics to a Prometheus endpoint, for exercising Prometheus's native exemplar path. The endpoint is resolved API-side and inlined into the generated config, so the collector container does not need it in its own environment; the flag stays off unless the endpoint is set.Note for the reviewer
The connector key must be exactly
spanmetrics. This was a P0 on the original PR — the config saidspan_metrics, which the binary does not register. There is now a test that readsbuilder-config.yamland pins every generated component id against what the build actually registers, including pipeline processor references. It fails if the name regresses.Verification
make ci-lintandmake ci-unitpass. The OpAMP config tests cover the flag off, the flag on, the remote-write variant, and the component-type pinning.