Last active
October 1, 2025 19:51
-
-
Save sgtsquiggs/60ef8945b3793dd64e8586d13ffdae2e to your computer and use it in GitHub Desktop.
DataDog tracing interceptor for @connectrpc/connect
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
| import type { Span } from 'dd-trace'; | |
| import { type DescMethod, type DescService } from '@bufbuild/protobuf'; | |
| import { | |
| Code, ConnectError, createContextKey, type Interceptor, | |
| } from '@connectrpc/connect'; | |
| import ddtracer from 'dd-trace'; | |
| import * as opentracing from 'opentracing'; | |
| const getMethodMetadata = (service: DescService, method: DescMethod) => { | |
| const tags = { | |
| kind: '', | |
| name: '', | |
| package: '', | |
| path: `/${service.typeName}/${method.name}`, | |
| service: '', | |
| }; | |
| switch (method.methodKind) { | |
| case 'bidi_streaming': | |
| tags.kind = 'bidi'; | |
| break; | |
| case 'client_streaming': | |
| tags.kind = 'clientStream'; | |
| break; | |
| case 'server_streaming': | |
| tags.kind = 'serverStream'; | |
| break; | |
| case 'unary': | |
| tags.kind = 'unary'; | |
| break; | |
| default: | |
| break; | |
| } | |
| const serviceParts = service.typeName.split('.'); | |
| tags.name = method.name; | |
| tags.service = serviceParts.pop() || ''; | |
| tags.package = serviceParts.join('.'); | |
| return tags; | |
| }; | |
| const addCode = (span: Span, code: Code | string) => { | |
| if (typeof code === 'string') { | |
| span.setTag('grpc.status.code', code); | |
| return; | |
| } | |
| const codeName = Code[code]; | |
| span.setTag('grpc.status.code', codeName.charAt(0).toUpperCase() + codeName.slice(1)); | |
| }; | |
| const metadataFilter = (key: string) => { | |
| if (['content-type', 'grpc-accept-encoding', 'te'].includes(key)) { | |
| return false; | |
| } | |
| if (key.startsWith('x-envoy')) { | |
| return false; | |
| } | |
| return true; | |
| }; | |
| const addMetadataTags = (span: Span, metadata: Headers, type: string) => { | |
| if (typeof metadata.forEach !== 'function') return; | |
| metadata.forEach((value, key) => { | |
| if (metadataFilter(key)) { | |
| span.setTag(`grpc.${type}.metadata.${key}`, value); | |
| } | |
| }); | |
| }; | |
| const inject = (span: Span, metadata: Headers) => { | |
| if (typeof metadata.set !== 'function') return; | |
| const carrier: Record<string, string> = {}; | |
| ddtracer.inject(span, opentracing.FORMAT_TEXT_MAP, carrier); | |
| Object.keys(carrier).forEach((key) => { | |
| metadata.set(key, carrier[key]); | |
| }); | |
| }; | |
| export const SpanContextKey = createContextKey<Span | undefined>(undefined); | |
| export const tracer: Interceptor = (next) => async (req) => | |
| ddtracer.trace('grpc.client', async (span) => { | |
| try { | |
| const method = getMethodMetadata(req.service, req.method); | |
| span.addTags({ | |
| component: 'grpc', | |
| 'grpc.method.kind': method.kind, | |
| 'grpc.method.name': method.name, | |
| 'grpc.method.package': method.package, | |
| 'grpc.method.path': method.path, | |
| 'grpc.method.service': method.service, | |
| 'grpc.status.code': 0, | |
| 'resource.name': method.path, | |
| 'span.kind': 'client', | |
| 'span.type': 'http', | |
| }); | |
| if (method.service && method.package) { | |
| span.setTag('rpc.service', `${method.package}.${method.service}`); | |
| } | |
| addMetadataTags(span, req.header, 'request'); | |
| inject(span, req.header); | |
| req.contextValues.set(SpanContextKey, span); | |
| const res = await next(req); | |
| addCode(span, 'OK'); | |
| addMetadataTags(span, res.header, 'response'); | |
| return res; | |
| } catch (err) { | |
| if (err instanceof ConnectError) { | |
| addCode(span, err.code); | |
| span.setTag('error', err); | |
| addMetadataTags(span, err.metadata, 'response'); | |
| } | |
| throw err; | |
| } | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment