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/graphql",newLogger=>{logger=newLogger});const requireHook=require("../../../util/requireHook"),tracingUtil=require("../../tracingUtil"),constants=require("../../constants"),cls=require("../../cls");let isActive=!1;const queryOperationType="query",mutationOperationType="mutation",subscriptionOperationType="subscription",operationTypes=[queryOperationType,mutationOperationType,subscriptionOperationType],subscriptionUpdate="subscription-update";function instrumentExecute(executeModule){shimmer.wrap(executeModule,"execute",shimExecuteFunction.bind(null))}function shimExecuteFunction(originalFunction){return function instrumentedExecute(){if(!isActive||cls.tracingSuppressed())return originalFunction.apply(this,arguments);var originalArgs=arguments;let doc,operationName;operationName=1===originalArgs.length&&"object"==typeof originalArgs[0]?(doc=originalArgs[0].document,originalArgs[0].operationName):(doc=originalArgs[1],originalArgs[5]);var operationDefinition=findOperationDefinition(doc,operationName);return operationDefinition?operationDefinition.operation?(operationDefinition.operation===subscriptionOperationType?traceSubscriptionUpdate:traceQueryOrMutation)(originalFunction,this,originalArgs,instrumentedExecute,operationDefinition,operationName):(logger.debug("Operation definition has no operation, GraphQL call will not be traced. "+JSON.stringify(operationDefinition)),originalFunction.apply(this,arguments)):(logger.debug("No operation definition, GraphQL call will not be traced."),originalFunction.apply(this,arguments))}}function traceQueryOrMutation(originalFunction,originalThis,originalArgs,stackTraceRef,operationDefinition,operationName){var activeEntrySpan=cls.getCurrentSpan(!0);let span;return activeEntrySpan&&activeEntrySpan.k===constants.ENTRY&&"graphql.server"!==activeEntrySpan.n&&((span=activeEntrySpan).n="graphql.server",span.postponeTransmit=!0),cls.ns.runAndReturn(()=>((span=span||cls.startSpan("graphql.server",constants.ENTRY)).stack=tracingUtil.getStackTrace(stackTraceRef),span.data.graphql={operationType:operationDefinition.operation,operationName:operationDefinition.name?operationDefinition.name.value:operationName,fields:{},args:{}},addFieldsAndArguments(span,operationDefinition),runOriginalAndFinish(originalFunction,originalThis,originalArgs,span)))}function traceSubscriptionUpdate(originalFunction,originalThis,originalArgs,stackTraceRef,operationDefinition,operationName){if(!isActive)return originalFunction.apply(originalThis,originalArgs);const parentSpan=cls.getCurrentSpan(!0)||cls.getReducedSpan(!0);return parentSpan&&!constants.isExitSpan(parentSpan)&&parentSpan.t&&parentSpan.s?cls.ns.runAndReturn(()=>{var span=cls.startSpan("graphql.client",constants.EXIT,parentSpan.t,parentSpan.s);return span.ts=Date.now(),span.stack=tracingUtil.getStackTrace(stackTraceRef),span.data.graphql={operationType:subscriptionUpdate,operationName:operationDefinition.name?operationDefinition.name.value:operationName,fields:{},args:{}},addFieldsAndArguments(span,operationDefinition),runOriginalAndFinish(originalFunction,originalThis,originalArgs,span)}):originalFunction.apply(originalThis,originalArgs)}function findOperationDefinition(doc,operationNameFromArgs){return doc&&Array.isArray(doc.definitions)?operationNameFromArgs?doc.definitions.filter(definition=>-1!==operationTypes.indexOf(definition.operation)).find(definition=>{definition=definition.name?definition.name.value:null;return definition&&operationNameFromArgs===definition}):doc.definitions.find(definition=>-1!==operationTypes.indexOf(definition.operation)):null}function addFieldsAndArguments(span,definition){traverseSelections(definition,entities=>{entities.forEach(function(entity){const entityName=entity.name.value;traverseSelections(entity,fields=>{span.data.graphql.fields[entityName]=fields.map(field=>field.name.value)}),Array.isArray(entity.arguments)&&0<entity.arguments.length&&(span.data.graphql.args[entityName]=entity.arguments.map(arg=>arg.name&&"string"==typeof arg.name.value?arg.name.value:"?"))})})}function traverseSelections(definition,selectionPostProcessor){return definition.selectionSet&&"object"==typeof definition.selectionSet&&Array.isArray(definition.selectionSet.selections)?selectionPostProcessor(definition.selectionSet.selections.filter(selection=>selection&&"Field"===selection.kind&&selection.name&&"string"==typeof selection.name.value)):null}function runOriginalAndFinish(originalFunction,originalThis,originalArgs,span){let result;try{result=originalFunction.apply(originalThis,originalArgs)}catch(e){return finishWithException(span,e),result}return result&&"function"==typeof result.then?result.then(promiseResult=>(finishSpan(span,promiseResult),promiseResult),err=>{throw finishWithException(span,err),err}):(finishSpan(span,result),result)}function finishSpan(span,result){span.ec=result.errors&&1<=result.errors.length?1:0,span.d=Date.now()-span.ts,Array.isArray(result.errors)&&(span.data.graphql.errors=result.errors.map(singleError=>"string"==typeof singleError.message?singleError.message:null).filter(msg=>!!msg).join(", ")),span.postponeTransmit||span.postponeTransmitApolloGateway||span.transmit()}function finishWithException(span,err){span.ec=1,span.d=Date.now()-span.ts,span.data.graphql.errors=err.message,span.postponeTransmit||span.transmit()}function instrumentApolloGatewayExecuteQueryPlan(apolloGatewayExecuteQueryPlanModule){shimmer.wrap(apolloGatewayExecuteQueryPlanModule,"executeQueryPlan",shimApolloGatewayExecuteQueryPlanFunction.bind(null))}function shimApolloGatewayExecuteQueryPlanFunction(originalFunction){return function(){if(!isActive||cls.tracingSuppressed())return originalFunction.apply(this,arguments);const activeEntrySpan=cls.getCurrentSpan();activeEntrySpan&&activeEntrySpan.k===constants.ENTRY&&(activeEntrySpan.postponeTransmitApolloGateway=!0);var resultPromise=originalFunction.apply(this,arguments);return resultPromise&&"function"==typeof resultPromise.then?resultPromise.then(promiseResult=>(delete activeEntrySpan.postponeTransmitApolloGateway,finishSpan(activeEntrySpan,promiseResult),promiseResult),err=>{throw delete activeEntrySpan.postponeTransmitApolloGateway,finishWithException(activeEntrySpan,err),err}):resultPromise}}exports.init=function(){requireHook.onFileLoad(/\/graphql\/execution\/execute.js/,instrumentExecute),requireHook.onFileLoad(/\/@apollo\/gateway\/dist\/executeQueryPlan.js/,instrumentApolloGatewayExecuteQueryPlan)},exports.activate=function(){isActive=!0},exports.deactivate=function(){isActive=!1};