Repository URL to install this package:
|
Version:
0.7.0 ▾
|
// (c) Copyright 2023 Supertenant Ltd. - all rights reserved.
// See LICENSE file in project root for license terms.
"use strict";const shimmer=require("shimmer");let logger;logger=require("../../../logger").getLogger("tracing/grpcjs",newLogger=>{logger=newLogger});const requireHook=require("../../../util/requireHook"),tracingUtil=require("../../tracingUtil"),constants=require("../../constants"),cls=require("../../cls");let Metadata,isActive=!1;const TYPES={UNARY:"unary",SERVER_STREAM:"serverStream",CLIENT_STREAM:"clientStream",BIDI:"bidi"},ALL_TYPES=[TYPES.UNARY,TYPES.SERVER_STREAM,TYPES.CLIENT_STREAM,TYPES.BIDI],TYPES_WITH_CALLBACK=[TYPES.UNARY,TYPES.CLIENT_STREAM],TYPES_WITH_CALL_END=[TYPES.SERVER_STREAM,TYPES.BIDI];function instrumentModule(grpc){Metadata=grpc.Metadata}function instrumentServer(serverModule){shimmer.wrap(serverModule.Server.prototype,"register",shimServerRegister)}function instrumentClient(clientModule){let address;class ClientMock extends clientModule.Client{constructor(_address){address=_address,super(...arguments)}}clientModule.Client=ClientMock;[{name:"makeUnaryRequest",responseStream:!1,requestStream:!1},{name:"makeServerStreamRequest",responseStream:!0,requestStream:!1},{name:"makeClientStreamRequest",responseStream:!1,requestStream:!0},{name:"makeBidiStreamRequest",responseStream:!0,requestStream:!0}].forEach(fnObj=>{const{name,responseStream,requestStream}=fnObj;shimmer.wrap(clientModule.Client.prototype,name,function(origFn){return function(method){var hostAndPort=splitHostPort(address),originalArgs=(hostAndPort.port&&"number"==typeof hostAndPort.port&&(hostAndPort.port=hostAndPort.port.toString()),copyArgs(arguments)),skipTracingResult=cls.skipExitTracing({isActive:isActive,extendedResponse:!0});return skipTracingResult.skip?skipTracingResult.suppressed?(modifyArgs(name,originalArgs,null),origFn.apply(this,originalArgs)):origFn.apply(this,arguments):instrumentedClientMethod(this,origFn,originalArgs,hostAndPort,method,requestStream,responseStream,(args,span)=>modifyArgs(name,args,span))}})})}function modifyArgs(name,originalArgs,span){const setInstanaHeaders=metadata=>{metadata&&metadata.set&&(span?(metadata.set(constants.spanIdHeaderName,span.s),metadata.set(constants.traceIdHeaderName,span.t),metadata.set(constants.traceLevelHeaderName,"1")):metadata.set(constants.traceLevelHeaderName,"0"))};var checkMetadataOptionsAndCallback=(method,serialize,deserialize,argument,metadata,options,callback)=>{var originalCb;let newMetadata,newOptions,newCallback;if("function"==typeof metadata)newMetadata=new Metadata,newCallback=metadata,originalArgs[originalArgs.length-1]=newMetadata,originalArgs.push(newCallback);else if("function"==typeof options)metadata instanceof Metadata?(newMetadata=metadata,newCallback=options,originalArgs[originalArgs.length-2]=newMetadata,originalArgs[originalArgs.length-1]=newCallback):(newMetadata=new Metadata,newOptions=metadata,newCallback=options,originalArgs[originalArgs.length-2]=newMetadata,originalArgs[originalArgs.length-1]=newOptions,originalArgs.push(newCallback));else{if(metadata instanceof Metadata&&!options&&!callback)return void setInstanaHeaders(metadata);if(!(metadata instanceof Metadata&&options instanceof Object&&"function"==typeof callback))return;newMetadata=metadata,newOptions=options,newCallback=callback}span&&(originalCb=newCallback,originalArgs[originalArgs.length-1]=cls.ns.bind(function(err){span.d=Date.now()-span.ts,err&&"Cancelled"!==(err=err.details||err.message)&&(span.ec=1,err)&&(span.data.rpc.error=err),span.transmit(),originalCb.apply(this,arguments)})),setInstanaHeaders(newMetadata)};if("makeClientStreamRequest"===name)return checkMetadataOptionsAndCallback(originalArgs[0],originalArgs[1],originalArgs[2],0,originalArgs[3],originalArgs[4],originalArgs[5]);if("makeUnaryRequest"===name)return checkMetadataOptionsAndCallback(...originalArgs);{var[,,,,name,checkMetadataOptionsAndCallback]=[...originalArgs];let newMetadata,newOptions;name instanceof Metadata?newMetadata=name:(name||checkMetadataOptionsAndCallback?(newOptions=name,newMetadata=new Metadata,originalArgs[originalArgs.length-1]=newMetadata):(newOptions={},newMetadata=new Metadata,originalArgs.push(newMetadata)),originalArgs.push(newOptions)),setInstanaHeaders(newMetadata);return}}function copyArgs(args){var originalArgs=new Array(args.length);for(let i=0;i<args.length;i++)originalArgs[i]=args[i];return originalArgs}function shimServerRegister(originalFunction){return function(name,handler,serialize,deserialize,type){var originalArgs,originalHandler;return ALL_TYPES.indexOf(type)<0?(logger.warn(`Failed to instrument GRPC-JS entry ${name}, type is unsupported: `+type),originalFunction.apply(this,arguments)):(originalHandler=(originalArgs=copyArgs(arguments))[1],originalArgs[1]=createInstrumentedServerHandler(name,type,originalHandler),originalFunction.apply(this,originalArgs))}}function createInstrumentedServerHandler(name,type,originalHandler){return function(call){const originalThis=this,originalArgs=arguments;var parentSpan=cls.getCurrentSpan();if(parentSpan){if("node.http.server"!==parentSpan.n)return logger.warn("Cannot start a GRPC-JS entry span when another span is already active. Currently, the following span is active: "+JSON.stringify(parentSpan)),originalHandler.apply(originalThis,originalArgs);parentSpan.cancel()}return cls.ns.runAndReturn(()=>{var metadata=call.metadata,level=readMetadata(metadata,constants.traceLevelHeaderName);if("0"===level&&cls.setTracingLevel("0"),isActive&&!cls.tracingSuppressed()){cls.ns.bindEmitter(call);level=readMetadata(metadata,constants.traceIdHeaderName),metadata=readMetadata(metadata,constants.spanIdHeaderName);const span=cls.startSpan("rpc-server",constants.ENTRY,level,metadata);if(span.data.rpc={call:dropLeadingSlash(name),flavor:"grpc"},0<=TYPES_WITH_CALLBACK.indexOf(type)){const originalCallback=originalArgs[1];originalArgs[1]=cls.ns.bind(function(err){return err&&(span.ec=1,err.message||err.details)&&(span.data.rpc.error=err.message||err.details),span.d=Date.now()-span.ts,span.transmit(),originalCallback.apply(this,arguments)})}if(0<=TYPES_WITH_CALL_END.indexOf(type)){const originalEnd=call.end;call.end=function(){return span.d=Date.now()-span.ts,process.nextTick(()=>{span.transmit()}),originalEnd.apply(this,arguments)},call.on("error",err=>{span.ec=1,(err.message||err.details)&&(span.data.rpc.error=err.message||err.details)}),call.on("cancelled",()=>{span.d=Date.now()-span.ts,span.transmit()})}}return originalHandler.apply(originalThis,originalArgs)})}}function instrumentedClientMethod(ctx,originalFunction,originalArgs,address,rpcPath,requestStream,responseStream,modifyArgsFn){return cls.ns.runAndReturn(()=>{const span=cls.startSpan("rpc-client",constants.EXIT);span.ts=Date.now(),span.stack=tracingUtil.getStackTrace(instrumentedClientMethod),span.data.rpc={host:address.host,port:address.port,call:dropLeadingSlash(rpcPath),flavor:"grpc"},modifyArgsFn(originalArgs,span);var call=originalFunction.apply(ctx,originalArgs);return(requestStream||responseStream)&&cls.ns.bindEmitter(call),responseStream&&(call.on("end",()=>{span.d=Date.now()-span.ts,span.transmit()}),call.on("error",err=>{span.d=Date.now()-span.ts;err=err.details||err.message;"Cancelled"!==err&&(span.ec=1,err)&&(span.data.rpc.error=err),span.transmit()})),call})}function readMetadata(metadata,key){metadata=metadata.get(key);return metadata&&0<metadata.length?metadata[0]:null}exports.init=function(){requireHook.onModuleLoad("@grpc/grpc-js",instrumentModule),requireHook.onFileLoad(/\/@grpc\/grpc-js\/build\/src\/server\.js/,instrumentServer),requireHook.onFileLoad(/\/@grpc\/grpc-js\/build\/src\/client\.js/,instrumentClient)};const NUMBER_REGEX=/^\d+$/;function splitHostPort(path){var host,hostEnd;return path.startsWith("[")?-1===(hostEnd=path.indexOf("]"))||-1===(host=path.substring(1,hostEnd)).indexOf(":")?{host:null,port:null}:path.length>hostEnd+1?":"===path[hostEnd+1]&&(hostEnd=path.substring(hostEnd+2),NUMBER_REGEX.test(hostEnd))?{host:host,port:+hostEnd}:{host:null,port:null}:{host:host,port:null}:2===(hostEnd=path.split(":")).length?NUMBER_REGEX.test(hostEnd[1])?{host:hostEnd[0],port:+hostEnd[1]}:{host:null,port:null}:{host:path,port:null}}function dropLeadingSlash(rpcPath){return"string"==typeof rpcPath?"/"===rpcPath[0]?rpcPath.substr(1):rpcPath:"unknown"}exports.modifyArgs=modifyArgs,exports.instrumentModule=instrumentModule,exports.activate=function(){isActive=!0},exports.deactivate=function(){isActive=!1};