nice-grpc-opentelemetry
Version:
OpenTelemetry instrumentation for nice-grpc
112 lines (98 loc) • 2.81 kB
text/typescript
import {
context as contextApi,
propagation,
ROOT_CONTEXT,
Span,
SpanKind,
SpanStatusCode,
} from '@opentelemetry/api';
import {
MESSAGETYPEVALUES_RECEIVED,
MESSAGETYPEVALUES_SENT,
} from '@opentelemetry/semantic-conventions';
import {isAbortError} from 'abort-controller-x';
import {
CallContext,
ServerError,
ServerMiddleware,
ServerMiddlewareCall,
Status,
} from 'nice-grpc-common';
import {
getMethodAttributes,
getPeerAttributes,
getStatusAttributes,
} from './attributes';
import {metadataGetter} from './propagation';
import {emitSpanEvents, getSpanName, tracer} from './traces';
import {bindAsyncGenerator} from './utils/bindAsyncGenerator';
export function openTelemetryServerMiddleware(): ServerMiddleware {
return (call, context) =>
tracer.startActiveSpan(
getSpanName(call.method.path),
{
kind: SpanKind.SERVER,
},
propagation.extract(ROOT_CONTEXT, context.metadata, metadataGetter),
span =>
bindAsyncGenerator(
contextApi.active(),
openTelemetryServerMiddlewareGenerator(span, call, context),
),
);
}
async function* openTelemetryServerMiddlewareGenerator<Request, Response>(
span: Span,
call: ServerMiddlewareCall<Request, Response>,
context: CallContext,
): AsyncGenerator<Response, Response | void, undefined> {
const attributes = {
...getMethodAttributes(call.method.path),
...getPeerAttributes(context.peer),
};
span.setAttributes(attributes);
let status: Status = Status.OK;
let errorMessage: string | undefined;
try {
let request;
if (!call.requestStream) {
request = call.request;
} else {
request = emitSpanEvents(call.request, span, MESSAGETYPEVALUES_RECEIVED);
}
if (!call.responseStream) {
return yield* call.next(request, context);
} else {
yield* emitSpanEvents(
call.next(request, context),
span,
MESSAGETYPEVALUES_SENT,
);
return;
}
} catch (err: unknown) {
if (err instanceof ServerError) {
status = err.code;
errorMessage = err.details;
} else if (isAbortError(err)) {
status = Status.CANCELLED;
errorMessage = 'The operation was cancelled';
} else {
status = Status.UNKNOWN;
errorMessage = 'Unknown server error occurred';
span.recordException(err as any);
}
throw err;
} finally {
const statusAttributes = getStatusAttributes(status);
span.setAttributes(statusAttributes);
// https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/rpc/#grpc-status
if (status !== Status.OK) {
span.setStatus({
code: SpanStatusCode.ERROR,
message: `${Status[status]}: ${errorMessage}`,
});
}
span.end();
}
}