Skip to content

fix(telemetry): report tracesUrl and explain trace disconnects - #534

Merged
mayankpande88 merged 2 commits into
mainfrom
fix/agent-traces-url-and-connection-error
Jul 16, 2026
Merged

fix(telemetry): report tracesUrl and explain trace disconnects#534
mayankpande88 merged 2 commits into
mainfrom
fix/agent-traces-url-and-connection-error

Conversation

@RamanKharchee

Copy link
Copy Markdown
Contributor

Summary

Two related telemetry gaps: the agent never reported tracesUrl, and never explained why traces were disconnected.

1. tracesUrl was always empty for otel_clickhouse clusters. The legacy sink passed clickhouse_url (CLICKHOUSE_HOST) into get_trace_url() (nudgebee_sink.py:1381). The Go port hardcoded that path to "", with a comment claiming "we don't run a local ClickHouse, so it's always ''" — stale, since main.go does read and probe CLICKHOUSE_HOST.

That left agent_service.go:235's TracesUrl != nil gate permanently closed, so TraceProviderConfig["otel_traces"] was never written and Last9 clusters resolved table otel_traces instead of otel.traces. tool_trace_clickhouse.go:136 missed the same last9.io check. Standard ClickHouse was unaffected — GetTracesTableNames defaults to the same value.

Adds isClickHouseEnabled as the otel_clickhouse counterpart of isJaegerEnabled, and reports the host.

2. tracesConnectionError was declared and rendered, but emitted by nobody. Declared at agent_service.go:94, read at agentHealth.jsx:198, rendered by renderReason(!isTracesManagerConnected, tracesError). Since renderReason needs disconnected && error and the value was permanently '', the "Reason - ..." line under a Disconnected Traces pill has never once rendered. probeClickhouse now returns a reason alongside its status.

Condition tracesConnectionError
ClickHouse healthy ""
Ping fails ClickHouse ping failed at ch.svc:8123: connection refused
CLICKHOUSE_HOST unset CLICKHOUSE_HOST is not set: no traces backend is configured
TRACES_ENABLED=false "" — off on purpose isn't a failure
Traces up via any provider ""

No backend or UI change needed; both consumers already exist.

Type of change

  • Bug fix (non-breaking)

Chart version

  • No version bump needed (docs/CI only)

Runner-only Go change, no chart templates touched. The closest precedent — #517, which also added a telemetry field — didn't bump either. Shout if that's wrong and I'll add one.

Test plan

  • go build ./..., go vet, and the full agent suite pass (relevant: the httpProbe split below touches every probe caller)
  • New unit tests: probe reason matrix, redactUserinfo, reason-clears-on-recovery, and a marshalling test pinning the no-omitempty contract
  • Not installed on a real cluster — verification is build + tests only

helm lint / ct lint / helm template not applicable (no chart changes).

Review Notes → Risks & Counterarguments

httpProbe refactor touches every probe caller. Split into a thin bool wrapper over a new httpProbeErr that preserves the error, so Grafana/OpenCost/logs callers are untouched by construction. Full suite passes. Behaviour is identical, but it's the widest-blast-radius hunk here.

Two deliberate deviations from legacy parity, both documented in-code:

  • ClickHouse is checked last in traceURL, not first. The legacy order returns the ClickHouse host even when TRACE_TABLE makes the provider bigquery, and the backend then backtick-quotes that host as a BigQuery table name.
  • isClickHouseEnabled is deliberately not used by traceStatus. Gating tracesEnabled on the URL would turn traces off for TRACES_ENABLED=true-without-host — a config main.go:1288 explicitly supports for external ClickHouse the agent can't probe.

The no-omitempty on tracesConnectionError is load-bearing. The collector merges into connection_status with jsonb || (telemetry_handler.py:267), so keys are sticky. An omitted key would strand a stale reason in the DB forever, resurfacing under any later failure that produces no message. A recovered ClickHouse must post an explicit "" to clear it. There's a marshalling test guarding this, because it's exactly what a future "add omitempty" cleanup would silently break.

Newly-activated backend branch. Populating tracesUrl makes agent_service.go:235 pass its gate for the first time and start writing TraceProviderConfig["otel_traces"]. For standard ClickHouse the written value equals the existing default, so no behaviour change; Last9 correctly switches to otel.traces. I traced the consumers but haven't exercised this against a live agent.

Credential exposure was a real risk here. The reason string lands in connection_status JSON and renders verbatim in the UI, and CLICKHOUSE_HOST may be a full URL. redactUserinfo strips user:pass@ and probeCause unwraps *url.Error so the request URL never reaches the string. Belt-and-braces — net/http already strips passwords — but the username survived that.

Pre-existing bug, deliberately not fixed: probeClickhouse builds fmt.Sprintf("http://%s:%s/ping", host, port), which breaks for a URL-form CLICKHOUSE_HOST (http://https://x:8123/ping); pkg/clickhouse.normalizeHost exists precisely for that. Those clusters already reported traces disconnected — the change is that they now get a "ping failed" reason that's true but points at the wrong culprit. In practice Last9-style setups sidestep it via TRACES_ENABLED=true. Worth a follow-up routing the probe through normalizeHost.

Related issues

Fixes nudgebee/nudgebee-enterprise#34231
Refs nudgebee/nudgebee-enterprise#34172 — possibly related (Traces page null for clickhouse otel), deliberately not linked as a fix. This only changes real behaviour for Last9 hosts, so it's unlikely to be that root cause unless that env is Last9-backed.

🤖 Generated with Claude Code

@RamanKharchee
RamanKharchee requested a review from a team as a code owner July 15, 2026 10:12

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances the ClickHouse probing mechanism to capture and report specific connection errors when traces are down, propagating them to the telemetry payload. The feedback highlights a potential bug where full URLs in CLICKHOUSE_HOST could result in malformed probe URLs, and suggests robust URL parsing to handle this. Additionally, it is recommended to fully drain the HTTP response body in httpProbeErr to prevent connection leaks, along with adding the necessary imports.

Comment thread runner/cmd/agent/main.go
Comment thread runner/cmd/agent/main.go
Comment thread runner/cmd/agent/main.go
mayankpande88
mayankpande88 previously approved these changes Jul 16, 2026
The agent never populated tracesUrl, and never said why traces were down.

tracesUrl: the legacy sink passed clickhouse_url (CLICKHOUSE_HOST) into
get_trace_url(); the Go port hardcoded that path to "" on the premise that we
no longer run a local ClickHouse. main.go does read and probe CLICKHOUSE_HOST,
so the premise was stale and the field shipped empty for every otel_clickhouse
cluster. That left agent_service.go's `TracesUrl != nil` gate permanently
closed, so the trace-table config was never written and Last9 clusters resolved
`otel_traces` instead of `otel.traces`. Add isClickHouseEnabled as the
otel_clickhouse counterpart of isJaegerEnabled, and report the host.

ClickHouse is checked last in traceURL rather than first as in the legacy: the
legacy order returns the ClickHouse host even when TRACE_TABLE makes the
provider bigquery, and the backend then quotes that host as a BigQuery table.
It is also deliberately not used by traceStatus — gating tracesEnabled on the
URL would turn traces off for TRACES_ENABLED=true-without-host, a config the
agent supports for external ClickHouse it cannot probe.

tracesConnectionError: the field was declared (agent_service.go) and rendered
(agentHealth.jsx renderReason) but emitted by nobody, so a Disconnected Traces
pill never showed a reason. probeClickhouse now returns one alongside the
status; httpProbe is split over a new httpProbeErr so the failure survives,
leaving its other callers untouched. Credentials in a URL-form CLICKHOUSE_HOST
are stripped before the reason ships, since it renders verbatim in the UI.

The field intentionally omits `omitempty`: the collector merges activity_stats
into connection_status with jsonb `||`, so an omitted key leaves the previous
value in place. A recovered ClickHouse must post an explicit "" to clear the
stale reason.

Fixes #34231

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mayankpande88
mayankpande88 force-pushed the fix/agent-traces-url-and-connection-error branch from aed1622 to 5cf4e2b Compare July 16, 2026 05:17
@github-actions

Copy link
Copy Markdown
Contributor

📦 Image Tags Updated

I've automatically updated the image tags in `charts/nudgebee-agent/values.yaml` to the latest versions from GHCR for the `main` branch.

The image tags are now synchronized with the latest builds and ready for release.

@mayankpande88
mayankpande88 merged commit 585a625 into main Jul 16, 2026
7 checks passed
@mayankpande88
mayankpande88 deleted the fix/agent-traces-url-and-connection-error branch July 16, 2026 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants