UNPKG

@opentelemetry/instrumentation-grpc

Version:
165 lines 6.92 kB
"use strict"; /* * Copyright The OpenTelemetry Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.shouldNotTraceServerCall = exports.handleUntracedServerFunction = exports.handleServerFunction = exports.CALL_SPAN_ENDED = void 0; /** * Symbol to include on grpc call if it has already emitted an error event. * grpc events that emit 'error' will also emit 'finish' and so only the * error event should be processed. */ const node_events_1 = require("node:events"); const api_1 = require("@opentelemetry/api"); const utils_1 = require("./utils"); const AttributeNames_1 = require("./enums/AttributeNames"); const semconv_1 = require("./semconv"); exports.CALL_SPAN_ENDED = Symbol('opentelemetry call span ended'); /** * Handle patching for serverStream and Bidi type server handlers */ function serverStreamAndBidiHandler(span, call, original) { let spanEnded = false; const endSpan = () => { if (!spanEnded) { spanEnded = true; span.end(); } }; api_1.context.bind(api_1.context.active(), call); call.on('finish', () => { // @grpc/js does not expose a way to check if this call also emitted an error, // e.g. call.status.code !== 0 if (call[exports.CALL_SPAN_ENDED]) { return; } // Set the "grpc call had an error" flag call[exports.CALL_SPAN_ENDED] = true; span.setStatus({ code: api_1.SpanStatusCode.UNSET, }); span.setAttribute(semconv_1.ATTR_RPC_GRPC_STATUS_CODE, semconv_1.RPC_GRPC_STATUS_CODE_VALUE_OK); endSpan(); }); call.on(node_events_1.errorMonitor, (err) => { if (call[exports.CALL_SPAN_ENDED]) { return; } // Set the "grpc call had an error" flag call[exports.CALL_SPAN_ENDED] = true; span.setStatus({ code: (0, utils_1._grpcStatusCodeToOpenTelemetryStatusCode)(err.code), message: err.message, }); span.setAttributes({ [AttributeNames_1.AttributeNames.GRPC_ERROR_NAME]: err.name, [AttributeNames_1.AttributeNames.GRPC_ERROR_MESSAGE]: err.message, [semconv_1.ATTR_RPC_GRPC_STATUS_CODE]: err.code, }); endSpan(); }); // TODO: Investigate this call/signature – it was inherited from very old // code and the `this: {}` is highly suspicious, and likely isn't doing // anything useful. There is probably a more precise cast we can do here. // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type return original.call({}, call); } /** * Handle patching for clientStream and unary type server handlers */ function clientStreamAndUnaryHandler(span, call, callback, original) { const patchedCallback = (err, value) => { if (err) { if (err.code) { span.setStatus({ code: (0, utils_1._grpcStatusCodeToOpenTelemetryStatusCode)(err.code), message: err.message, }); span.setAttribute(semconv_1.ATTR_RPC_GRPC_STATUS_CODE, err.code); } span.setAttributes({ [AttributeNames_1.AttributeNames.GRPC_ERROR_NAME]: err.name, [AttributeNames_1.AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: api_1.SpanStatusCode.UNSET }); span.setAttribute(semconv_1.ATTR_RPC_GRPC_STATUS_CODE, semconv_1.RPC_GRPC_STATUS_CODE_VALUE_OK); } span.end(); return callback(err, value); }; api_1.context.bind(api_1.context.active(), call); // TODO: Investigate this call/signature – it was inherited from very old // code and the `this: {}` is highly suspicious, and likely isn't doing // anything useful. There is probably a more precise cast we can do here. // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type return original.call({}, call, patchedCallback); } /** * Patch callback or EventEmitter provided by `originalFunc` and set appropriate `span` * properties based on its result. */ function handleServerFunction(span, type, originalFunc, call, callback) { switch (type) { case 'unary': case 'clientStream': case 'client_stream': return clientStreamAndUnaryHandler(span, call, callback, originalFunc); case 'serverStream': case 'server_stream': case 'bidi': return serverStreamAndBidiHandler(span, call, originalFunc); default: break; } } exports.handleServerFunction = handleServerFunction; /** * Does not patch any callbacks or EventEmitters to omit tracing on requests * that should not be traced. */ function handleUntracedServerFunction(type, originalFunc, call, callback) { switch (type) { case 'unary': case 'clientStream': case 'client_stream': // TODO: Investigate this call/signature – it was inherited from very old // code and the `this: {}` is highly suspicious, and likely isn't doing // anything useful. There is probably a more precise cast we can do here. // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type return originalFunc.call({}, call, callback); case 'serverStream': case 'server_stream': case 'bidi': // TODO: Investigate this call/signature – it was inherited from very old // code and the `this: {}` is highly suspicious, and likely isn't doing // anything useful. There is probably a more precise cast we can do here. // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type return originalFunc.call({}, call); default: break; } } exports.handleUntracedServerFunction = handleUntracedServerFunction; /** * Returns true if the server call should not be traced. */ function shouldNotTraceServerCall(methodName, ignoreGrpcMethods) { const parsedName = methodName.split('/'); return (0, utils_1._methodIsIgnored)(parsedName[parsedName.length - 1] || methodName, ignoreGrpcMethods); } exports.shouldNotTraceServerCall = shouldNotTraceServerCall; //# sourceMappingURL=serverUtils.js.map