@instana/core
Advanced tools
Comparing version 1.135.0 to 1.136.0
{ | ||
"name": "@instana/core", | ||
"version": "1.135.0", | ||
"version": "1.136.0", | ||
"description": "Core library for Instana's Node.js packages", | ||
@@ -135,3 +135,3 @@ "main": "src/index.js", | ||
}, | ||
"gitHead": "20309759c0759e51266e8d49126e8e21e3e9e5c1" | ||
"gitHead": "2a2aaf4aa675a10ba0534c50e9bd2c34ab104d78" | ||
} |
@@ -34,3 +34,12 @@ /* | ||
/** @typedef {import('../../collector/src/agentConnection').AgentConnectionEvent} AgentConnectionEvent */ | ||
/** | ||
* This type is based on /nodejs/packages/collector/src/agentConnection.js | ||
* @typedef {Object} DownstreamConnection | ||
* @property {(spans: *, cb: Function) => void} sendSpans | ||
* @property {(eventData: AgentConnectionEvent, cb: (...args: *) => *) => void} sendEvent | ||
*/ | ||
/** | ||
* @param {Array.<import('./tracing/index').InstanaInstrumentedModule>} additionalInstrumentationModules | ||
@@ -55,3 +64,3 @@ */ | ||
* @param {import('./util/normalizeConfig').InstanaConfig} config | ||
* @param {tracing.spanBuffer.TemporaryAgentConnection} downstreamConnection | ||
* @param {DownstreamConnection} downstreamConnection | ||
* @param {import('../../collector/src/pidStore')} processIdentityProvider | ||
@@ -58,0 +67,0 @@ */ |
@@ -106,5 +106,5 @@ /* | ||
// properties used within the collector that should not be transmitted to the agent/backend | ||
// NOTE: If you add a new property, make sure that it is not enumerable, as it may otherwise be transmitted | ||
// to the backend! | ||
// Properties for the span that are only used internally but will not be transmitted to the agent/backend, | ||
// therefore defined as non-enumerabled. NOTE: If you add a new property, make sure that it is also defined as | ||
// non-enumerable. | ||
Object.defineProperty(this, 'cleanupFunctions', { | ||
@@ -130,2 +130,19 @@ value: [], | ||
}); | ||
// Marker for higher level instrumentation (like graphql.server) to not transmit the span once they are done, but | ||
// instead we wait for the protocol level instrumentation to finish (which then transmits the span). | ||
Object.defineProperty(this, 'postponeTransmit', { | ||
value: false, | ||
configurable: true, | ||
writable: true, | ||
enumerable: false | ||
}); | ||
// Additional special purpose marker that is only used to control transmission logig between the GraphQL server core | ||
// instrumentation and Apollo Gateway instrumentation | ||
// (see packages/core/tracing/instrumentation/protocols/graphql.js). | ||
Object.defineProperty(this, 'postponeTransmitApolloGateway', { | ||
value: false, | ||
configurable: true, | ||
writable: true, | ||
enumerable: false | ||
}); | ||
} | ||
@@ -132,0 +149,0 @@ |
@@ -131,3 +131,3 @@ /* | ||
* @param {import('../util/normalizeConfig').InstanaConfig} _config | ||
* @param {import('./spanBuffer').TemporaryAgentConnection} downstreamConnection | ||
* @param {import('..').DownstreamConnection} downstreamConnection | ||
* @param {CollectorPIDStore} _processIdentityProvider | ||
@@ -134,0 +134,0 @@ */ |
@@ -17,3 +17,4 @@ /* | ||
require('./kinesis'), | ||
require('./s3') | ||
require('./s3'), | ||
require('./sns') | ||
]; | ||
@@ -20,0 +21,0 @@ |
@@ -114,7 +114,8 @@ /* | ||
// Possible consequences: | ||
// 1) If a customer does GraphQL over any transport that we trace as an entry, we will replace this | ||
// transport/protocol level entry span with a GraphQL entry. But if the customer is using a transport that we do | ||
// _not_ trace, we will start a new root entry span here. We will still trace the GraphQL entry but might lose trace | ||
// continuity, as X-Instana-T andX-Instana-S are not transported at the GraphQL layer but rather in the underlying | ||
// transport layer (HTTP, AMQP, ...) | ||
// 1) If a customer does GraphQL over any transport that we trace as an entry, we will repurpose this | ||
// transport/protocol level entry span to a GraphQL entry by overwriting span.n. The attributes captured by the | ||
// protocol level tracing (the initial timestamp and for example HTTP attributes like url and method) will be kept. | ||
// But if the customer is using a transport that we do _not_ trace, we will start a new root entry span here. We | ||
// will still trace the GraphQL entry but might lose trace continuity, as X-Instana-T andX-Instana-S are not | ||
// transported at the GraphQL layer but rather in the underlying transport layer (HTTP, AMQP, ...) | ||
// | ||
@@ -126,6 +127,12 @@ // 2) If a customer does multiple things in an HTTP, AMQP, GRPc, ... call, only one of which is running a GraphQL | ||
// Replace generic node.http.server/rabbitmq/... span by a more specific graphql.server span. We change the values | ||
// in the active span in-situ. This way, other exit spans that have already been created as children of the current | ||
// entry span still have the the correct parent span ID. | ||
// in the active span in-situ. This way, other intermediate and exit spans that have already been created as | ||
// children of the current entry span still have the the correct parent span ID. | ||
span = activeEntrySpan; | ||
span.n = 'graphql.server'; | ||
// Mark this span so that the GraphQL instrumentation will not transmit it, instead we wait for the protocol level | ||
// instrumentation to finish, which will then transmit it. This will ensure that we also capture attributes that are | ||
// only written by the protocol level instrumentation at the end (like the HTTP status code). | ||
// (This property is defined as non-enumerable in the InstanaSpan class and will not be serialized to JSON.) | ||
span.postponeTransmit = true; | ||
} | ||
@@ -138,3 +145,2 @@ | ||
span = cls.startSpan('graphql.server', constants.ENTRY); | ||
span.ts = Date.now(); | ||
} | ||
@@ -274,3 +280,3 @@ span.stack = tracingUtil.getStackTrace(stackTraceRef); | ||
} | ||
if (!span.postponeTransmit) { | ||
if (!span.postponeTransmit && !span.postponeTransmitApolloGateway) { | ||
span.transmit(); | ||
@@ -284,3 +290,5 @@ } | ||
span.data.graphql.errors = err.message; | ||
span.transmit(); | ||
if (!span.postponeTransmit) { | ||
span.transmit(); | ||
} | ||
} | ||
@@ -306,13 +314,10 @@ | ||
// instrument. There is one case that requires extra instrumentation, though. @apollo/gateway does something funky | ||
// with errors that come back from individual services: At the we would normally finish and transmit the span in | ||
// shimExecuteFunction/traceQueryOrMutation/runOriginalAndFinish, the errors (if any) are not part of the | ||
// response. Therefore we mark the span so that transmitting it will postpone, that is, it won't happen in | ||
// runOriginalAndFinish. Once the call returns from @apollo/gateway#executeQueryPlan the errors have been merged | ||
// back into the response and only then will we transmit the span. | ||
Object.defineProperty(activeEntrySpan, 'postponeTransmit', { | ||
value: true, | ||
configurable: true, | ||
writable: true, | ||
enumerable: false | ||
}); | ||
// with errors that come back from individual services: We would normally finish and transmit the span in | ||
// shimExecuteFunction/traceQueryOrMutation/runOriginalAndFinish, but when Apollo Gateway is involved the errors | ||
// (if any) are not part of the response. Therefore we mark the span so that transmitting it will be postponed, | ||
// that is, it won't happen in runOriginalAndFinish. Once the call returns from @apollo/gateway#executeQueryPlan | ||
// the errors have been merged back into the response and only then will we record the errors and finally transmit | ||
// the span. This is implemented via the marker property postponeTransmitApolloGateway. | ||
// (This property is defined as non-enumerable in the InstanaSpan class and will not be serialized to JSON.) | ||
activeEntrySpan.postponeTransmitApolloGateway = true; | ||
} | ||
@@ -324,3 +329,3 @@ | ||
promiseResult => { | ||
delete activeEntrySpan.postponeTransmit; | ||
delete activeEntrySpan.postponeTransmitApolloGateway; | ||
finishSpan(activeEntrySpan, promiseResult); | ||
@@ -330,3 +335,3 @@ return promiseResult; | ||
err => { | ||
delete activeEntrySpan.postponeTransmit; | ||
delete activeEntrySpan.postponeTransmitApolloGateway; | ||
finishWithException(activeEntrySpan, err); | ||
@@ -333,0 +338,0 @@ throw err; |
@@ -154,18 +154,20 @@ /* | ||
function finishSpan() { | ||
// Check if a span with higher priority (like graphql.server) already finished this span, only overwrite | ||
// span attributes if that is not the case. | ||
if (!span.transmitted) { | ||
// safe guard just in case a higher prio instrumentation (graphql etc.) has removed data.http (planning to | ||
// take over the span) but did not actually transmit this span. | ||
span.data.http = span.data.http || {}; | ||
span.data.http.status = res.statusCode; | ||
span.data.http.header = httpCommon.mergeExtraHeadersFromServerResponseOrClientResponse( | ||
span.data.http.header, | ||
res, | ||
extraHttpHeadersToCapture | ||
); | ||
// Always capture duration and HTTP response details, no matter if a higher level instrumentation | ||
// (like graphql.server) has modified the span or not. | ||
span.d = Date.now() - span.ts; | ||
span.data.http = span.data.http || {}; | ||
span.data.http.status = res.statusCode; | ||
span.data.http.header = httpCommon.mergeExtraHeadersFromServerResponseOrClientResponse( | ||
span.data.http.header, | ||
res, | ||
extraHttpHeadersToCapture | ||
); | ||
if (!span.postponeTransmit) { | ||
// Do not overwrite the error count if an instrumentation with a higher priority (like graphql.server) has | ||
// already made a decision about it. | ||
span.ec = res.statusCode >= 500 ? 1 : 0; | ||
span.d = Date.now() - span.ts; | ||
span.transmit(); | ||
} | ||
span.transmit(); | ||
} | ||
@@ -172,0 +174,0 @@ |
@@ -31,6 +31,6 @@ /* | ||
* @param {string} name | ||
* @param {string | Function} tags | ||
* @param {string | Function} traceId | ||
* @param {Object.<string, *> | Function} tags | ||
* @param {Object.<string, *> | Function | string} traceId | ||
* @param {string} parentSpanId | ||
* @param {Function | string} callback | ||
* @param {Function | Object.<string, *>} callback | ||
*/ | ||
@@ -42,3 +42,3 @@ function startEntrySpan(name, tags, traceId, parentSpanId, callback) { | ||
} else if (isCallbackApi && arguments.length === 3 && typeof arguments[2] === 'function') { | ||
callback = traceId; | ||
callback = /** @type {Function} */ (traceId); | ||
traceId = parentSpanId = null; | ||
@@ -67,4 +67,4 @@ } | ||
startEntrySpan, | ||
/** @type {string | null} */ (tags), | ||
/** @type {string | null} */ (traceId), | ||
/** @type {Object.<string, *> | null} */ (tags), | ||
/** @type {string} */ (traceId), | ||
parentSpanId, | ||
@@ -77,3 +77,3 @@ /** @type {Function} */ (callback) | ||
* @param {Error} error | ||
* @param {string} tags | ||
* @param {Object.<string, *>} tags | ||
*/ | ||
@@ -109,4 +109,4 @@ function completeEntrySpan(error, tags) { | ||
* @param {string} name | ||
* @param {string} tags | ||
* @param {Function | string} callback | ||
* @param {Object.<string, *>} tags | ||
* @param {Function | Object.<string, *>} callback | ||
* @returns {Function | Promise<*>} | ||
@@ -157,3 +157,3 @@ */ | ||
* @param {Error} error | ||
* @param {string} tags | ||
* @param {Object.<string, *>} tags | ||
*/ | ||
@@ -189,4 +189,4 @@ function completeIntermediateSpan(error, tags) { | ||
* @param {string} name | ||
* @param {string} tags | ||
* @param {Function | string} callback | ||
* @param {Object.<string, *>} tags | ||
* @param {Function | Object.<string, *>} callback | ||
* @returns {Function | Promise<*>} | ||
@@ -237,3 +237,3 @@ */ | ||
* @param {Error} error | ||
* @param {string} tags | ||
* @param {Object.<string, *>} tags | ||
*/ | ||
@@ -272,3 +272,3 @@ function completeExitSpan(error, tags) { | ||
* @param {Function} stackTraceRef | ||
* @param {string} tags | ||
* @param {Object.<string, *>} tags | ||
* @param {string} traceId | ||
@@ -288,3 +288,6 @@ * @param {string} parentSpanId | ||
if (tags) { | ||
span.data.sdk.custom = { tags }; | ||
// We make a copy of the tags object to assure that it is extensible. | ||
// As the tags object is provided by users, they could have been made not extensible with | ||
// Object.freeze or Object.preventExtensions | ||
span.data.sdk.custom = { tags: Object.assign({}, tags) }; | ||
} | ||
@@ -298,3 +301,3 @@ return callNext(callback); | ||
* @param {import('../cls').InstanaBaseSpan} span | ||
* @param {string} tags | ||
* @param {Object.<string, *>} tags | ||
*/ | ||
@@ -301,0 +304,0 @@ function completeSpan(error, span, tags) { |
@@ -19,9 +19,3 @@ /* | ||
/** | ||
* This module will not be typed yet: /nodejs/packages/collector/src/agentConnection.js | ||
* @typedef {Object} TemporaryAgentConnection | ||
* @property {(spans: *, cb: Function) => void} sendSpans | ||
*/ | ||
/** @type {TemporaryAgentConnection} */ | ||
/** @type {import('..').DownstreamConnection} */ | ||
let downstreamConnection = null; | ||
@@ -96,3 +90,3 @@ let isActive = false; | ||
* @param {import('../util/normalizeConfig').InstanaConfig} config | ||
* @param {TemporaryAgentConnection} _downstreamConnection | ||
* @param {import('..').DownstreamConnection} _downstreamConnection | ||
*/ | ||
@@ -99,0 +93,0 @@ exports.init = function init(config, _downstreamConnection) { |
552907
108
15602