UNPKG

@sentry/google-cloud-serverless

Version:
110 lines (100 loc) • 3.33 kB
import { _optionalChain } from '@sentry/utils'; import { defineIntegration, getClient, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; import { startInactiveSpan } from '@sentry/node'; import { fill } from '@sentry/utils'; const SERVICE_PATH_REGEX = /^(\w+)\.googleapis.com$/; const INTEGRATION_NAME = 'GoogleCloudGrpc'; const SETUP_CLIENTS = new WeakMap(); const _googleCloudGrpcIntegration = ((options = {}) => { const optional = options.optional || false; return { name: INTEGRATION_NAME, setupOnce() { try { // eslint-disable-next-line @typescript-eslint/no-var-requires const gaxModule = require('google-gax'); fill( gaxModule.GrpcClient.prototype, // eslint-disable-line @typescript-eslint/no-unsafe-member-access 'createStub', wrapCreateStub, ); } catch (e) { if (!optional) { throw e; } } }, setup(client) { SETUP_CLIENTS.set(client, true); }, }; }) ; /** * Google Cloud Platform service requests tracking for GRPC APIs. */ const googleCloudGrpcIntegration = defineIntegration(_googleCloudGrpcIntegration); /** Returns a wrapped function that returns a stub with tracing enabled */ function wrapCreateStub(origCreate) { return async function ( ...args) { const servicePath = _optionalChain([args, 'access', _ => _[1], 'optionalAccess', _2 => _2.servicePath]); if (servicePath == null || servicePath == undefined) { return origCreate.apply(this, args); } const serviceIdentifier = identifyService(servicePath); const stub = await origCreate.apply(this, args); for (const methodName of Object.keys(Object.getPrototypeOf(stub))) { fillGrpcFunction(stub, serviceIdentifier, methodName); } return stub; }; } /** Patches the function in grpc stub to enable tracing */ function fillGrpcFunction(stub, serviceIdentifier, methodName) { const funcObj = stub[methodName]; if (typeof funcObj !== 'function') { return; } const callType = !funcObj.requestStream && !funcObj.responseStream ? 'unary call' : funcObj.requestStream && !funcObj.responseStream ? 'client stream' : !funcObj.requestStream && funcObj.responseStream ? 'server stream' : 'bidi stream'; if (callType != 'unary call') { return; } fill( stub, methodName, (orig) => (...args) => { const ret = orig.apply(stub, args); if (typeof _optionalChain([ret, 'optionalAccess', _3 => _3.on]) !== 'function' || !SETUP_CLIENTS.has(getClient() )) { return ret; } const span = startInactiveSpan({ name: `${callType} ${methodName}`, onlyIfParent: true, op: `grpc.${serviceIdentifier}`, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.grpc.serverless', }, }); ret.on('status', () => { if (span) { span.end(); } }); return ret; }, ); } /** Identifies service by its address */ function identifyService(servicePath) { const match = servicePath.match(SERVICE_PATH_REGEX); return match ? match[1] : servicePath; } export { googleCloudGrpcIntegration }; //# sourceMappingURL=google-cloud-grpc.js.map