@cerbos/opentelemetry
Version:
OpenTelemetry instrumentation for the @cerbos/grpc and @cerbos/http client libraries
97 lines • 3.46 kB
JavaScript
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