feat(client-v2-otel): add OpenTelemetry span recorder module - #3065
Open
polyglotAI-bot wants to merge 1 commit into
Open
feat(client-v2-otel): add OpenTelemetry span recorder module#3065polyglotAI-bot wants to merge 1 commit into
polyglotAI-bot wants to merge 1 commit into
Conversation
Adds the optional module client-v2-otel with OpenTelemetrySpanRecorder, an implementation of the client-v2 observability SPI that reports operation and transport-request spans to OpenTelemetry. The recorder derives every span name and attribute through SpanSupport, so it reports the standard values, and maps them onto OpenTelemetry: CLIENT spans, an operation span under the current context, a request span per attempt under its operation span, typed attributes, and ERROR status plus an exception event on failure. Implements: #2974
|
Client V2 CoverageCoverage Report
Class Coverage
|
JDBC V2 CoverageCoverage Report
Class Coverage
|
JDBC V1 CoverageCoverage Report
Class Coverage
|
Client V1 CoverageCoverage Report
Class Coverage
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Description
Implements #2974 — PR 2 of 2, on top of the SPI merged in #2988.
Adds the optional module
com.clickhouse:client-v2-otelwithOpenTelemetrySpanRecorder, a consumer of the merged SPI. It is registered like any other recorder:No change to
client-v2code: the module only implementsSpanRecorderand opts in toSpanSupportfor the standard names and attributes, so it reports the same information as any other recorder.Design
OpenTelemetrySpanRecorder extends DefaultSpanRecorder(packagecom.clickhouse.client.api.observability.otel). Everystart...method takes the name fromSpanSupport(querySpanName/insertSpanName/requestSpanName) and the attributes fromfill*Attributes; everyrecord...method delegates to the matchingSpanSupportmethod. Nothing is recomputed here.new OpenTelemetrySpanRecorder(openTelemetry),OpenTelemetrySpanRecorder.forTracer(tracer)(application-chosen instrumentation scope), ornew OpenTelemetrySpanRecorder()forGlobalOpenTelemetry. The global instance is read when a span is started, not in the constructor, so a client may be built before the application installs its SDK (reading it too early would pin the no-op instance and make a laterGlobalOpenTelemetry.set(...)throw). Default scope name:com.clickhouse.client.Context.current(), so it joins the application's ambient trace; each request span — one per attempt, including retries — is started under its operation span's context. A request span whose operation span was not created by this recorder falls back to the current context instead of failing. Both kinds areSpanKind.CLIENT.String→ string,Boolean→ boolean,Double/Float→ double, any otherNumber→ long, anything else →String.valueOf. Anullkey or value records nothing.setErrorsets statusERRORand recordserror.type;recordFailure/recordRequestFailureadditionally record the throwable as an OpenTelemetry exception event, so the message and stack trace are not lost (the SPI hands the recorder the throwable;error.typealone drops everything but the class name).end()is idempotent (AtomicBoolean), matching the SPI contract; the recorder holds no per-operation state and is thread-safe.Dependency placement.
opentelemetry-apiis a normal dependency of this module only;client-v2is untouched and still needs no OpenTelemetry on the classpath, which is the issue's "no new runtime dependency" constraint. The module is not added topackages/clickhouse-jdbc-all— that would shade OpenTelemetry into the uber-jar for every JDBC user, andjdbc-v2has no way to configure a recorder yet (see Follow-ups). Say the word if you want it in the package anyway; it is a two-line change.Compatibility: purely additive — a new module and one new public class. No existing signature or behaviour changed. Java 8 (
release 8).Changes
client-v2-otel/pom.xml— new module: depends onclient-v2+opentelemetry-api; test scope addsopentelemetry-sdk,opentelemetry-sdk-testing, TestNG and theclickhouse-clienttest-jar for the integration harness.client-v2-otel/.../observability/otel/OpenTelemetrySpanRecorder.java— the recorder and itsSpanimplementation.pom.xml— new<module>client-v2-otel</module>and theopentelemetry.versionproperty (1.51.0).CHANGELOG.md,docs/features.md— newclient-v2-otelsection, including the compatibility-sensitive traits (span kind/nesting, attribute typing, failure mapping, idempotent end, no span made current).Test
New tests only; no existing test edited or weakened.
OpenTelemetrySpanRecorderUnitTest(25 cases, in-memory exporter): query span name/kind/scope and every standard attribute; insert span withdb.collection.name+db.operation.batch.size, and a contrast case that a stream insert (BATCH_SIZE_UNKNOWN) records no batch size and an insert records nodb.query.text; request span is a child of the operation span withhttp.request.method/http.response.status_code/ per-attemptserver.address; two attempts under one operation span (failed attempt isERROR, retry isUNSET, operation staysUNSET); operation span joins an ambient trace; foreign operation span → current context (with and without an ambient span); client failure →ERROR+error.typeand no server error code;ServerException→error.type,db.response.status_code=60,http.response.status_code=404on the request span and the operation span; success records the query id anddb.response.returned_rows, and records nothing when metrics arenull; failure recorded as an exception event with type and message; idempotentend(); attribute typing via@DataProvider(8 rows: String / Boolean / int / long / short / double / float / other object);nullkey or value ignored;forTracerreports under the given scope name and version;nullOpenTelemetry/Tracerrejected.OpenTelemetrySpanRecorderTest(3 integration cases, real server): a successful query exports the operation span withdb.response.returned_rows=3, the server-assigned query id and a childPOSTspan with HTTP 200; a failing query exportsERROR+error.type=…ServerException+db.response.status_code=60on the operation span and HTTP 404 on the request span; a POJO insert exportsinsert <db>.<table>withdb.operation.batch.size=1and its child request span.mvn -pl client-v2-otel -DskipITs=true test→ 25 passed;mvn -pl client-v2-otel -DskipUTs=true -Dit.test=OpenTelemetrySpanRecorderTest verify→ 3 passed;mvn -pl client-v2 -DskipITs=true test→ 556 passed (unchanged);mvn -Dj8 -DskipTests install(full reactor, incl.jdbc-v2andpackages/clickhouse-jdbc-all) → BUILD SUCCESS.Docs / surface
CHANGELOG.md: entry under0.11.0-rc1→ New Features, tagged**[client-v2-otel]**, with the issue link. The feat(client-v2): add span recorder SPI for operation and request tracing #2988 entry's closing sentence now points at this module instead of announcing it as upcoming.docs/features.md: new## client-v2-otelsection with a feature list and compatibility-sensitive traits.0.11.0-rc1). No backport needed.docs/changes_checklist.mdwalk-throughOpenTelemetrySpanRecorderfollows the module's naming and the SPI's documented extension pattern (extendDefaultSpanRecorder, override what you record); nullability is explicit (nullOpenTelemetry/Tracerrejected withIllegalArgumentException,nullattribute key/value ignored);forTracer(...)is a static factory rather than a second constructor sonew OpenTelemetrySpanRecorder(null)cannot be an ambiguous call; behaviour-focused tests added;docs/features.mdupdated.opentelemetry-api), scoped to the new module only, version pinned by a parent property next to the other version properties.client-v2andclickhouse-jdbc-allgain nothing transitively.nullchecks and theinstanceoffallback for a foreign operation span; both are covered by tests.CI note (no workflow file touched, per
AGENTS.md)The whole-reactor
compilejob builds the new module and runs its unit tests, so they gate this PR. Two things need a workflow change, which I did not make:build.yml/test_head.ymlenumerate projects explicitly (project: ["clickhouse-http-client", "client-v2", …]), so the module's integration test does not run in CI untilclient-v2-otelis added to those matrices.release.ymlenumerates the jars attached to a release, so the new artifact must be added there before it is published.Tell me which you want and I will add it in a follow-up (or apply it here if you prefer a CI change in this PR).
Pre-PR validation gate
Client)docs/features.md+CHANGELOG.mdupdatedAGENTS.md,docs/ai-review.mdanddocs/changes_checklist.mdFollow-ups
jdbc-v2surfacing — still the open question from #2988:jdbc-v2builds itsClientfrom string properties, so injecting a recorder needs its own small decision (aspan_recorderdriver property naming a class to instantiate, or a setter onDataSourceImpl). @chernser which would you like? I kept it out of this PR so the recorder itself can land independently.