feat: opt-in Datadog instrumentation of every KB call (1.2.0) - #107
Merged
Merged
Conversation
fatbeard2
force-pushed
the
feat/datadog-instrumentation
branch
3 times, most recently
from
September 23, 2026 10:46
507156d to
6ea3540
Compare
…dog span (1.2.0) Connect timeouts against KB never produced a Net::HTTP span: faraday-net_http opens the socket before Net::HTTP#request, the method the Datadog tracer patches, so they were invisible to the kb.client.errors metric and the Tech ops dashboard. Observing the transport meant seeing only what the transport's tracer covers. Make KB::Client the observation boundary instead. Every public method now goes through one private `perform` seam that wraps the call (cache lookup, connect, TLS, write, read, parse) in an ActiveSupport::Notifications event, `request.kb_client`, with verb, path, base_url, cache_hit, status and the exception in the payload. The gem core has no Datadog dependency. `KB::Instrumentation::Datadog.subscribe!` registers a start/finish subscriber that turns each event into a `kb.client.request` span inheriting the app's service, with low-cardinality resources (`GET /v1/pets/?/contracts`), peer.service/peer.hostname, kb.cache_hit and http.status_code tags. Works with ddtrace 1.x and datadog 2.x; the tracer stays the app's dependency. With tracing disabled the client behaves as before. Basecamp: https://app.basecamp.com/3934852/buckets/27820160/todos/10331579143 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
fatbeard2
force-pushed
the
feat/datadog-instrumentation
branch
from
September 23, 2026 10:55
6ea3540 to
e45c535
Compare
fatbeard2
marked this pull request as ready for review
September 23, 2026 12:08
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.
Basecamp: https://app.basecamp.com/3934852/buckets/27820160/todos/10331579143
Why?
After Funnel moved to barkibu-kb 1.0.0, connect timeouts against KB (
Faraday::ConnectionFailed: Failed to open TCP connection to knowledge-base-production.herokuapp.com:443 (execution expired)) reached Rollbar but never the Tech ops dashboard. Thekb.client.errorsspan metric is built on the apps'*-httpspans, which come from the Datadog Net::HTTP tracer patchingNet::HTTP#request. faraday-net_http 1.0.1 opens the socket withhttp.startbefore that method, so a connect failure produces no http span at all. The exception lands on whatever span is the parent (therails.cachespan for reads throughKB::Cache, the controller or Sidekiq span for writes), and when the app rescues it, Datadog sees nothing.We were observing KB calls by instrumenting the transport underneath the gem. What we saw depended on the adapter, the Faraday version and the tracer's patch point. Findings and traces: https://app.basecamp.com/3934852/buckets/27820160/todos/10192244285#__recording_10331530328
Changes
KB::Client: every public method (request,all,find,create,update,destroy,upsert) now goes through one privateperformseam, which wraps the call in anActiveSupport::Notificationsevent,request.kb_client(KB::Client::REQUEST_EVENT). Payload:verb,path,base_url,cache_hit(GET only),status, plus ActiveSupport'sexception/exception_objectwhen the call raised. Same cache keys, same params, same error classes; the existing 176 examples pass unchanged. Anything can subscribe (a log line, StatsD, a test); the gem itself has no Datadog dependency.KB::Instrumentation::Datadog(lib/kb/instrumentation/datadog.rb): a start/finish subscriber to that event.subscribe!registers it once; the span opens on event start and closes on finish, so the tracer's Net::HTTP spans nest under it. No prepend, no class modification, so it composes with connected_health's circuit-breaker prepend without ordering concerns. The span:service:given, so the span inherits the app's service (c.service); nothing new appears in the APM service listGET /v1/pets/birthdays,GET /v1/pets/?/contracts(UUID and numeric segments collapsed to?)peer.hostname(which KB host the caller used),kb.method,kb.cache_hit(GET only),http.status_code(also on 4xx/5xx raises), plus Datadog's ownerror.type/error.messagewhen the call raises. Deliberately nospan.kind:clientand nopeer.service: the span covers the client's whole call (cache lookup, connect, parse), so Datadog must not infer it onto the knowledge-base service page as one of KB's operations (verified on staging: with those tags it did)ddtrace1.x (Funnel) anddatadog2.x (Global Admin, connected_health); the tracer gem stays the app's dependency (datadog ~> 2.0is a development dependency here for the specs only)How to test
spec/instrumentation/datadog_spec.rbruns the real tracer in test mode with a transport that discards traces, and covers: resource templating, a successful GET, cache hit on the secondfind, a 404 (span error withhttp.status_code:404), a POST, tracing disabled, and the motivating case: a connect failure against a closed local port, recorded on thekb.client.requestspan witherror.type:Faraday::ConnectionFailed.Rollout
Apps opt in with two lines after
Datadog.configure:Global Admin and Funnel first, then repoint the
kb.client.errorsspan metric tooperation_name:kb.client.request status:errorand the Tech ops dashboard widget.🤖 Generated with Claude Code