@instana/core
Advanced tools
Comparing version 1.90.0 to 1.91.0
{ | ||
"name": "@instana/core", | ||
"version": "1.90.0", | ||
"version": "1.91.0", | ||
"description": "Core library for Instana's Node.js packages", | ||
@@ -136,3 +136,3 @@ "main": "src/index.js", | ||
}, | ||
"gitHead": "d12ff418d974d9b7e720d259187229a2891c74b9" | ||
"gitHead": "3c96ce6f064c504904076e5fa819636152ca9698" | ||
} |
@@ -13,7 +13,8 @@ 'use strict'; | ||
var currentRootSpanKey = (exports.currentRootSpanKey = 'com.instana.rootSpan'); | ||
var currentEntrySpanKey = (exports.currentEntrySpanKey = 'com.instana.entry'); | ||
var currentSpanKey = (exports.currentSpanKey = 'com.instana.span'); | ||
var reducedSpanKey = (exports.reducedSpanKey = 'com.instana.reduced'); | ||
var tracingLevelKey = (exports.tracingLevelKey = 'com.instana.tl'); | ||
var w3cTraceContextKey = (exports.w3cTraceContextKey = 'com.instana.w3ctc'); | ||
var tracingLevelKey = (exports.tracingLevelKey = 'com.instana.tl'); | ||
// eslint-disable-next-line no-undef-init | ||
@@ -24,3 +25,3 @@ var serviceName = undefined; | ||
/* | ||
* Access the Instana namespace in context local storage. | ||
* Access the Instana namespace in continuation local storage. | ||
* | ||
@@ -31,3 +32,2 @@ * Usage: | ||
* cls.ns.run(function() {}); | ||
* | ||
*/ | ||
@@ -46,3 +46,3 @@ exports.ns = hooked.createNamespace('instana.collector'); | ||
*/ | ||
exports.startSpan = function startSpan(spanName, kind, traceId, parentSpanId, modifyAsyncContext) { | ||
exports.startSpan = function startSpan(spanName, kind, traceId, parentSpanId, w3cTraceContext) { | ||
tracingMetrics.incrementOpened(); | ||
@@ -53,30 +53,49 @@ if (!kind || (kind !== constants.ENTRY && kind !== constants.EXIT && kind !== constants.INTERMEDIATE)) { | ||
} | ||
modifyAsyncContext = modifyAsyncContext !== false; | ||
var span = new InstanaSpan(spanName); | ||
var parentSpan = exports.ns.get(currentSpanKey); | ||
span.k = kind; | ||
// If specified, use params | ||
if (traceId && parentSpanId) { | ||
var parentSpan = exports.getCurrentSpan(); | ||
var parentW3cTraceContext = exports.getW3cTraceContext(); | ||
// If the client code has specified a trace ID/parent ID, use the provided IDs. | ||
if (traceId) { | ||
span.t = traceId; | ||
span.p = parentSpanId; | ||
// else use pre-existing context (if any) | ||
if (parentSpanId) { | ||
span.p = parentSpanId; | ||
} | ||
} else if (parentSpan) { | ||
// Otherwise, use the currently active span (if any) as parent. | ||
span.t = parentSpan.t; | ||
span.p = parentSpan.s; | ||
// last resort, use newly generated Ids | ||
} else { | ||
// If no IDs have been provided, we start a new trace by generating a new trace ID. We do not set a parent ID in | ||
// this case. | ||
span.t = tracingUtil.generateRandomTraceId(); | ||
} | ||
// Always generate a new span ID for the new span. | ||
span.s = tracingUtil.generateRandomSpanId(); | ||
if (!w3cTraceContext && parentW3cTraceContext) { | ||
// If there is no incoming W3C trace context that has been read from HTTP headers, but there is a parent trace | ||
// context associated with a parent span, we will create an updated copy of that parent W3C trace context. We must | ||
// make sure that the parent trace context in the parent cls context is not modified. | ||
w3cTraceContext = parentW3cTraceContext.clone(); | ||
} | ||
if (w3cTraceContext) { | ||
w3cTraceContext.updateParent(span.t, span.s); | ||
span.addCleanup(exports.ns.set(w3cTraceContextKey, w3cTraceContext)); | ||
} | ||
if (span.k === constants.ENTRY) { | ||
if (!span.p && modifyAsyncContext) { | ||
span.addCleanup(exports.ns.set(currentRootSpanKey, span)); | ||
} | ||
// Make the entry span available independently (even if getCurrentSpan would return an intermediate or an exit at | ||
// any given moment). This is used by the instrumentations of web frameworks like Express.js to add path templates | ||
// and error messages to the entry span. | ||
span.addCleanup(exports.ns.set(currentEntrySpanKey, span)); | ||
} | ||
if (modifyAsyncContext) { | ||
span.addCleanup(exports.ns.set(currentSpanKey, span)); | ||
} | ||
// Set the span object as the currently active span in the active CLS context and also add a cleanup hook for when | ||
// this span is transmitted. | ||
span.addCleanup(exports.ns.set(currentSpanKey, span)); | ||
return span; | ||
@@ -86,6 +105,6 @@ }; | ||
/* | ||
* Get the currently active root span. | ||
* Get the currently active entry span. | ||
*/ | ||
exports.getCurrentRootSpan = function getCurrentRootSpan() { | ||
return exports.ns.get(currentRootSpanKey); | ||
exports.getCurrentEntrySpan = function getCurrentEntrySpan() { | ||
return exports.ns.get(currentEntrySpanKey); | ||
}; | ||
@@ -98,3 +117,2 @@ | ||
exports.ns.set(currentSpanKey, span); | ||
return span; | ||
}; | ||
@@ -117,2 +135,16 @@ | ||
/* | ||
* Stores the W3C trace context object. | ||
*/ | ||
exports.setW3cTraceContext = function setW3cTraceContext(traceContext) { | ||
exports.ns.set(w3cTraceContextKey, traceContext); | ||
}; | ||
/* | ||
* Returns the W3C trace context object. | ||
*/ | ||
exports.getW3cTraceContext = function getW3cTraceContext() { | ||
return exports.ns.get(w3cTraceContextKey); | ||
}; | ||
/* | ||
* Determine if we're currently tracing or not. | ||
@@ -129,3 +161,2 @@ */ | ||
exports.ns.set(tracingLevelKey, level); | ||
return level; | ||
}; | ||
@@ -145,3 +176,3 @@ | ||
var tl = exports.tracingLevel(); | ||
return tl && tl === '0'; | ||
return typeof tl === 'string' && tl.indexOf('0') === 0; | ||
}; | ||
@@ -148,0 +179,0 @@ |
@@ -19,2 +19,7 @@ 'use strict'; | ||
exports.w3cTraceParent = 'traceparent'; | ||
exports.w3cTraceState = 'tracestate'; | ||
exports.w3cInstana = 'in'; | ||
exports.w3cInstanaEquals = exports.w3cInstana + '='; | ||
exports.serviceNameHeaderName = 'X-Instana-Service'; | ||
@@ -21,0 +26,0 @@ exports.serviceNameHeaderNameLowerCase = exports.serviceNameHeaderName.toLowerCase(); |
@@ -61,3 +61,3 @@ 'use strict'; | ||
if (err && err.message && err.stack) { | ||
annotateHttpRootSpanWithError(err); | ||
annotateHttpEntrySpanWithError(err); | ||
} | ||
@@ -89,8 +89,8 @@ return fn.apply(this, arguments); | ||
function wrapExpress4ErrorHandlingFn(fn) { | ||
// DO NOT REMOVE THE UNUSED PARAMETERS IN THE FOLLOWING LINE | ||
// express is checking the existence for four parameters on the function to identify that this is an error | ||
// handling function. Defining less than four parameter would change application behavior. | ||
// Do not remove the unused parameters in the following line! Express.js uses the number of parameters on the function | ||
// to check whether this is an error handling function. Defining less than four parameter would change application | ||
// behavior. | ||
// eslint-disable-next-line no-unused-vars | ||
return function wrappedErrorHandlingFn(err, req, res, next) { | ||
annotateHttpRootSpanWithError(err); | ||
annotateHttpEntrySpanWithError(err); | ||
return fn.apply(this, arguments); | ||
@@ -100,3 +100,3 @@ }; | ||
function annotateHttpRootSpanWithError(err) { | ||
function annotateHttpEntrySpanWithError(err) { | ||
if (!err || !active) { | ||
@@ -106,3 +106,3 @@ return; | ||
var span = cls.getCurrentRootSpan(); | ||
var span = cls.getCurrentEntrySpan(); | ||
if (!span || span.n !== httpServer.spanName) { | ||
@@ -157,3 +157,3 @@ return; | ||
var span = cls.getCurrentRootSpan(); | ||
var span = cls.getCurrentEntrySpan(); | ||
if (!span || span.n !== httpServer.spanName) { | ||
@@ -160,0 +160,0 @@ return; |
@@ -95,3 +95,3 @@ 'use strict'; | ||
var span = cls.getCurrentRootSpan(); | ||
var span = cls.getCurrentEntrySpan(); | ||
if (!span || span.n !== httpServer.spanName) { | ||
@@ -98,0 +98,0 @@ return; |
@@ -46,3 +46,3 @@ 'use strict'; | ||
} | ||
var span = cls.getCurrentRootSpan(); | ||
var span = cls.getCurrentEntrySpan(); | ||
if (!span || span.n !== httpServer.spanName) { | ||
@@ -49,0 +49,0 @@ return; |
@@ -114,3 +114,3 @@ 'use strict'; | ||
function annotateHttpEntrySpanWithPathTemplate(pathTemplate) { | ||
var span = cls.getCurrentRootSpan(); | ||
var span = cls.getCurrentEntrySpan(); | ||
if (!span || span.n !== httpServer.spanName) { | ||
@@ -117,0 +117,0 @@ return; |
@@ -77,6 +77,8 @@ 'use strict'; | ||
var w3cTraceContext = cls.getW3cTraceContext(); | ||
if (!isActive || !cls.isTracing()) { | ||
var traceLevelHeaderHasBeenAdded = false; | ||
if (cls.tracingLevel()) { | ||
traceLevelHeaderHasBeenAdded = tryToAddTraceLevelAddHeaderToOpts(options, cls.tracingLevel()); | ||
traceLevelHeaderHasBeenAdded = tryToAddTraceLevelAddHeaderToOpts(options, cls.tracingLevel(), w3cTraceContext); | ||
} | ||
@@ -86,2 +88,3 @@ clientRequest = originalRequest.apply(coreModule, arguments); | ||
clientRequest.setHeader(constants.traceLevelHeaderName, cls.tracingLevel()); | ||
setW3cHeadersOnRequest(clientRequest, w3cTraceContext); | ||
} | ||
@@ -95,3 +98,3 @@ return clientRequest; | ||
if (cls.tracingSuppressed()) { | ||
traceLevelHeaderHasBeenAdded = tryToAddTraceLevelAddHeaderToOpts(options, '0'); | ||
traceLevelHeaderHasBeenAdded = tryToAddTraceLevelAddHeaderToOpts(options, '0', w3cTraceContext); | ||
} | ||
@@ -101,2 +104,3 @@ clientRequest = originalRequest.apply(coreModule, arguments); | ||
clientRequest.setHeader(constants.traceLevelHeaderName, '0'); | ||
setW3cHeadersOnRequest(clientRequest, w3cTraceContext); | ||
} | ||
@@ -109,2 +113,6 @@ return clientRequest; | ||
// startSpan updates the W3C trace context and writes it back to CLS, so we have to refetch the updated context | ||
// object from CLS. | ||
w3cTraceContext = cls.getW3cTraceContext(); | ||
var completeCallUrl; | ||
@@ -151,3 +159,3 @@ var params; | ||
try { | ||
var instanaHeadersHaveBeenAdded = tryToAddHeadersToOpts(options, span); | ||
var instanaHeadersHaveBeenAdded = tryToAddHeadersToOpts(options, span, w3cTraceContext); | ||
clientRequest = originalRequest.apply(coreModule, originalArgs); | ||
@@ -170,3 +178,3 @@ } catch (e) { | ||
if (!instanaHeadersHaveBeenAdded) { | ||
instanaHeadersHaveBeenAdded = setHeadersOnRequest(clientRequest, span); | ||
instanaHeadersHaveBeenAdded = setHeadersOnRequest(clientRequest, span, w3cTraceContext); | ||
} | ||
@@ -255,3 +263,3 @@ | ||
function tryToAddHeadersToOpts(options, span) { | ||
function tryToAddHeadersToOpts(options, span, w3cTraceContext) { | ||
// Some HTTP spec background: If the request has a header Expect: 100-continue, the client will first send the | ||
@@ -272,4 +280,5 @@ // request headers, without the body. The client is then ought to wait for the server to send a first, preliminary | ||
// slightly more general solution: If there is an options object parameter with a `headers` object, we just always | ||
// add our headers there. Only when this object is missing do we use request.setHeader on the ClientRequest object | ||
// add our headers there. We use request.setHeader on the ClientRequest object only when the headers object is missing | ||
// (see setHeadersOnRequest). | ||
if (hasHeadersOption(options)) { | ||
@@ -282,8 +291,10 @@ if (!isItSafeToModifiyHeadersInOptions(options)) { | ||
options.headers[constants.traceLevelHeaderName] = '1'; | ||
tryToAddW3cHeaderToOpts(options, w3cTraceContext); | ||
return true; | ||
} | ||
return false; | ||
} | ||
function tryToAddTraceLevelAddHeaderToOpts(options, level) { | ||
function tryToAddTraceLevelAddHeaderToOpts(options, level, w3cTraceContext) { | ||
if (hasHeadersOption(options)) { | ||
@@ -294,2 +305,3 @@ if (!isItSafeToModifiyHeadersInOptions(options)) { | ||
options.headers[constants.traceLevelHeaderName] = level; | ||
tryToAddW3cHeaderToOpts(options, w3cTraceContext); | ||
return true; | ||
@@ -300,2 +312,11 @@ } | ||
function tryToAddW3cHeaderToOpts(options, w3cTraceContext) { | ||
if (w3cTraceContext) { | ||
options.headers[constants.w3cTraceParent] = w3cTraceContext.renderTraceParent(); | ||
if (w3cTraceContext.hasTraceState()) { | ||
options.headers[constants.w3cTraceState] = w3cTraceContext.renderTraceState(); | ||
} | ||
} | ||
} | ||
function hasHeadersOption(options) { | ||
@@ -305,3 +326,3 @@ return options && typeof options === 'object' && options.headers && typeof options.headers === 'object'; | ||
function setHeadersOnRequest(clientRequest, span) { | ||
function setHeadersOnRequest(clientRequest, span, w3cTraceContext) { | ||
if (!isItSafeToModifiyHeadersForRequest(clientRequest)) { | ||
@@ -313,4 +334,14 @@ return; | ||
clientRequest.setHeader(constants.traceLevelHeaderName, '1'); | ||
setW3cHeadersOnRequest(clientRequest, w3cTraceContext); | ||
} | ||
function setW3cHeadersOnRequest(clientRequest, w3cTraceContext) { | ||
if (w3cTraceContext) { | ||
clientRequest.setHeader(constants.w3cTraceParent, w3cTraceContext.renderTraceParent()); | ||
if (w3cTraceContext.hasTraceState()) { | ||
clientRequest.setHeader(constants.w3cTraceState, w3cTraceContext.renderTraceState()); | ||
} | ||
} | ||
} | ||
function isItSafeToModifiyHeadersInOptions(options) { | ||
@@ -317,0 +348,0 @@ var keys = Object.keys(options.headers); |
@@ -7,2 +7,3 @@ 'use strict'; | ||
var constants = require('../../constants'); | ||
var tracingHeaders = require('../../tracingHeaders'); | ||
var urlUtil = require('../../../util/url'); | ||
@@ -48,18 +49,33 @@ var httpCommon = require('./_http'); | ||
// Respect any incoming tracing level headers | ||
if (req && req.headers && req.headers[constants.traceLevelHeaderNameLowerCase] === '0') { | ||
cls.setTracingLevel(req.headers[constants.traceLevelHeaderNameLowerCase]); | ||
var headers = tracingHeaders.fromHttpRequest(req); | ||
var w3cTraceContext = headers.w3cTraceContext; | ||
if (typeof headers.level === 'string' && headers.level.indexOf('0') === 0) { | ||
cls.setTracingLevel('0'); | ||
if (w3cTraceContext) { | ||
w3cTraceContext.disableSampling(); | ||
} | ||
} | ||
if (w3cTraceContext) { | ||
// Ususally we commit the W3C trace context to CLS in start span, but in some cases (e.g. when suppressed), | ||
// we don't call startSpan, so we write to CLS here unconditionally. If we also write an update trace context | ||
// later, the one written here will be overwritten. | ||
cls.setW3cTraceContext(w3cTraceContext); | ||
} | ||
if (cls.tracingSuppressed()) { | ||
// We still need to forward X-INSTANA-L and the W3C trace context; this happens in exit instrumentations | ||
// (like httpClient.js). | ||
return realEmit.apply(originalThis, originalArgs); | ||
} | ||
var incomingTraceId = getExistingTraceId(req); | ||
var incomingParentSpanId = getExistingSpanId(req); | ||
var span = cls.startSpan(exports.spanName, constants.ENTRY, incomingTraceId, incomingParentSpanId); | ||
var span = cls.startSpan(exports.spanName, constants.ENTRY, headers.traceId, headers.parentId, w3cTraceContext); | ||
// Grab the URL before application code gets access to the incoming message. | ||
// We are doing this because libs like express are manipulating req.url when | ||
// using routers. | ||
if (headers.foreignParent) { | ||
span.fp = headers.foreignParent; | ||
} | ||
// Capture the URL before application code gets access to the incoming message. Libraries like express manipulate | ||
// req.url when routers are used. | ||
var urlParts = req.url.split('?'); | ||
@@ -82,4 +98,7 @@ if (urlParts.length >= 1) { | ||
// Handle client / backend eum correlation. | ||
// Handle client/back end eum correlation. | ||
if (!span.p) { | ||
// We add the trace ID to the incoming request so a customer's app can render it into the EUM snippet, see | ||
// eslint-disable-next-line max-len | ||
// https://docs.instana.io/products/website_monitoring/backendCorrelation/#retrieve-the-backend-trace-id-in-nodejs | ||
req.headers['x-instana-t'] = span.t; | ||
@@ -149,20 +168,4 @@ | ||
function getExistingSpanId(req) { | ||
var spanId = req.headers[constants.spanIdHeaderNameLowerCase]; | ||
if (spanId == null) { | ||
return null; | ||
} | ||
return spanId; | ||
} | ||
function getExistingTraceId(req) { | ||
var traceId = req.headers[constants.traceIdHeaderNameLowerCase]; | ||
if (traceId == null) { | ||
return null; | ||
} | ||
return traceId; | ||
} | ||
exports.setExtraHttpHeadersToCapture = function setExtraHttpHeadersToCapture(_extraHeaders) { | ||
extraHttpHeadersToCapture = _extraHeaders; | ||
}; |
@@ -24,2 +24,6 @@ 'use strict'; | ||
exports.generateRandomLongTraceId = function generateRandomLongTraceId() { | ||
return exports.generateRandomId(32); | ||
}; | ||
exports.generateRandomSpanId = function generateRandomSpanId() { | ||
@@ -26,0 +30,0 @@ return exports.generateRandomId(16); |
316875
82
8580