UNPKG

@cerbos/opentelemetry

Version:

OpenTelemetry instrumentation for the @cerbos/grpc and @cerbos/http client libraries

97 lines 3.46 kB
import { SpanKind, SpanStatusCode, context, propagation, trace, } from "@opentelemetry/api"; import { ATTR_RPC_GRPC_STATUS_CODE, ATTR_RPC_METHOD, ATTR_RPC_SERVICE, ATTR_RPC_SYSTEM, } from "@opentelemetry/semantic-conventions/incubating"; import { NotOK, Status } from "@cerbos/core"; import { methodName } from "@cerbos/core/~internal"; export class Transport { instrumentation; transport; constructor(instrumentation, transport) { this.instrumentation = instrumentation; this.transport = { unary: transport.unary.bind(transport), serverStream: transport.serverStream.bind(transport), }; } async unary(method, request, headers, abortHandler) { const { call, succeeded, failed } = this.instrument(this.transport.unary, method, headers); try { const output = await call(method, request, headers, abortHandler); succeeded(); return output; } catch (error) { failed(error); throw error; } } async *serverStream(method, request, headers, abortHandler) { const { call, succeeded, failed } = this.instrument(this.transport.serverStream, method, headers); let done = false; try { yield* call(method, request, headers, abortHandler); done = true; succeeded(); } catch (error) { done = true; failed(error); throw error; } finally { if (!done) { failed(abortHandler.error()); } } } instrument(fn, method, headers) { const startTime = performance.now(); const status = { code: SpanStatusCode.UNSET }; const attributes = { [ATTR_RPC_SYSTEM]: "grpc", [ATTR_RPC_SERVICE]: method.parent.typeName, [ATTR_RPC_METHOD]: method.name, }; const span = this.tracer.startSpan(methodName(method), { kind: SpanKind.CLIENT, startTime, }); const activeContext = trace.setSpan(context.active(), span); propagation.inject(activeContext, headers, { set(carrier, key, value) { carrier.set(key, value); }, }); const finish = () => { const endTime = performance.now(); span.setStatus(status); span.setAttributes(attributes); span.end(endTime); this.instruments.duration.record(endTime - startTime, attributes); }; return { call: context.bind(activeContext, fn), succeeded: () => { attributes[ATTR_RPC_GRPC_STATUS_CODE] = Status.OK; finish(); }, failed: (error) => { status.code = SpanStatusCode.ERROR; if (error instanceof Error) { status.message = error.message; attributes["cerbos.error"] = error.message; if (error instanceof NotOK) { attributes[ATTR_RPC_GRPC_STATUS_CODE] = error.code; } } finish(); }, }; } get instruments() { return this.instrumentation["~instruments"]; } get tracer() { return this.instrumentation["~tracer"]; } } //# sourceMappingURL=transport.js.map