diff --git a/api.js b/api.js index cde8f4f663..3e0c5c7933 100644 --- a/api.js +++ b/api.js @@ -1524,12 +1524,13 @@ API.prototype.getTraceMetadata = function getTraceMetadata() { const metadata = {} const segment = this.agent.tracer.getSegment() - if (!segment) { + const transaction = this.agent.tracer.getTransaction() + if (!(segment || transaction)) { logger.debug('No transaction found when calling API#getTraceMetadata') } else if (!this.agent.config.distributed_tracing.enabled) { logger.debug('Distributed tracing disabled when calling API#getTraceMetadata') } else { - metadata.traceId = segment.transaction.traceId + metadata.traceId = transaction.traceId const spanId = segment.getSpanId() if (spanId) { diff --git a/lib/agent.js b/lib/agent.js index 599ce34260..3898c8e219 100644 --- a/lib/agent.js +++ b/lib/agent.js @@ -834,6 +834,7 @@ Agent.prototype._listenForConfigChanges = function _listenForConfigChanges() { */ Agent.prototype.getLinkingMetadata = function getLinkingMetadata() { const segment = this.tracer.getSegment() + const transaction = this.tracer.getTransaction() const config = this.config const linkingMetadata = { @@ -843,7 +844,7 @@ Agent.prototype.getLinkingMetadata = function getLinkingMetadata() { } if (config.distributed_tracing.enabled && segment) { - linkingMetadata['trace.id'] = segment.transaction.traceId + linkingMetadata['trace.id'] = transaction.traceId const spanId = segment.getSpanId() if (spanId) { linkingMetadata['span.id'] = spanId diff --git a/lib/attributes.js b/lib/attributes.js index 5b6ec8205d..04aa964e6c 100644 --- a/lib/attributes.js +++ b/lib/attributes.js @@ -22,7 +22,7 @@ class Attributes { * @param {string} scope * The scope of the attributes this will collect. Must be `transaction` or * `segment`. - * @param {number} [limit=Infinity] + * @param {number} [limit] * The maximum number of attributes to retrieve for each destination. */ constructor(scope, limit = Infinity) { @@ -104,7 +104,7 @@ class Attributes { * @param {DESTINATIONS} destinations - The default destinations for this key. * @param {string} key - The attribute name. * @param {string} value - The attribute value. - * @param {boolean} [truncateExempt=false] - Flag marking value exempt from truncation + * @param {boolean} [truncateExempt] - Flag marking value exempt from truncation */ addAttribute(destinations, key, value, truncateExempt = false) { if (this.attributeCount + 1 > this.limit) { diff --git a/lib/collector/remote-method.js b/lib/collector/remote-method.js index 897b366939..dc0a2e53f6 100644 --- a/lib/collector/remote-method.js +++ b/lib/collector/remote-method.js @@ -92,7 +92,7 @@ RemoteMethod.prototype._reportDataUsage = function reportDataUsage(sent, receive * you're doing it wrong. * * @param {object} payload Serializable payload. - * @param {object} [nrHeaders=null] NR request headers from connect response. + * @param {object} [nrHeaders] NR request headers from connect response. * @param {Function} callback What to do next. Gets passed any error. */ RemoteMethod.prototype.invoke = function invoke(payload, nrHeaders, callback) { diff --git a/lib/db/parsed-statement.js b/lib/db/parsed-statement.js index c509e22ab2..1e23e39b07 100644 --- a/lib/db/parsed-statement.js +++ b/lib/db/parsed-statement.js @@ -21,10 +21,9 @@ function ParsedStatement(type, operation, collection, raw) { } } -ParsedStatement.prototype.recordMetrics = function recordMetrics(segment, scope) { +ParsedStatement.prototype.recordMetrics = function recordMetrics(segment, scope, transaction) { const duration = segment.getDurationInMillis() const exclusive = segment.getExclusiveDurationInMillis() - const transaction = segment.transaction const type = transaction.isWeb() ? DB.WEB : DB.OTHER const thisTypeSlash = this.type + '/' const operation = DB.OPERATION + '/' + thisTypeSlash + this.operation @@ -68,7 +67,13 @@ ParsedStatement.prototype.recordMetrics = function recordMetrics(segment, scope) } if (this.raw) { - transaction.agent.queries.add(segment, this.type.toLowerCase(), this.raw, this.trace) + transaction.agent.queries.add({ + segment, + transaction, + type: this.type.toLowerCase(), + query: this.raw, + trace: this.trace + }) } } diff --git a/lib/db/query-sample.js b/lib/db/query-sample.js index 46051d38e4..985f258286 100644 --- a/lib/db/query-sample.js +++ b/lib/db/query-sample.js @@ -34,7 +34,7 @@ QuerySample.prototype.merge = function merge(sample) { } QuerySample.prototype.prepareJSON = function prepareJSON(done) { - const transaction = this.trace.segment.transaction + const transaction = this.trace.transaction const sample = this const trace = sample.trace @@ -56,7 +56,7 @@ QuerySample.prototype.prepareJSON = function prepareJSON(done) { } QuerySample.prototype.prepareJSONSync = function prepareJSONSync() { - const transaction = this.trace.segment.transaction + const transaction = this.trace.transaction const sample = this const trace = sample.trace @@ -99,7 +99,7 @@ QuerySample.prototype.getParams = function getParams() { } if (this.tracer.config.distributed_tracing.enabled) { - this.trace.segment.transaction.addDistributedTraceIntrinsics(params) + this.trace.transaction.addDistributedTraceIntrinsics(params) } return params diff --git a/lib/db/query-trace-aggregator.js b/lib/db/query-trace-aggregator.js index bb3e2700a3..2f4b6047da 100644 --- a/lib/db/query-trace-aggregator.js +++ b/lib/db/query-trace-aggregator.js @@ -48,7 +48,7 @@ class QueryTraceAggregator extends Aggregator { } } - add(segment, type, query, trace) { + add({ segment, transaction, type, query, trace }) { const ttConfig = this.config.transaction_tracer // If DT is enabled and the segment is part of a sampled transaction @@ -57,12 +57,12 @@ class QueryTraceAggregator extends Aggregator { let slowQuery switch (ttConfig.record_sql) { case 'raw': - slowQuery = new SlowQuery(segment, type, query, trace) + slowQuery = new SlowQuery({ segment, transaction, type, query, trace }) logger.trace('recording raw sql') segment.addAttribute('sql', slowQuery.query, true) break case 'obfuscated': - slowQuery = new SlowQuery(segment, type, query, trace) + slowQuery = new SlowQuery({ transaction, segment, type, query, trace }) logger.trace('recording obfuscated sql') segment.addAttribute('sql_obfuscated', slowQuery.obfuscated, true) break @@ -78,7 +78,7 @@ class QueryTraceAggregator extends Aggregator { return } - slowQuery = slowQuery || new SlowQuery(segment, type, query, trace) + slowQuery = slowQuery || new SlowQuery({ segment, transaction, type, query, trace }) segment.addAttribute('backtrace', slowQuery.trace) diff --git a/lib/db/slow-query.js b/lib/db/slow-query.js index c80bcf881a..2dcc38a237 100644 --- a/lib/db/slow-query.js +++ b/lib/db/slow-query.js @@ -10,7 +10,7 @@ const crypto = require('crypto') const path = require('path') const NR_ROOT = path.resolve(__dirname, '..') -function SlowQuery(segment, type, query, trace) { +function SlowQuery({ segment, transaction, type, query, trace }) { this.obfuscated = obfuscate(query, type) this.normalized = this.obfuscated.replace(/\?\s*,\s*|\s*/g, '') this.id = normalizedHash(this.normalized) @@ -18,6 +18,7 @@ function SlowQuery(segment, type, query, trace) { this.query = query this.metric = segment.name this.trace = formatTrace(trace) + this.transaction = transaction this.duration = segment.getDurationInMillis() } diff --git a/lib/environment.js b/lib/environment.js index 694e679b45..c8c2becc5c 100644 --- a/lib/environment.js +++ b/lib/environment.js @@ -67,7 +67,7 @@ function clearSetting(name) { * the provided root. * * @param {string} root - Path to start listing packages from. - * @param {Array} [packages=[]] - Array to append found packages to. + * @param {Array} [packages] - Array to append found packages to. */ async function listPackages(root, packages = []) { _log('Listing packages in %s', root) diff --git a/lib/grpc/connection.js b/lib/grpc/connection.js index 29579a78d5..49929b4340 100644 --- a/lib/grpc/connection.js +++ b/lib/grpc/connection.js @@ -43,7 +43,7 @@ class GrpcConnection extends EventEmitter { * * @param {object} infiniteTracingConfig config item config.infinite_tracing * @param {MetricAggregator} metrics metric aggregator, for supportability metrics - * @param {number} [reconnectDelayMs=15000] number of milliseconds to wait before reconnecting + * @param {number} [reconnectDelayMs] number of milliseconds to wait before reconnecting * for error states that require a reconnect delay. */ constructor(infiniteTracingConfig, metrics, reconnectDelayMs = DEFAULT_RECONNECT_DELAY_MS) { diff --git a/lib/instrumentation/aws-sdk/v3/bedrock.js b/lib/instrumentation/aws-sdk/v3/bedrock.js index 7fe1b4d277..665b2caf3b 100644 --- a/lib/instrumentation/aws-sdk/v3/bedrock.js +++ b/lib/instrumentation/aws-sdk/v3/bedrock.js @@ -70,10 +70,11 @@ function recordEvent({ agent, type, msg }) { * @param {object} params input params * @param {Agent} params.agent NR agent instance * @param {TraceSegment} params.segment active segment + * @param params.transaction */ -function addLlmMeta({ agent, segment }) { +function addLlmMeta({ agent, segment, transaction }) { agent.metrics.getOrCreateMetric(TRACKING_METRIC).incrementCallCount() - segment.transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true) + transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true) // end segment to get a consistent segment duration // for both the LLM events and the segment segment.end() @@ -92,11 +93,13 @@ function addLlmMeta({ agent, segment }) { * @param {Error|null} params.err error from request if exists * @param params.bedrockResponse * @param params.shim + * @param params.transaction */ function recordChatCompletionMessages({ agent, shim, segment, + transaction, bedrockCommand, bedrockResponse, err @@ -110,6 +113,7 @@ function recordChatCompletionMessages({ agent, bedrockResponse, bedrockCommand, + transaction, segment, isError: err !== null }) @@ -119,6 +123,7 @@ function recordChatCompletionMessages({ segment, bedrockCommand, bedrockResponse, + transaction, index: 0, completionId: summary.id }) @@ -128,6 +133,7 @@ function recordChatCompletionMessages({ const chatCompletionMessage = new LlmChatCompletionMessage({ agent, segment, + transaction, bedrockCommand, bedrockResponse, isResponse: true, @@ -142,7 +148,7 @@ function recordChatCompletionMessages({ if (err) { const llmError = new LlmError({ bedrockResponse, err, summary }) - agent.errors.add(segment.transaction, err, llmError) + agent.errors.add(transaction, err, llmError) } } @@ -157,8 +163,17 @@ function recordChatCompletionMessages({ * @param {BedrockCommand} params.bedrockCommand parsed input * @param {Error|null} params.err error from request if exists * @param params.bedrockResponse + * @param params.transaction */ -function recordEmbeddingMessage({ agent, shim, segment, bedrockCommand, bedrockResponse, err }) { +function recordEmbeddingMessage({ + agent, + shim, + segment, + transaction, + bedrockCommand, + bedrockResponse, + err +}) { if (shouldSkipInstrumentation(agent.config) === true) { shim.logger.debug('skipping sending of ai data') return @@ -167,6 +182,7 @@ function recordEmbeddingMessage({ agent, shim, segment, bedrockCommand, bedrockR const embedding = new LlmEmbedding({ agent, segment, + transaction, bedrockCommand, bedrockResponse, isError: err !== null @@ -175,7 +191,7 @@ function recordEmbeddingMessage({ agent, shim, segment, bedrockCommand, bedrockR recordEvent({ agent, type: 'LlmEmbedding', msg: embedding }) if (err) { const llmError = new LlmError({ bedrockResponse, err, embedding }) - agent.errors.add(segment.transaction, err, llmError) + agent.errors.add(transaction, err, llmError) } } @@ -222,12 +238,13 @@ function getBedrockSpec({ commandName }, shim, _original, _name, args) { return new RecorderSpec({ promise: true, name: `Llm/${modelType}/Bedrock/${commandName}`, - after: ({ shim, error: err, result: response, segment }) => { + after: ({ shim, error: err, result: response, segment, transaction }) => { const passThroughParams = { shim, err, response, segment, + transaction, bedrockCommand, modelType } @@ -250,22 +267,23 @@ function getBedrockSpec({ commandName }, shim, _original, _name, args) { 'ai_monitoring.streaming.enabled is set to `false`, stream will not be instrumented.' ) agent.metrics.getOrCreateMetric(AI.STREAMING_DISABLED).incrementCallCount() - addLlmMeta({ agent, segment }) + addLlmMeta({ agent, segment, transaction }) } } }) } -function handleResponse({ shim, err, response, segment, bedrockCommand, modelType }) { +function handleResponse({ shim, err, response, segment, transaction, bedrockCommand, modelType }) { const { agent } = shim const bedrockResponse = createBedrockResponse({ bedrockCommand, response, err }) - addLlmMeta({ agent, segment }) + addLlmMeta({ agent, segment, transaction }) if (modelType === 'completion') { recordChatCompletionMessages({ agent, shim, segment, + transaction, bedrockCommand, bedrockResponse, err @@ -275,6 +293,7 @@ function handleResponse({ shim, err, response, segment, bedrockCommand, modelTyp agent, shim, segment, + transaction, bedrockCommand, bedrockResponse, err diff --git a/lib/instrumentation/core/globals.js b/lib/instrumentation/core/globals.js index 2cd4a2dfba..d97fe67a18 100644 --- a/lib/instrumentation/core/globals.js +++ b/lib/instrumentation/core/globals.js @@ -43,9 +43,7 @@ function initialize(agent, nodule, name, shim) { !process.domain && process.listenerCount('unhandledRejection') === 0 ) { - // If there are no unhandledRejection handlers report the error. - const segment = promise[symbols.context] && promise[symbols.context].getSegment() - const tx = segment && segment.transaction + const tx = promise[symbols.context] && promise[symbols.context].getTransaction() shim.logger.trace('Captured unhandled rejection for transaction %s', tx && tx.id) agent.errors.add(tx, error) } diff --git a/lib/instrumentation/core/http-outbound.js b/lib/instrumentation/core/http-outbound.js index c1d6d2d52b..1caa57d12a 100644 --- a/lib/instrumentation/core/http-outbound.js +++ b/lib/instrumentation/core/http-outbound.js @@ -145,10 +145,14 @@ module.exports = function instrumentOutbound(agent, opts, makeRequest) { * @param {string} params.hostname domain of outbound request * @param {string} params.port port of outbound request * @param {TraceSegment} segment outbound http segment + * @param {Transaction} transaction active tx * @returns {http.IncomingMessage} request actual http outbound request */ -function instrumentRequest({ agent, opts, makeRequest, host, port, hostname }, segment) { - const transaction = segment.transaction +function instrumentRequest( + { agent, opts, makeRequest, host, port, hostname }, + segment, + transaction +) { const outboundHeaders = Object.create(null) opts.headers = opts.headers || {} @@ -157,7 +161,15 @@ function instrumentRequest({ agent, opts, makeRequest, host, port, hostname }, s maybeAddDtCatHeaders(agent, transaction, outboundHeaders, opts?.headers) opts.headers = assignOutgoingHeaders(opts.headers, outboundHeaders) - const request = applySegment({ opts, makeRequest, hostname, host, port, segment }) + const request = applySegment({ + opts, + makeRequest, + hostname, + host, + port, + segment, + config: agent.config + }) instrumentRequestEmit(agent, host, segment, request) @@ -225,13 +237,14 @@ function assignOutgoingHeaders(currentHeaders, outboundHeaders) { * @param {string} params.port port of outbound request * @param {string} params.hostname host + port of outbound request * @param {TraceSegment} params.segment outbound http segment + * @param {Transaction} params.config agent config * @returns {http.IncomingMessage} request actual http outbound request */ -function applySegment({ opts, makeRequest, host, port, hostname, segment }) { +function applySegment({ opts, makeRequest, host, port, hostname, segment, config }) { segment.start() const request = makeRequest(opts) const parsed = urltils.scrubAndParseParameters(request.path) - parsed.path = urltils.obfuscatePath(segment.transaction.agent.config, parsed.path) + parsed.path = urltils.obfuscatePath(config, parsed.path) const proto = parsed.protocol || opts.protocol || 'http:' segment.name += parsed.path segment.captureExternalAttributes({ @@ -262,11 +275,12 @@ function instrumentRequestEmit(agent, host, segment, request) { shimmer.wrapMethod(request, 'request.emit', 'emit', function wrapEmit(emit) { const boundEmit = agent.tracer.bindFunction(emit, segment) return function wrappedRequestEmit(evnt, arg) { + const transaction = agent.tracer.getTransaction() if (evnt === 'error') { segment.end() - handleError(segment, request, arg) + handleError({ transaction, req: request, error: arg }) } else if (evnt === 'response') { - handleResponse(segment, host, arg) + handleResponse({ agent, segment, transaction, host, res: arg }) } return boundEmit.apply(this, arguments) @@ -281,41 +295,47 @@ function instrumentRequestEmit(agent, host, segment, request) { * request object. * * @param {object} segment TraceSegment instance + * @param segment.transaction * @param {object} req http.ClientRequest * @param {Error} error If provided, unhandled error that occurred during request + * @param segment.req + * @param segment.error * @returns {boolean} True if the error will be collected by New Relic. */ -function handleError(segment, req, error) { +function handleError({ transaction, req, error }) { if (req.listenerCount('error') > 0) { logger.trace(error, 'Not capturing outbound error because user has already handled it.') return false } logger.trace(error, 'Captured outbound error on behalf of the user.') - const tx = segment.transaction - tx.agent.errors.add(tx, error) + transaction.agent.errors.add(transaction, error) return true } /** * Ties the response object to the request segment. * + * @param segment.agent * @param {object} segment TraceSegment instance * @param {string} hostname host of the HTTP request * @param host * @param {object} res http.ServerResponse + * @param segment.segment + * @param segment.transaction + * @param segment.host + * @param segment.res */ -function handleResponse(segment, host, res) { +function handleResponse({ agent, segment, transaction, host, res }) { // Add response attributes for spans segment.addSpanAttribute('http.statusCode', res.statusCode) segment.addSpanAttribute('http.statusText', res.statusMessage) // If CAT is enabled, grab those headers! - const agent = segment.transaction.agent if (agent.config.cross_application_tracer.enabled && !agent.config.distributed_tracing.enabled) { const { appData } = cat.extractCatHeaders(res.headers) const decodedAppData = cat.parseAppData(agent.config, appData) - cat.assignCatToSegment(decodedAppData, segment, host) + cat.assignCatToSegment({ appData: decodedAppData, segment, host, transaction }) } // Again a custom emit wrapper because we want to watch for the `end` event. diff --git a/lib/instrumentation/grpc-js/grpc.js b/lib/instrumentation/grpc-js/grpc.js index 438facefec..1d0b4519a1 100644 --- a/lib/instrumentation/grpc-js/grpc.js +++ b/lib/instrumentation/grpc-js/grpc.js @@ -48,6 +48,7 @@ function wrapStart(shim, original) { return original.apply(this, arguments) } + const transaction = shim.tracer.getTransaction() const channel = this.channel const authorityName = (channel.target && channel.target.path) || channel.getDefaultAuthority // in 1.8.0 this changed from methodName to method @@ -64,8 +65,6 @@ function wrapStart(shim, original) { function callStart() { const args = shim.argsToArray.apply(shim, arguments) - const transaction = segment.transaction - const originalMetadata = args[0] const nrMetadata = originalMetadata.clone() @@ -93,7 +92,7 @@ function wrapStart(shim, original) { const config = agent.config if (shouldTrackError(code, config)) { - shim.agent.errors.add(segment.transaction, details) + shim.agent.errors.add(transaction, details) } segment.addAttribute('component', 'gRPC') diff --git a/lib/instrumentation/kafkajs/consumer.js b/lib/instrumentation/kafkajs/consumer.js index c8a71ada44..a1b566b060 100644 --- a/lib/instrumentation/kafkajs/consumer.js +++ b/lib/instrumentation/kafkajs/consumer.js @@ -100,14 +100,14 @@ function handler({ consumer }) { * * @param {MessageShim} shim instance of shim * @param {Array} args arguments passed to the `eachMessage` function of the `consumer.run` + * @param tx * @returns {MessageSpec} spec for message handling */ - return function messageHandler(shim, args) { + return function messageHandler(shim, args, tx) { recordMethodMetric({ agent: shim.agent, name: 'eachMessage' }) const [data] = args const { topic } = data - const segment = shim.getActiveSegment() recordLinkingMetrics({ agent: shim.agent, @@ -116,8 +116,7 @@ function handler({ consumer }) { producer: false }) - if (segment?.transaction) { - const tx = segment.transaction + if (tx) { const byteLength = data?.message.value?.byteLength const metricPrefix = `Message/Kafka/Topic/Named/${topic}/Received` // This will always be 1 diff --git a/lib/instrumentation/koa/instrumentation.js b/lib/instrumentation/koa/instrumentation.js index 7514529017..602f02895c 100644 --- a/lib/instrumentation/koa/instrumentation.js +++ b/lib/instrumentation/koa/instrumentation.js @@ -117,14 +117,12 @@ function wrapMatchedRoute(shim, context) { set: (val) => { // match should never be undefined given _matchedRoute was set if (val) { - const currentSegment = shim.getActiveSegment() + const transaction = shim.tracer.getTransaction() // Segment/Transaction may be null, see: // - https://github.com/newrelic/node-newrelic-koa/issues/32 // - https://github.com/newrelic/node-newrelic-koa/issues/33 - if (currentSegment) { - const transaction = currentSegment.transaction - + if (transaction) { if (context[symbols.koaMatchedRoute]) { transaction.nameState.popPath() } diff --git a/lib/instrumentation/langchain/runnable.js b/lib/instrumentation/langchain/runnable.js index b6d76f4ba6..1159730736 100644 --- a/lib/instrumentation/langchain/runnable.js +++ b/lib/instrumentation/langchain/runnable.js @@ -62,8 +62,9 @@ function instrumentInvokeChain({ langchain, shim }) { return new RecorderSpec({ name: `${LANGCHAIN.CHAIN}/${fnName}`, promise: true, - after({ error: err, result: output, segment }) { + after({ error: err, result: output, segment, transaction }) { recordChatCompletionEvents({ + transaction, segment, messages: [output], events: [request, output], @@ -97,14 +98,15 @@ function instrumentStream({ langchain, shim }) { return new RecorderSpec({ name: `${LANGCHAIN.CHAIN}/${fnName}`, promise: true, - after({ error: err, result: output, segment }) { + after({ error: err, result: output, segment, transaction }) { // Input error occurred which means a stream was not created. // Skip instrumenting streaming and create Llm Events from // the data we have if (output?.next) { - wrapNextHandler({ shim, output, segment, request, metadata, tags }) + wrapNextHandler({ shim, output, segment, request, metadata, tags, transaction }) } else { recordChatCompletionEvents({ + transaction, segment, messages: [], events: [request], @@ -131,8 +133,9 @@ function instrumentStream({ langchain, shim }) { * @param {string} params.request the prompt message * @param {object} params.metadata metadata for the call * @param {Array} params.tags tags for the call + * @param params.transaction */ -function wrapNextHandler({ shim, output, segment, request, metadata, tags }) { +function wrapNextHandler({ shim, output, segment, transaction, request, metadata, tags }) { shim.wrap(output, 'next', function wrapIterator(shim, orig) { let content = '' return async function wrappedIterator() { @@ -141,6 +144,7 @@ function wrapNextHandler({ shim, output, segment, request, metadata, tags }) { // only create Llm events when stream iteration is done if (result?.done) { recordChatCompletionEvents({ + transaction, segment, messages: [content], events: [request, content], @@ -154,6 +158,7 @@ function wrapNextHandler({ shim, output, segment, request, metadata, tags }) { return result } catch (error) { recordChatCompletionEvents({ + transaction, segment, messages: [content], events: [request, content], @@ -183,8 +188,18 @@ function wrapNextHandler({ shim, output, segment, request, metadata, tags }) { * @param {Array} params.tags tags for the call * @param {Error} params.err error object from call * @param {Shim} params.shim shim instance + * @param params.transaction */ -function recordChatCompletionEvents({ segment, messages, events, metadata, tags, err, shim }) { +function recordChatCompletionEvents({ + segment, + transaction, + messages, + events, + metadata, + tags, + err, + shim +}) { const { pkgVersion, agent } = shim segment.end() @@ -226,7 +241,7 @@ function recordChatCompletionEvents({ segment, messages, events, metadata, tags, if (err) { agent.errors.add( - segment.transaction, + transaction, err, new LlmErrorMessage({ response: {}, @@ -236,7 +251,7 @@ function recordChatCompletionEvents({ segment, messages, events, metadata, tags, ) } - segment.transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true) + transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true) } /** diff --git a/lib/instrumentation/langchain/tools.js b/lib/instrumentation/langchain/tools.js index 17b0178998..c867e141da 100644 --- a/lib/instrumentation/langchain/tools.js +++ b/lib/instrumentation/langchain/tools.js @@ -31,7 +31,7 @@ module.exports = function initialize(shim, tools) { return new RecorderSpec({ name: `${LANGCHAIN.TOOL}/${name}`, promise: true, - after({ error: err, result: output, segment }) { + after({ error: err, result: output, segment, transaction }) { const metadata = mergeMetadata(instanceMeta, paramsMeta) const tags = mergeTags(instanceTags, paramsTags) segment.end() @@ -60,7 +60,7 @@ module.exports = function initialize(shim, tools) { if (err) { agent.errors.add( - segment.transaction, + transaction, err, new LlmErrorMessage({ response: {}, @@ -70,7 +70,7 @@ module.exports = function initialize(shim, tools) { ) } - segment.transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true) + transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true) } }) }) diff --git a/lib/instrumentation/langchain/vectorstore.js b/lib/instrumentation/langchain/vectorstore.js index 61d27174b0..af1298e363 100644 --- a/lib/instrumentation/langchain/vectorstore.js +++ b/lib/instrumentation/langchain/vectorstore.js @@ -27,8 +27,19 @@ const LlmErrorMessage = require('../../llm-events/error-message') * @param {TraceSegment} params.segment active segment from vector search * @param {string} params.pkgVersion langchain version * @param {Error} params.err if it exists + * @param params.transaction */ -function recordVectorSearch({ request, k, output, agent, shim, segment, pkgVersion, err }) { +function recordVectorSearch({ + request, + k, + output, + agent, + shim, + segment, + transaction, + pkgVersion, + err +}) { const vectorSearch = new LangChainVectorSearch({ agent, segment, @@ -61,7 +72,7 @@ function recordVectorSearch({ request, k, output, agent, shim, segment, pkgVersi if (err) { agent.errors.add( - segment.transaction, + transaction, err, new LlmErrorMessage({ response: output, @@ -91,7 +102,7 @@ module.exports = function initialize(shim, vectorstores) { return new RecorderSpec({ name: `${LANGCHAIN.VECTORSTORE}/${fnName}`, promise: true, - after({ error: err, result: output, segment }) { + after({ error: err, result: output, segment, transaction }) { if (!output) { // If we get an error, it is possible that `output = null`. // In that case, we define it to be an empty array. @@ -107,9 +118,19 @@ module.exports = function initialize(shim, vectorstores) { return } - recordVectorSearch({ request, k, output, agent, shim, segment, pkgVersion, err }) + recordVectorSearch({ + request, + k, + output, + agent, + shim, + segment, + transaction, + pkgVersion, + err + }) - segment.transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true) + transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true) } }) } diff --git a/lib/instrumentation/nextjs/next-server.js b/lib/instrumentation/nextjs/next-server.js index c2528be5d6..37272e6a35 100644 --- a/lib/instrumentation/nextjs/next-server.js +++ b/lib/instrumentation/nextjs/next-server.js @@ -122,10 +122,8 @@ module.exports = function initialize(shim, nextServer) { } function assignParameters(shim, parameters) { - const activeSegment = shim.getActiveSegment() - if (activeSegment) { - const transaction = activeSegment.transaction - + const transaction = shim.tracer.getTransaction() + if (transaction) { const prefixedParameters = shim.prefixRouteParameters(parameters) // We have to add params because this framework doesn't diff --git a/lib/instrumentation/openai.js b/lib/instrumentation/openai.js index 36e4487057..3f4dc78465 100644 --- a/lib/instrumentation/openai.js +++ b/lib/instrumentation/openai.js @@ -89,11 +89,11 @@ function recordEvent({ agent, type, msg }) { * * @param {object} params input params * @param {Agent} params.agent NR agent instance - * @param {TraceSegment} params.segment active segment + * @param {Transaction} params.transaction active transaction */ -function addLlmMeta({ agent, segment }) { +function addLlmMeta({ agent, transaction }) { agent.metrics.getOrCreateMetric(TRACKING_METRIC).incrementCallCount() - segment.transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true) + transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true) } /** @@ -110,8 +110,17 @@ function addLlmMeta({ agent, segment }) { * @param {object} params.request chat completion params * @param {object} params.response chat completion response * @param {boolean} [params.err] err if it exists + * @param params.transaction */ -function recordChatCompletionMessages({ agent, shim, segment, request, response, err }) { +function recordChatCompletionMessages({ + agent, + shim, + segment, + request, + response, + err, + transaction +}) { if (shouldSkipInstrumentation(agent.config, shim) === true) { shim.logger.debug('skipping sending of ai data') return @@ -155,7 +164,7 @@ function recordChatCompletionMessages({ agent, shim, segment, request, response, if (err) { const llmError = new LlmErrorMessage({ cause: err, summary: completionSummary, response }) - agent.errors.add(segment.transaction, err, llmError) + agent.errors.add(transaction, err, llmError) } delete response.headers @@ -170,7 +179,7 @@ function recordChatCompletionMessages({ agent, shim, segment, request, response, * messages * */ -function instrumentStream({ agent, shim, request, response, segment }) { +function instrumentStream({ agent, shim, request, response, segment, transaction }) { if (!agent.config.ai_monitoring.streaming.enabled) { shim.logger.warn( '`ai_monitoring.streaming.enabled` is set to `false`, stream will not be instrumented.' @@ -209,6 +218,7 @@ function instrumentStream({ agent, shim, request, response, segment }) { agent: shim.agent, shim, segment, + transaction, request, response: chunk, err @@ -276,21 +286,22 @@ module.exports = function initialize(agent, openai, moduleName, shim) { return new RecorderSpec({ name: OPENAI.COMPLETION, promise: true, - after({ error: err, result: response, segment }) { + after({ error: err, result: response, segment, transaction }) { if (request.stream) { - instrumentStream({ agent, shim, request, response, segment }) + instrumentStream({ agent, shim, request, response, segment, transaction }) } else { recordChatCompletionMessages({ agent, shim, segment, + transaction, request, response, err }) } - addLlmMeta({ agent, segment }) + addLlmMeta({ agent, transaction }) } }) } @@ -308,8 +319,8 @@ module.exports = function initialize(agent, openai, moduleName, shim) { return new RecorderSpec({ name: OPENAI.EMBEDDING, promise: true, - after({ error: err, result: response, segment }) { - addLlmMeta({ agent, segment }) + after({ error: err, result: response, segment, transaction }) { + addLlmMeta({ agent, transaction }) if (!response) { // If we get an error, it is possible that `response = null`. @@ -342,7 +353,7 @@ module.exports = function initialize(agent, openai, moduleName, shim) { if (err) { const llmError = new LlmErrorMessage({ cause: err, embedding, response }) - shim.agent.errors.add(segment.transaction, err, llmError) + shim.agent.errors.add(transaction, err, llmError) } // cleanup keys on response before returning to user code diff --git a/lib/instrumentation/restify.js b/lib/instrumentation/restify.js index 85960bf157..463cc733de 100644 --- a/lib/instrumentation/restify.js +++ b/lib/instrumentation/restify.js @@ -24,13 +24,13 @@ module.exports = function initialize(_agent, restify, _moduleName, shim) { shim.wrap(http.ServerResponse.prototype, methods, function wrapMethod(shim, fn) { return function wrappedMethod() { - const segment = shim.getActiveSegment() + const transaction = shim.tracer.getTransaction() - if (segment) { + if (transaction) { // Freezing the name state prevents transaction names from potentially being // manipulated by asynchronous response methods executed as part of res.send() // but before `next()` is called. - segment.transaction.nameState.freeze() + transaction.nameState.freeze() } return fn.apply(this, arguments) diff --git a/lib/instrumentation/superagent.js b/lib/instrumentation/superagent.js index 185925ee55..94aaa64cc9 100644 --- a/lib/instrumentation/superagent.js +++ b/lib/instrumentation/superagent.js @@ -48,8 +48,8 @@ function wrapHttpReq(shim, fn, name, req) { function wrapCallback(shim, callback) { return function wrappedCallback() { - const segment = shim.getSegment(this) - if (segment && segment.transaction.isActive()) { + const segment = shim.getActiveSegment(this) + if (segment) { shim.bindCallbackSegment(null, this, '_callback', segment) } return callback.apply(this, arguments) @@ -58,7 +58,7 @@ function wrapCallback(shim, callback) { function wrapThen(shim, then) { return function wrappedThen(resolve, reject) { - const segment = shim.getSegment(this) || shim.getActiveSegment() + const segment = shim.getActiveSegment(this) if (!segment) { return then.apply(this, arguments) } diff --git a/lib/instrumentation/undici.js b/lib/instrumentation/undici.js index cda18b0942..adfe2182f4 100644 --- a/lib/instrumentation/undici.js +++ b/lib/instrumentation/undici.js @@ -66,7 +66,7 @@ module.exports.unsubscribe = function unsubscribe() { * @param {Shim} shim instance of shim * @returns {TraceSegment} parent segment */ -function getParentSegment(shim) { +function getParentContext(shim) { const { config } = shim.agent if (config.feature_flag.undici_async_tracking) { const resource = executionAsyncResource() @@ -74,10 +74,13 @@ function getParentSegment(shim) { if (!resource[symbols.parentSegment]) { const parent = shim.getSegment() resource[symbols.parentSegment] = parent + const tx = shim.tracer.getTransaction() + resource[symbols.transaction] = tx } - return resource[symbols.parentSegment] + return { segment: resource[symbols.parentSegment], transaction: resource[symbols.transaction] } } - return shim.getSegment() + + return shim.tracer.getContext() } /** @@ -113,24 +116,25 @@ function addDTHeaders({ transaction, config, request }) { * @param {Shim} params.shim instance of shim * @param {object} params.request undici request object * @param {object} params.parentSegment current active, about to be parent of external segment + * @param params.segment */ -function createExternalSegment({ shim, request, parentSegment }) { +function createExternalSegment({ shim, request, segment }) { const url = new URL(request.origin + request.path) const obfuscatedPath = urltils.obfuscatePath(shim.agent.config, url.pathname) const name = NAMES.EXTERNAL.PREFIX + url.host + obfuscatedPath // Metrics for `External/` will have a suffix of undici // We will have to see if this matters for people only using fetch // It's undici under the hood so ¯\_(ツ)_/¯ - const segment = shim.createSegment(name, recordExternal(url.host, 'undici'), parentSegment) + const externalSegment = shim.createSegment(name, recordExternal(url.host, 'undici'), segment) // the captureExternalAttributes expects queryParams to be an object, do conversion // to object see: https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams const queryParams = Object.fromEntries(url.searchParams.entries()) - if (segment) { - segment.start() - shim.setActiveSegment(segment) - segment.captureExternalAttributes({ + if (externalSegment) { + externalSegment.start() + shim.setActiveSegment(externalSegment) + externalSegment.captureExternalAttributes({ protocol: url.protocol, hostname: url.hostname, host: url.host, @@ -140,7 +144,7 @@ function createExternalSegment({ shim, request, parentSegment }) { queryParams }) - request[symbols.segment] = segment + request[symbols.segment] = externalSegment } } @@ -156,21 +160,22 @@ function createExternalSegment({ shim, request, parentSegment }) { */ function requestCreateHook(shim, { request }) { const { config } = shim.agent - const parentSegment = getParentSegment(shim) - request[symbols.parentSegment] = parentSegment - if (!parentSegment || (parentSegment && parentSegment.opaque)) { + const { transaction, segment } = getParentContext(shim) + request[symbols.parentSegment] = segment + request[symbols.transaction] = transaction + if (!(segment || transaction) || segment?.opaque) { logger.trace( 'Not capturing data for outbound request (%s) because parent segment opaque (%s)', request.path, - parentSegment && parentSegment.name + segment?.name ) return } try { - addDTHeaders({ transaction: parentSegment.transaction, config, request }) - createExternalSegment({ shim, request, parentSegment }) + addDTHeaders({ transaction, config, request }) + createExternalSegment({ shim, request, segment }) } catch (err) { logger.warn(err, 'Unable to create external segment') } @@ -189,6 +194,7 @@ function requestCreateHook(shim, { request }) { function requestHeadersHook(shim, { request, response }) { const { config } = shim.agent const activeSegment = request[symbols.segment] + const transaction = request[symbols.transaction] if (!activeSegment) { return } @@ -202,7 +208,12 @@ function requestHeadersHook(shim, { request, response }) { const decodedAppData = cat.parseAppData(config, appData) const attrs = activeSegment.getAttributes() const url = new URL(attrs.url) - cat.assignCatToSegment(decodedAppData, activeSegment, url.host) + cat.assignCatToSegment({ + appData: decodedAppData, + segment: activeSegment, + host: url.host, + transaction + }) } catch (err) { logger.warn(err, 'Cannot add CAT data to segment') } @@ -222,11 +233,12 @@ function requestHeadersHook(shim, { request, response }) { function endAndRestoreSegment(shim, { request, error }) { const activeSegment = request[symbols.segment] const parentSegment = request[symbols.parentSegment] + const tx = request[symbols.transaction] if (activeSegment) { activeSegment.end() if (error) { - handleError(shim, activeSegment, error) + handleError(shim, tx, error) } if (parentSegment) { @@ -239,11 +251,10 @@ function endAndRestoreSegment(shim, { request, error }) { * Adds the error to the active transaction * * @param {Shim} shim instance of shim - * @param {TraceSegment} activeSegment active segment + * @param {Transaction} tx active transaction * @param {Error} error error from undici request */ -function handleError(shim, activeSegment, error) { +function handleError(shim, tx, error) { logger.trace(error, 'Captured outbound error on behalf of the user.') - const { transaction } = activeSegment - shim.agent.errors.add(transaction, error) + shim.agent.errors.add(tx, error) } diff --git a/lib/llm-events/aws-bedrock/chat-completion-summary.js b/lib/llm-events/aws-bedrock/chat-completion-summary.js index 20c4edd6c2..36492a0e73 100644 --- a/lib/llm-events/aws-bedrock/chat-completion-summary.js +++ b/lib/llm-events/aws-bedrock/chat-completion-summary.js @@ -10,6 +10,7 @@ const LlmEvent = require('./event') /** * @typedef {object} LlmChatCompletionSummaryParams * @augments LlmEventParams + * @property */ /** * @type {LlmChatCompletionSummaryParams} diff --git a/lib/llm-events/aws-bedrock/embedding.js b/lib/llm-events/aws-bedrock/embedding.js index 3cfa9e3b88..a1eec6e1bf 100644 --- a/lib/llm-events/aws-bedrock/embedding.js +++ b/lib/llm-events/aws-bedrock/embedding.js @@ -10,6 +10,7 @@ const LlmEvent = require('./event') /** * @typedef {object} LlmEmbeddingParams * @augments LlmEventParams + * @property */ /** * @type {LlmEmbeddingParams} diff --git a/lib/llm-events/aws-bedrock/event.js b/lib/llm-events/aws-bedrock/event.js index d5434b6546..946ba40af7 100644 --- a/lib/llm-events/aws-bedrock/event.js +++ b/lib/llm-events/aws-bedrock/event.js @@ -22,9 +22,8 @@ const defaultParams = { agent: {}, bedrockCommand: {}, bedrockResponse: {}, - segment: { - transaction: {} - } + transaction: {}, + segment: {} } /** @@ -47,7 +46,7 @@ class LlmEvent { params = Object.assign({}, defaultParams, params) this.constructionParams = params - const { agent, bedrockCommand, bedrockResponse, segment } = params + const { agent, bedrockCommand, bedrockResponse, segment, transaction } = params this.bedrockCommand = bedrockCommand this.bedrockResponse = bedrockResponse @@ -56,7 +55,7 @@ class LlmEvent { this.ingest_source = 'Node' this.appName = agent.config.applications()[0] this.span_id = segment.id - this.trace_id = segment.transaction.traceId + this.trace_id = transaction.traceId this.request_id = this.bedrockResponse.requestId this.metadata = agent diff --git a/lib/metrics/recorders/custom.js b/lib/metrics/recorders/custom.js index b2d1c588c2..c36cebe44b 100644 --- a/lib/metrics/recorders/custom.js +++ b/lib/metrics/recorders/custom.js @@ -7,10 +7,9 @@ const NAMES = require('../names') -function record(segment, scope) { +function record(segment, scope, transaction) { const duration = segment.getDurationInMillis() const exclusive = segment.getExclusiveDurationInMillis() - const transaction = segment.transaction const name = NAMES.CUSTOM + NAMES.ACTION_DELIMITER + segment.name if (scope) { diff --git a/lib/metrics/recorders/generic.js b/lib/metrics/recorders/generic.js index 3deed95d32..f46293c16c 100644 --- a/lib/metrics/recorders/generic.js +++ b/lib/metrics/recorders/generic.js @@ -5,10 +5,9 @@ 'use strict' -function record(segment, scope) { +function record(segment, scope, transaction) { const duration = segment.getDurationInMillis() const exclusive = segment.getExclusiveDurationInMillis() - const transaction = segment.transaction if (scope) { transaction.measure(segment.name, scope, duration, exclusive) diff --git a/lib/metrics/recorders/http.js b/lib/metrics/recorders/http.js index 83a0880bb4..777424f6bf 100644 --- a/lib/metrics/recorders/http.js +++ b/lib/metrics/recorders/http.js @@ -10,13 +10,12 @@ const recordDistributedTrace = require('./distributed-trace') const TO_MILLIS = 1e3 -function recordWeb(segment, scope) { +function recordWeb(segment, scope, tx) { // in web metrics, scope is required if (!scope) { return } - const tx = segment.transaction // if there was a nested webTransaction use its recorder instead if (tx.type === 'web' && tx.baseSegment && segment !== tx.baseSegment) { return @@ -26,7 +25,7 @@ function recordWeb(segment, scope) { const totalTime = tx.trace.getTotalTimeDurationInMillis() const exclusive = segment.getExclusiveDurationInMillis() const partial = segment.partialName - const config = segment.transaction.agent.config + const config = tx.agent.config // named / key transaction support requires per-name apdexT const keyApdexInMillis = config.web_transactions_apdex[scope] * TO_MILLIS || 0 diff --git a/lib/metrics/recorders/http_external.js b/lib/metrics/recorders/http_external.js index 2e3bd7e2e4..252a97f1f0 100644 --- a/lib/metrics/recorders/http_external.js +++ b/lib/metrics/recorders/http_external.js @@ -8,10 +8,9 @@ const EXTERNAL = require('../../metrics/names').EXTERNAL function recordExternal(host, library) { - return function externalRecorder(segment, scope) { + return function externalRecorder(segment, scope, transaction) { const duration = segment.getDurationInMillis() const exclusive = segment.getExclusiveDurationInMillis() - const transaction = segment.transaction const metricName = EXTERNAL.PREFIX + host + '/' + library const rollupType = transaction.isWeb() ? EXTERNAL.WEB : EXTERNAL.OTHER const rollupHost = EXTERNAL.PREFIX + host + '/all' diff --git a/lib/metrics/recorders/message-transaction.js b/lib/metrics/recorders/message-transaction.js index ed7172d953..c880226b1d 100644 --- a/lib/metrics/recorders/message-transaction.js +++ b/lib/metrics/recorders/message-transaction.js @@ -7,15 +7,14 @@ const NAMES = require('../../metrics/names.js') -function recordMessageTransaction(segment, scope) { - const tx = segment.transaction +function recordMessageTransaction(segment, scope, tx) { if (tx.type !== 'message' || tx.baseSegment !== segment) { return } const duration = segment.getDurationInMillis() const exclusive = segment.getExclusiveDurationInMillis() - const totalTime = segment.transaction.trace.getTotalTimeDurationInMillis() + const totalTime = tx.trace.getTotalTimeDurationInMillis() if (scope) { tx.measure(scope, null, duration, exclusive) diff --git a/lib/metrics/recorders/other.js b/lib/metrics/recorders/other.js index 11d472d394..0637ccaaa9 100644 --- a/lib/metrics/recorders/other.js +++ b/lib/metrics/recorders/other.js @@ -8,16 +8,15 @@ const NAMES = require('../../metrics/names') const recordDistributedTrace = require('./distributed-trace') -function recordBackground(segment, scope) { +function recordBackground(segment, scope, tx) { // if there was a nested otherTransaction use its recorder instead - const tx = segment.transaction if (tx.type === 'bg' && tx.baseSegment && segment !== tx.baseSegment) { return } const duration = segment.getDurationInMillis() const exclusive = segment.getExclusiveDurationInMillis() - const totalTime = segment.transaction.trace.getTotalTimeDurationInMillis() + const totalTime = tx.trace.getTotalTimeDurationInMillis() const name = segment.partialName if (scope) { diff --git a/lib/shim/datastore-shim.js b/lib/shim/datastore-shim.js index 9c1b85bfcc..7875f19320 100644 --- a/lib/shim/datastore-shim.js +++ b/lib/shim/datastore-shim.js @@ -605,10 +605,11 @@ function _getSpec({ spec, shim, fn, fnName, args }) { * @param {ParsedStatement} parsed instance of ParsedStatement * @param {TraceSegment} segment active segment * @param {string} scope scope of metrics if it exists + * @param {Transaction} transaction - The transaction being recorded. */ -function _recordQueryMetrics(parsed, segment, scope) { - if (segment) { - parsed.recordMetrics(segment, scope) +function _recordQueryMetrics(parsed, segment, scope, transaction) { + if (segment && transaction) { + parsed.recordMetrics(segment, scope, transaction) } } @@ -622,17 +623,17 @@ function _recordQueryMetrics(parsed, segment, scope) { * @implements {MetricFunction} * @param {TraceSegment} segment - The segment being recorded. * @param {string} [scope] - The scope of the segment. + * @param {Transaction} transaction - The transaction being recorded. * @see DatastoreShim#recordOperation * @see MetricFunction */ -function _recordOperationMetrics(segment, scope) { - if (!segment) { +function _recordOperationMetrics(segment, scope, transaction) { + if (!(segment || transaction)) { return } const duration = segment.getDurationInMillis() const exclusive = segment.getExclusiveDurationInMillis() - const transaction = segment.transaction const type = transaction.isWeb() ? 'allWeb' : 'allOther' const operation = segment.name diff --git a/lib/shim/message-shim/subscribe-consume.js b/lib/shim/message-shim/subscribe-consume.js index 8a38e805f5..eead64968c 100644 --- a/lib/shim/message-shim/subscribe-consume.js +++ b/lib/shim/message-shim/subscribe-consume.js @@ -151,7 +151,7 @@ function createConsumerWrapper({ shim, spec, consumer }) { return consumer.apply(this, args) } - const msgDesc = spec.messageHandler.call(this, shim, args) + const msgDesc = spec.messageHandler.call(this, shim, args, tx) // If message could not be handled, immediately kill this transaction. if (!msgDesc) { @@ -212,7 +212,7 @@ function createConsumerWrapper({ shim, spec, consumer }) { } } if (msgDesc.headers) { - shim.handleMqTracingHeaders(msgDesc.headers, tx.baseSegment, shim._transportType) + shim.handleMqTracingHeaders(msgDesc.headers, tx.baseSegment, tx, shim._transportType) } shim.logger.trace('Started message transaction %s named %s', tx.id, txName) diff --git a/lib/shim/shim.js b/lib/shim/shim.js index a940ffc280..75a289780f 100644 --- a/lib/shim/shim.js +++ b/lib/shim/shim.js @@ -677,7 +677,10 @@ function recordWrapper({ shim, fn, name, recordNamer }) { let transaction // See if we're in an active transaction. if (segDesc.parent) { - segDesc.parent = segDesc.parent?.transaction?.isActive() && segDesc.parent?.segment ? segDesc.parent.segment : shim.getActiveSegment() + segDesc.parent = + segDesc.parent?.transaction?.isActive() && segDesc.parent?.segment + ? segDesc.parent.segment + : shim.getActiveSegment() transaction = segDesc?.parent?.transaction // We only want to continue recording in a transaction if the // transaction is active. @@ -822,12 +825,12 @@ function _applyRecorderSegment({ transaction, segment, ctx, args, segDesc, shim, function onThen(val) { segment.touch() // passing in error as some instrumentation checks if it's not equal to `null` - segDesc.after({ shim, fn, name, error, result: val, segment }) + segDesc.after({ shim, fn, name, error, result: val, segment, transaction }) return val }, function onCatch(err) { segment.touch() - segDesc.after({ shim, fn, name, error: err, segment }) + segDesc.after({ shim, fn, name, error: err, segment, transaction }) throw err // NOTE: This is not an error from our instrumentation. } ) @@ -838,7 +841,7 @@ function _applyRecorderSegment({ transaction, segment, ctx, args, segDesc, shim, throw err // Just rethrowing this error, not our error! } finally { if (segDesc.after && (error || !promised)) { - segDesc.after({ shim, fn, name, error, result: ret, segment }) + segDesc.after({ shim, fn, name, error, result: ret, segment, transaction }) } } } @@ -970,12 +973,12 @@ function bindSegment(nodule, property, segment, full) { segment = property property = null } - + let transaction if (segment?.segment instanceof TraceSegment) { segment = segment?.segment transaction = segment?.transaction - } + } // This protects against the `bindSegment(func, null, true)` case, where the // segment is `null`, and thus `true` (the full param) is detected as the @@ -997,18 +1000,30 @@ function bindSegment(nodule, property, segment, full) { return func } - let ctx - if (transaction) { - ctx = { transaction, segment } - } else { - ctx = segment - } + const ctx = setContext({ transaction, segment }) const binder = _makeBindWrapper(shim, func, ctx, full || false) shim.storeSegment(binder, segment) return binder }) } +/** + * + * @param root0 + * @param root0.transaction + * @param root0.segment + */ +function setContext({ transaction, segment }) { + let ctx + if (transaction) { + ctx = { transaction, segment } + } else { + ctx = segment + } + + return ctx +} + /** * Replaces the callback in an arguments array with one that has been bound to * the given segment. @@ -1093,7 +1108,7 @@ function wrapCallback({ shim, args, cbIdx, parentSegment, spec }) { } if (spec?.after) { - spec.after({ shim, fn, name, args: arguments, segment: realParent }) + spec.after({ shim, fn, name, args: arguments, segment: realParent, transaction }) } // CB may end the transaction so update the parent's time preemptively. @@ -1141,7 +1156,8 @@ function getSegment(obj) { */ function getActiveSegment(obj) { const segment = this.getSegment(obj) - if (segment && segment.transaction && segment.transaction.isActive()) { + const transaction = this.tracer.getTransaction() + if (transaction?.isActive()) { return segment } return null diff --git a/lib/shim/transaction-shim.js b/lib/shim/transaction-shim.js index 157881e4cd..565d1efbf5 100644 --- a/lib/shim/transaction-shim.js +++ b/lib/shim/transaction-shim.js @@ -195,10 +195,11 @@ function setTransactionName(name) { * @param {TraceSegment} [segment] * The trace segment to associate the header data with. If no segment is * provided then the currently active segment is used. + * @param transaction * @param {string} [transportType] * The transport type that brought the headers. Usually `HTTP` or `HTTPS`. */ -function handleMqTracingHeaders(headers, segment, transportType) { +function handleMqTracingHeaders(headers, segment, transaction, transportType) { // TODO: replace functionality when CAT fully removed. if (!headers) { @@ -215,13 +216,11 @@ function handleMqTracingHeaders(headers, segment, transportType) { // Check that we're in an active transaction. const currentSegment = segment || this.getSegment() - if (!currentSegment || !currentSegment.transaction.isActive()) { + if (!currentSegment || !transaction.isActive()) { this.logger.trace('Not processing headers for CAT or DT, not in an active transaction.') return } - const transaction = currentSegment.transaction - if (config.distributed_tracing.enabled) { transaction.acceptDistributedTraceHeaders(transportType, headers) return @@ -237,7 +236,7 @@ function handleMqTracingHeaders(headers, segment, transportType) { ) cat.assignCatToTransaction(externalId, externalTransaction, transaction) const decodedAppData = cat.parseAppData(config, appData) - cat.assignCatToSegment(decodedAppData, currentSegment) + cat.assignCatToSegment({ appData: decodedAppData, segment: currentSegment, transaction }) // TODO: Handle adding ExternalTransaction metrics for this segment. } @@ -316,11 +315,11 @@ function insertCATReplyHeader(headers, useAlternateHeaderNames) { // Are we in a transaction? const segment = this.getSegment() - if (!segment || !segment.transaction.isActive()) { + const transaction = this.tracer.getTransaction() + if (!segment || !transaction?.isActive()) { this.logger.trace('Not adding CAT reply header, not in an active transaction.') return } - const tx = segment.transaction // Hunt down the content length. // NOTE: In AMQP, content-type and content-encoding are guaranteed fields, but @@ -334,11 +333,16 @@ function insertCATReplyHeader(headers, useAlternateHeaderNames) { } } - const { key, data } = cat.encodeAppData(config, tx, contentLength, useAlternateHeaderNames) + const { key, data } = cat.encodeAppData( + config, + transaction, + contentLength, + useAlternateHeaderNames + ) // Add the header. if (key && data) { headers[key] = data - this.logger.trace('Added outbound response CAT headers for transaction %s', tx.id) + this.logger.trace('Added outbound response CAT headers for transaction %s', transaction.id) } } diff --git a/lib/shim/webframework-shim/middleware.js b/lib/shim/webframework-shim/middleware.js index 2d412cbab5..6d1e99a5d7 100644 --- a/lib/shim/webframework-shim/middleware.js +++ b/lib/shim/webframework-shim/middleware.js @@ -340,10 +340,9 @@ module.exports._recordMiddleware = function _recordMiddleware(shim, middleware, * @returns {Function} recorder for middleware */ function _makeMiddlewareRecorder(_shim, metricName) { - return function middlewareMetricRecorder(segment, scope) { + return function middlewareMetricRecorder(segment, scope, transaction) { const duration = segment.getDurationInMillis() const exclusive = segment.getExclusiveDurationInMillis() - const transaction = segment.transaction if (scope) { transaction.measure(metricName, scope, duration, exclusive) @@ -454,7 +453,7 @@ function wrapNextFn({ shim, txInfo, nextDetails, segment }, nodule, property, is if (isError(shim, err)) { assignError(txInfo, err) } else if (!isFinal && !nextDetails.isErrorWare && nextDetails.appendPath) { - segment.transaction.nameState.popPath(nextDetails.route) + txInfo.transaction.nameState.popPath(nextDetails.route) } // The next call does not signify the end of the segment @@ -472,7 +471,7 @@ function wrapNextFn({ shim, txInfo, nextDetails, segment }, nodule, property, is // more work to do in that scope. return ret.then(function onNextFinish(v) { if (nextDetails.appendPath) { - segment.transaction.nameState.appendPath(nextDetails.route) + txInfo.transaction.nameState.appendPath(nextDetails.route) } txInfo.segmentStack.push(segment) diff --git a/lib/spans/base-span-streamer.js b/lib/spans/base-span-streamer.js index b8ea567442..3f6146d17c 100644 --- a/lib/spans/base-span-streamer.js +++ b/lib/spans/base-span-streamer.js @@ -78,7 +78,7 @@ class BaseSpanStreamer { * span, a drain event handler is setup to continue writing when possible. * * @param {*} data spans or span - * @param {number} [spanLen=1] number of spans sent in a stream(defaults to 1) + * @param {number} [spanLen] number of spans sent in a stream(defaults to 1) */ send(data, spanLen = 1) { // false indicates the stream has reached the highWaterMark diff --git a/lib/spans/span-event-aggregator.js b/lib/spans/span-event-aggregator.js index 5f1fd2e601..32d5f09b36 100644 --- a/lib/spans/span-event-aggregator.js +++ b/lib/spans/span-event-aggregator.js @@ -62,22 +62,21 @@ class SpanEventAggregator extends EventAggregator { * Attempts to add the given segment to the collection. * * @param {TraceSegment} segment - The segment to add. - * @param {string} [parentId=null] - The GUID of the parent span. + * @param {string} [parentId] - The GUID of the parent span. * @param isRoot * @returns {boolean} True if the segment was added, or false if it was discarded. */ - addSegment(segment, parentId, isRoot) { + addSegment({ segment, transaction, parentId, isRoot }) { // Check if the priority would be accepted before creating the event object. - const tx = segment.transaction - if (tx.priority < this._items.getMinimumPriority()) { + if (transaction.priority < this._items.getMinimumPriority()) { ++this.events.seen this._metrics.getOrCreateMetric(this._metricNames.SEEN).incrementCallCount() return false } - const span = SpanEvent.fromSegment(segment, parentId || null, isRoot) - return this.add(span, tx.priority) + const span = SpanEvent.fromSegment(segment, transaction, parentId || null, isRoot) + return this.add(span, transaction.priority) } /** diff --git a/lib/spans/span-event.js b/lib/spans/span-event.js index fcc04064e9..027fc67234 100644 --- a/lib/spans/span-event.js +++ b/lib/spans/span-event.js @@ -93,16 +93,17 @@ class SpanEvent { * category of the segment. * * @param {TraceSegment} segment - The segment to turn into a span event. + * @param transaction * @param {?string} [parentId] - The ID of the segment's parent. * @param isRoot * @returns {SpanEvent} The constructed event. */ - static fromSegment(segment, parentId = null, isRoot = false) { + static fromSegment(segment, transaction, parentId = null, isRoot = false) { const spanContext = segment.getSpanContext() // Since segments already hold span agent attributes and we want to leverage // filtering, we add to the segment attributes prior to processing. - if (spanContext.hasError && !segment.transaction.hasIgnoredErrorStatusCode()) { + if (spanContext.hasError && !transaction.hasIgnoredErrorStatusCode()) { const details = spanContext.errorDetails segment.addSpanAttribute('error.message', details.message) segment.addSpanAttribute('error.class', details.type) @@ -128,25 +129,23 @@ class SpanEvent { span.intrinsics[key] = value } - const tx = segment.transaction - - span.intrinsics.traceId = tx.traceId + span.intrinsics.traceId = transaction.traceId span.intrinsics.guid = segment.id span.intrinsics.parentId = parentId - span.intrinsics.transactionId = tx.id - span.intrinsics.sampled = tx.sampled - span.intrinsics.priority = tx.priority + span.intrinsics.transactionId = transaction.id + span.intrinsics.sampled = transaction.sampled + span.intrinsics.priority = transaction.priority span.intrinsics.name = segment.name if (isRoot) { - span.intrinsics.trustedParentId = tx.traceContext.trustedParentId - if (tx.traceContext.tracingVendors) { - span.intrinsics.tracingVendors = tx.traceContext.tracingVendors + span.intrinsics.trustedParentId = transaction.traceContext.trustedParentId + if (transaction.traceContext.tracingVendors) { + span.intrinsics.tracingVendors = transaction.traceContext.tracingVendors } } // Only set this if it will be `true`. Must be `null` otherwise. - if (tx.baseSegment === segment) { + if (transaction.baseSegment === segment) { span.intrinsics['nr.entryPoint'] = true } diff --git a/lib/spans/streaming-span-event-aggregator.js b/lib/spans/streaming-span-event-aggregator.js index 7c05c6f99c..5b2c7731c8 100644 --- a/lib/spans/streaming-span-event-aggregator.js +++ b/lib/spans/streaming-span-event-aggregator.js @@ -105,13 +105,13 @@ class StreamingSpanEventAggregator extends Aggregator { * @param isRoot * @returns {boolean} True if the segment was added, or false if it was discarded. */ - addSegment(segment, parentId, isRoot) { + addSegment({ segment, transaction, parentId, isRoot }) { if (!this.started) { logger.trace('Aggregator has not yet started, dropping span (%s).', segment.name) return } - const span = StreamingSpanEvent.fromSegment(segment, parentId, isRoot) + const span = StreamingSpanEvent.fromSegment(segment, transaction, parentId, isRoot) this.stream.write(span) } diff --git a/lib/spans/streaming-span-event.js b/lib/spans/streaming-span-event.js index 4d435be4e2..da7432bfef 100644 --- a/lib/spans/streaming-span-event.js +++ b/lib/spans/streaming-span-event.js @@ -114,12 +114,12 @@ class StreamingSpanEvent { } } - static fromSegment(segment, parentId = null, isRoot = false) { + static fromSegment(segment, transaction, parentId = null, isRoot = false) { const spanContext = segment.getSpanContext() // Since segments already hold span agent attributes and we want to leverage // filtering, we add to the segment attributes prior to processing. - if (spanContext.hasError && !segment.transaction.hasIgnoredErrorStatusCode()) { + if (spanContext.hasError && !transaction.hasIgnoredErrorStatusCode()) { const details = spanContext.errorDetails segment.addSpanAttribute('error.message', details.message) segment.addSpanAttribute('error.class', details.type) @@ -132,7 +132,6 @@ class StreamingSpanEvent { const customAttributes = spanContext.customAttributes.get(DESTINATIONS.SPAN_EVENT) - const transaction = segment.transaction const traceId = transaction.traceId let span = null diff --git a/lib/transaction/index.js b/lib/transaction/index.js index 73e80b8d49..b4d2bd8be8 100644 --- a/lib/transaction/index.js +++ b/lib/transaction/index.js @@ -698,7 +698,7 @@ Transaction.prototype.addRecorder = function addRecorder(recorder) { Transaction.prototype.record = function record() { const name = this.name for (let i = 0, l = this._recorders.length; i < l; ++i) { - this._recorders[i](name) + this._recorders[i](name, this) } } diff --git a/lib/transaction/trace/index.js b/lib/transaction/trace/index.js index 83cb05f86e..7b11835a23 100644 --- a/lib/transaction/trace/index.js +++ b/lib/transaction/trace/index.js @@ -115,7 +115,12 @@ Trace.prototype.generateSpanEvents = function generateSpanEvents() { // Even though at some point we might want to stop adding events because all the priorities // should be the same, we need to count the spans as seen. - spanAggregator.addSegment(segment, segmentInfo.parentId, segmentInfo.isRoot) + spanAggregator.addSegment({ + segment, + transaction: this.transaction, + parentId: segmentInfo.parentId, + isRoot: segmentInfo.isRoot + }) const nodes = segment.getChildren() for (let i = 0; i < nodes.length; ++i) { diff --git a/lib/transaction/trace/segment.js b/lib/transaction/trace/segment.js index 0975c34279..16d9d53081 100644 --- a/lib/transaction/trace/segment.js +++ b/lib/transaction/trace/segment.js @@ -402,6 +402,8 @@ TraceSegment.prototype.toJSON = function toJSON() { while (segmentsToProcess.length !== 0) { const { segment, destination } = segmentsToProcess.pop() + // TODO: how can we get the relevant transaction?? + // this is processed i _think_ once the tx ends, maybe we have to pass it through const start = segment.timer.startedRelativeTo(segment.transaction.trace.root.timer) const duration = segment.getDurationInMillis() diff --git a/lib/transaction/tracer/index.js b/lib/transaction/tracer/index.js index a0ba1e96c0..1b0ae441b3 100644 --- a/lib/transaction/tracer/index.js +++ b/lib/transaction/tracer/index.js @@ -24,6 +24,7 @@ function Tracer(agent, contextManager) { this._contextManager = contextManager } +Tracer.prototype.getContext = getContext Tracer.prototype.getTransaction = getTransaction Tracer.prototype.getSegment = getSegment Tracer.prototype.setSegment = setSegment @@ -44,8 +45,12 @@ Tracer.prototype.wrapFunctionFirst = wrapFunctionFirst Tracer.prototype.wrapSyncFunction = wrapSyncFunction Tracer.prototype.wrapCallback = wrapCallback +function getContext() { + return this._contextManager.getContext() +} + function getTransaction() { - const ctx = this._contextManager.getContext() + const ctx = this.getContext() if (ctx?.transaction && ctx?.transaction?.isActive()) { return ctx.transaction } @@ -55,7 +60,7 @@ function getTransaction() { // TODO: Remove/replace external uses to tracer.getSegment() function getSegment() { - const ctx = this._contextManager.getContext() + const ctx = this.getContext() return ctx?.segment || null } @@ -95,9 +100,10 @@ function addSegment(name, recorder, parent, full, task) { } const segment = this.createSegment(name, recorder, parent) + const transaction = this.getTransaction() maybeAddCLMAttributes(task, segment) - return this.bindFunction(task, segment, full)(segment) + return this.bindFunction(task, segment, full)(segment, transaction) } function transactionProxy(handler) { @@ -114,14 +120,15 @@ function transactionProxy(handler) { // don't nest transactions, reuse existing ones const segment = tracer.getSegment() + const currentTx = tracer.getTransaction() if (segment) { - if (segment.transaction.traceStacks) { + if (currentTx.traceStacks) { segment.probe('!!! Nested transaction creation !!!') - segment.transaction.traceFlag = true // Will log the stacks when it ends. + currentTx.traceFlag = true // Will log the stacks when it ends. } logger.warn( { - transaction: { id: segment.transaction.id, name: segment.transaction.getName() }, + transaction: { id: currentTx.id, name: currentTx.getName() }, segment: segment.name }, 'Active transaction when creating non-nested transaction' diff --git a/lib/util/cat.js b/lib/util/cat.js index 78cb15d0cc..f89c6a5ba5 100644 --- a/lib/util/cat.js +++ b/lib/util/cat.js @@ -273,11 +273,15 @@ cat.parseAppData = function parseAppData(config, obfAppData) { * Assigns the CAT id, transaction to segment and adds `transaction_guid` when it exists. * It also renames the segment name based on the newly decoded app data when host is present * + * @param appData.appData * @param {Array} appData decodes CAT app data * @param {TraceSegment} segment * @param {string} [host] if host is present it will rename segment with app data and host + * @param appData.segment + * @param appData.host + * @param appData.transaction */ -cat.assignCatToSegment = function assignCatToSegment(appData, segment, host) { +cat.assignCatToSegment = function assignCatToSegment({ appData, segment, host, transaction }) { if (!Array.isArray(appData) || typeof appData[0] !== 'string') { logger.trace(`Unknown format for CAT header ${HTTP_CAT_APP_DATA_HEADER}.`) return @@ -297,7 +301,7 @@ cat.assignCatToSegment = function assignCatToSegment(appData, segment, host) { } logger.trace( 'Got inbound response CAT headers in transaction %s from %s', - segment.transaction.id, + transaction.id, transactionGuid ) } diff --git a/lib/util/copy.js b/lib/util/copy.js index 0930138f91..9bc8ecd316 100644 --- a/lib/util/copy.js +++ b/lib/util/copy.js @@ -13,7 +13,7 @@ exports.shallow = shallowCopy * Performs a shallow copy of all properties on the source object. * * @param {object} source - The object to copy the properties from. - * @param {object} [dest={}] - The object to copy the properties to. + * @param {object} [dest] - The object to copy the properties to. * @returns {object} The destination object. */ function shallowCopy(source, dest) { diff --git a/test/integration/agent/serverless-harvest.tap.js b/test/integration/agent/serverless-harvest.tap.js index 0e1767f219..402db0a0b3 100644 --- a/test/integration/agent/serverless-harvest.tap.js +++ b/test/integration/agent/serverless-harvest.tap.js @@ -336,7 +336,13 @@ tap.test('Serverless mode harvest', (t) => { const expectedSql = 'select pg_sleep(1)' - agent.queries.add(tx.trace.root, 'postgres', expectedSql, 'FAKE STACK') + agent.queries.add({ + transaction: tx, + segment: tx.trace.root, + type: 'postgres', + query: expectedSql, + trace: 'FAKE STACK' + }) tx.end() agent.once('harvestFinished', () => { diff --git a/test/integration/newrelic-response-handling.tap.js b/test/integration/newrelic-response-handling.tap.js index 2dbc37bc36..c9e358241d 100644 --- a/test/integration/newrelic-response-handling.tap.js +++ b/test/integration/newrelic-response-handling.tap.js @@ -324,7 +324,13 @@ function createTestData(agent, callback) { helper.runInTransaction(agent, (transaction) => { const segment = transaction.trace.add('MySegment') segment.overwriteDurationInMillis(1) - agent.queries.add(segment, 'mysql', 'select * from foo', new Error().stack) + agent.queries.add({ + segment, + transaction, + type: 'mysql', + query: 'select * from foo', + trace: new Error().stack + }) transaction.finalizeNameFromUri('/some/test/url', 200) transaction.end() diff --git a/test/unit/agent/agent.test.js b/test/unit/agent/agent.test.js index 5f52c078cd..342199084e 100644 --- a/test/unit/agent/agent.test.js +++ b/test/unit/agent/agent.test.js @@ -655,7 +655,13 @@ test('when connected', async (t) => { const tx = new helper.FakeTransaction(agent, '/path/to/fake') tx.metrics = { apdexT: 0 } const segment = new helper.FakeSegment(tx, 2_000) - agent.queries.add(segment, 'mysql', 'select * from foo', 'Stack\nFrames') + agent.queries.add({ + transaction: tx, + segment, + type: 'mysql', + query: 'select * from foo', + trace: 'Stack\nFrames' + }) agent.spanEventAggregator.add(segment) agent.transactionEventAggregator.add(tx) agent.customEventAggregator.add({ key: 'value' }) @@ -694,7 +700,13 @@ test('when connected', async (t) => { const tx = new helper.FakeTransaction(agent, '/path/to/fake') tx.metrics = { apdexT: 0 } const segment = new helper.FakeSegment(tx, 2_000) - agent.queries.add(segment, 'mysql', 'select * from foo', 'Stack\nFrames') + agent.queries.add({ + transaction: tx, + segment, + type: 'mysql', + query: 'select * from foo', + trace: 'Stack\nFrames' + }) agent.spanEventAggregator.add(segment) agent.transactionEventAggregator.add(tx) agent.customEventAggregator.add({ key: 'value' }) diff --git a/test/unit/db/query-sample.test.js b/test/unit/db/query-sample.test.js index 93b85793b1..1a522cd8d5 100644 --- a/test/unit/db/query-sample.test.js +++ b/test/unit/db/query-sample.test.js @@ -83,9 +83,8 @@ test('Query Sample', async (t) => { } } const fakeSample = { - segment: { - transaction: {} - } + transaction: {}, + segment: {} } let codecCalled = false @@ -118,13 +117,12 @@ test('Query Sample', async (t) => { let getFullNameCalled = false const fakeSample = { - segment: { - transaction: { - getFullName: () => { - getFullNameCalled = true - } + transaction: { + getFullName: () => { + getFullNameCalled = true } - } + }, + segment: {} } const clock = sinon.useFakeTimers({ @@ -162,11 +160,11 @@ test('Query Sample', async (t) => { const fakeGetAttributes = () => expectedParams const fakeSample = { trace: {}, + transaction: { + addDistributedTraceIntrinsics: () => {} + }, segment: { - getAttributes: fakeGetAttributes, - transaction: { - addDistributedTraceIntrinsics: () => {} - } + getAttributes: fakeGetAttributes } } @@ -190,13 +188,13 @@ test('Query Sample', async (t) => { } const fakeSample = { trace: {}, - segment: { - getAttributes: () => ({}), - transaction: { - addDistributedTraceIntrinsics: () => { - addDtIntrinsicsCalled = true - } + transaction: { + addDistributedTraceIntrinsics: () => { + addDtIntrinsicsCalled = true } + }, + segment: { + getAttributes: () => ({}) } } @@ -218,13 +216,13 @@ test('Query Sample', async (t) => { } const fakeSample = { trace: {}, - segment: { - getAttributes: () => ({}), - transaction: { - addDistributedTraceIntrinsics: () => { - addDtIntrinsicsCalled = true - } + transaction: { + addDistributedTraceIntrinsics: () => { + addDtIntrinsicsCalled = true } + }, + segment: { + getAttributes: () => ({}) } } diff --git a/test/unit/db/query-trace-aggregator.test.js b/test/unit/db/query-trace-aggregator.test.js index e23ef76423..b95e0224ee 100644 --- a/test/unit/db/query-trace-aggregator.test.js +++ b/test/unit/db/query-trace-aggregator.test.js @@ -1095,7 +1095,13 @@ function addQuery(queries, duration, url, query) { const transaction = new FakeTransaction(null, url) const segment = new FakeSegment(transaction, duration) - queries.add(segment, 'mysql', query || 'select * from foo where a=2', FAKE_STACK) + queries.add({ + transaction, + segment, + type: 'mysql', + query: query || 'select * from foo where a=2', + trace: FAKE_STACK + }) return segment } diff --git a/test/unit/db/trace.test.js b/test/unit/db/trace.test.js index 361fa978e4..87e18b7c86 100644 --- a/test/unit/db/trace.test.js +++ b/test/unit/db/trace.test.js @@ -39,7 +39,13 @@ test('SQL trace attributes', async (t) => { const payload = tx._createDistributedTracePayload().text() tx.isDistributedTrace = null tx._acceptDistributedTracePayload(payload) - agent.queries.add(tx.trace.root, 'postgres', 'select pg_sleep(1)', 'FAKE STACK') + agent.queries.add({ + transaction: tx, + segment: tx.trace.root, + type: 'postgres', + query: 'select pg_sleep(1)', + trace: 'FAKE STACK' + }) agent.queries.prepareJSON((err, samples) => { const sample = samples[0] const attributes = sample[sample.length - 1] @@ -62,7 +68,13 @@ test('SQL trace attributes', async (t) => { const { agent } = t.nr helper.runInTransaction(agent, function (tx) { const query = 'select pg_sleep(1)' - agent.queries.add(tx.trace.root, 'postgres', query, 'FAKE STACK') + agent.queries.add({ + transaction: tx, + segment: tx.trace.root, + type: 'postgres', + query, + trace: 'FAKE STACK' + }) const sampleObj = agent.queries.samples.values().next().value const sample = agent.queries.prepareJSONSync()[0] assert.equal(sample[0], tx.getFullName()) @@ -85,7 +97,13 @@ test('SQL trace attributes', async (t) => { agent.config.account_id = 1 agent.config.simple_compression = true helper.runInTransaction(agent, function (tx) { - agent.queries.add(tx.trace.root, 'postgres', 'select pg_sleep(1)', 'FAKE STACK') + agent.queries.add({ + transaction: tx, + segment: tx.trace.root, + type: 'postgres', + query: 'select pg_sleep(1)', + trace: 'FAKE STACK' + }) agent.queries.prepareJSON((err, samples) => { const sample = samples[0] const attributes = sample[sample.length - 1] diff --git a/test/unit/instrumentation/undici.test.js b/test/unit/instrumentation/undici.test.js index 1db52ab1c8..e15b9107ac 100644 --- a/test/unit/instrumentation/undici.test.js +++ b/test/unit/instrumentation/undici.test.js @@ -88,6 +88,7 @@ test('undici instrumentation', async function (t) { } channels.create.publish({ request }) assert.ok(request[symbols.parentSegment]) + assert.ok(request[symbols.transaction]) assert.equal(request.addHeader.callCount, 2) assert.deepEqual(request.addHeader.args[0], ['x-newrelic-synthetics', 'synthHeader']) assert.deepEqual(request.addHeader.args[1], [ @@ -146,6 +147,11 @@ test('undici instrumentation', async function (t) { request2[symbols.parentSegment].id, 'parent segment should be same' ) + assert.equal( + request[symbols.transaction].id, + request2[symbols.transaction].id, + 'tx should be same' + ) tx.end() end() }) @@ -165,6 +171,7 @@ test('undici instrumentation', async function (t) { const request2 = { path: '/request2', addHeader: sandbox.stub(), origin: HOST } channels.create.publish({ request: request2 }) assert.notEqual(request[symbols.parentSegment], request2[symbols.parentSegment]) + assert.equal(request[symbols.transaction], request2[symbols.transaction]) tx.end() end() }) @@ -190,6 +197,11 @@ test('undici instrumentation', async function (t) { request2[symbols.parentSegment].name, 'parent segment should not be same' ) + assert.equal( + request[symbols.transaction].id, + request2[symbols.transaction].id, + 'tx should be the same' + ) tx.end() end() }) @@ -365,7 +377,8 @@ test('undici instrumentation', async function (t) { shim.setActiveSegment(segment) const request = { [symbols.parentSegment]: parentSegment, - [symbols.segment]: segment + [symbols.segment]: segment, + [symbols.transaction]: tx } channels.send.publish({ request }) assert.equal(segment.timer.state, 3, 'previous active segment timer should be stopped') @@ -388,7 +401,8 @@ test('undici instrumentation', async function (t) { const error = new Error('request failed') const request = { [symbols.parentSegment]: parentSegment, - [symbols.segment]: segment + [symbols.segment]: segment, + [symbols.transaction]: tx } channels.error.publish({ request, error }) assert.equal(segment.timer.state, 3, 'previous active segment timer should be stopped') diff --git a/test/unit/llm-events/aws-bedrock/chat-completion-message.test.js b/test/unit/llm-events/aws-bedrock/chat-completion-message.test.js index 4e484e84e6..1b9a94cd98 100644 --- a/test/unit/llm-events/aws-bedrock/chat-completion-message.test.js +++ b/test/unit/llm-events/aws-bedrock/chat-completion-message.test.js @@ -49,12 +49,12 @@ test.beforeEach((ctx) => { ctx.nr.content = 'a prompt' + ctx.nr.transaction = { + id: 'tx-1', + traceId: 'trace-1' + } ctx.nr.segment = { - id: 'segment-1', - transaction: { - id: 'tx-1', - traceId: 'trace-1' - } + id: 'segment-1' } ctx.nr.bedrockResponse = { diff --git a/test/unit/llm-events/aws-bedrock/chat-completion-summary.test.js b/test/unit/llm-events/aws-bedrock/chat-completion-summary.test.js index 0bc79f281f..0df281f198 100644 --- a/test/unit/llm-events/aws-bedrock/chat-completion-summary.test.js +++ b/test/unit/llm-events/aws-bedrock/chat-completion-summary.test.js @@ -38,10 +38,10 @@ test.beforeEach((ctx) => { } } + ctx.nr.transaction = { + id: 'tx-1' + } ctx.nr.segment = { - transaction: { - id: 'tx-1' - }, getDurationInMillis() { return 100 } diff --git a/test/unit/llm-events/aws-bedrock/embedding.test.js b/test/unit/llm-events/aws-bedrock/embedding.test.js index 801e7f64a4..e2d2d8d4cf 100644 --- a/test/unit/llm-events/aws-bedrock/embedding.test.js +++ b/test/unit/llm-events/aws-bedrock/embedding.test.js @@ -54,8 +54,10 @@ test.beforeEach((ctx) => { 'x-amzn-requestid': 'request-1' } } + ctx.nr.transaction = { + traceId: 'id' + } ctx.nr.segment = { - transaction: { traceId: 'id' }, getDurationInMillis() { return 1.008 } diff --git a/test/unit/llm-events/aws-bedrock/event.test.js b/test/unit/llm-events/aws-bedrock/event.test.js index 1d062b0bee..a613a0f18b 100644 --- a/test/unit/llm-events/aws-bedrock/event.test.js +++ b/test/unit/llm-events/aws-bedrock/event.test.js @@ -39,11 +39,11 @@ test.beforeEach((ctx) => { } } + ctx.nr.transaction = { + traceId: 'trace-1' + } ctx.nr.segment = { - id: 'segment-1', - transaction: { - traceId: 'trace-1' - } + id: 'segment-1' } ctx.nr.bedrockResponse = { diff --git a/test/unit/metrics-recorder/generic.test.js b/test/unit/metrics-recorder/generic.test.js index 35d9e5d687..50af5bcbe7 100644 --- a/test/unit/metrics-recorder/generic.test.js +++ b/test/unit/metrics-recorder/generic.test.js @@ -27,7 +27,7 @@ function record(options) { const transaction = options.transaction transaction.finalizeNameFromUri(options.url, options.code) - recordGeneric(segment, options.transaction.name) + recordGeneric(segment, options.transaction.name, options.transaction) } test('recordGeneric', async function (t) { @@ -50,7 +50,7 @@ test('recordGeneric', async function (t) { exclusive: 0 }) assert.doesNotThrow(function () { - recordGeneric(segment, undefined) + recordGeneric(segment, undefined, trans) }) }) @@ -61,7 +61,7 @@ test('recordGeneric', async function (t) { duration: 5, exclusive: 5 }) - recordGeneric(segment, undefined) + recordGeneric(segment, undefined, trans) const result = [[{ name: 'placeholder' }, [1, 0.005, 0.005, 0.005, 0.005, 0.000025]]] diff --git a/test/unit/metrics-recorder/http-external.test.js b/test/unit/metrics-recorder/http-external.test.js index 8ccdcc52ee..e5f4c7059f 100644 --- a/test/unit/metrics-recorder/http-external.test.js +++ b/test/unit/metrics-recorder/http-external.test.js @@ -10,8 +10,8 @@ const helper = require('../../lib/agent_helper') const generateRecorder = require('../../../lib/metrics/recorders/http_external') const Transaction = require('../../../lib/transaction') -function recordExternal(segment, scope) { - return generateRecorder('test.example.com', 'http')(segment, scope) +function recordExternal(segment, scope, transaction) { + return generateRecorder('test.example.com', 'http')(segment, scope, transaction) } function makeSegment(options) { @@ -31,7 +31,7 @@ function record(options) { const transaction = options.transaction transaction.finalizeNameFromUri(options.url, options.code) - recordExternal(segment, options.transaction.name) + recordExternal(segment, options.transaction.name, options.transaction) } test('recordExternal', async function (t) { @@ -56,7 +56,7 @@ test('recordExternal', async function (t) { exclusive: 0 }) assert.doesNotThrow(function () { - recordExternal(segment, undefined) + recordExternal(segment, undefined, trans) }) }) @@ -67,7 +67,7 @@ test('recordExternal', async function (t) { duration: 0, exclusive: 0 }) - recordExternal(segment, undefined) + recordExternal(segment, undefined, trans) const result = [ [{ name: 'External/test.example.com/http' }, [1, 0, 0, 0, 0, 0]], diff --git a/test/unit/metrics-recorder/http.test.js b/test/unit/metrics-recorder/http.test.js index afacf786ed..890fb2e45f 100644 --- a/test/unit/metrics-recorder/http.test.js +++ b/test/unit/metrics-recorder/http.test.js @@ -29,7 +29,7 @@ function record(options) { transaction.finalizeNameFromUri(options.url, options.code) segment.markAsWeb(options.url) - recordWeb(segment, options.transaction.name) + recordWeb(segment, options.transaction.name, options.transaction) } function beforeEach(ctx) { diff --git a/test/unit/metrics-recorder/queue-time-http.test.js b/test/unit/metrics-recorder/queue-time-http.test.js index adb9aa16e8..07647353f5 100644 --- a/test/unit/metrics-recorder/queue-time-http.test.js +++ b/test/unit/metrics-recorder/queue-time-http.test.js @@ -30,7 +30,7 @@ function record(options) { transaction.finalizeNameFromUri(options.url, options.code) transaction.queueTime = options.queueTime segment.markAsWeb(options.url) - recordWeb(segment, options.transaction.name) + recordWeb(segment, options.transaction.name, options.transaction) } test('when recording queueTime', async (t) => { diff --git a/test/unit/parsed-statement.test.js b/test/unit/parsed-statement.test.js index 33b9569699..379e44622d 100644 --- a/test/unit/parsed-statement.test.js +++ b/test/unit/parsed-statement.test.js @@ -32,7 +32,7 @@ test('recording database metrics', async (t) => { transaction.type = Transaction.TYPES.BG segment.setDurationInMillis(333) - ps.recordMetrics(segment, 'TEST') + ps.recordMetrics(segment, 'TEST', transaction) transaction.end() ctx.nr.metrics = transaction.metrics @@ -100,7 +100,7 @@ test('recording database metrics', async (t) => { transaction.type = Transaction.TYPES.BG segment.setDurationInMillis(333) - ps.recordMetrics(segment, 'TEST') + ps.recordMetrics(segment, 'TEST', transaction) transaction.end() ctx.nr.metrics = transaction.metrics @@ -165,7 +165,7 @@ test('recording database metrics', async (t) => { transaction.type = Transaction.TYPES.BG segment.setDurationInMillis(333) - ps.recordMetrics(segment, null) + ps.recordMetrics(segment, null, transaction) transaction.end() ctx.nr.metrics = transaction.metrics @@ -228,7 +228,7 @@ test('recording database metrics', async (t) => { transaction.type = Transaction.TYPES.BG segment.setDurationInMillis(333) - ps.recordMetrics(segment, null) + ps.recordMetrics(segment, null, transaction) transaction.end() ctx.nr.metrics = transaction.metrics @@ -297,13 +297,13 @@ test('recording slow queries', async (t) => { ctx.nr.segment = segment segment.setDurationInMillis(503) - ps.recordMetrics(segment, 'TEST') + ps.recordMetrics(segment, 'TEST', transaction) const ps2 = new ParsedStatement('MySql', 'select', 'foo', 'select * from foo where b=2') const segment2 = transaction.trace.add('test') segment2.setDurationInMillis(501) - ps2.recordMetrics(segment2, 'TEST') + ps2.recordMetrics(segment2, 'TEST', transaction) transaction.end() }) @@ -358,13 +358,13 @@ test('recording slow queries', async (t) => { ctx.nr.segment = segment segment.setDurationInMillis(503) - ps.recordMetrics(segment, 'TEST') + ps.recordMetrics(segment, 'TEST', transaction) const ps2 = new ParsedStatement('MySql', 'select', null, 'select * from foo where b=2') const segment2 = transaction.trace.add('test') segment2.setDurationInMillis(501) - ps2.recordMetrics(segment2, 'TEST') + ps2.recordMetrics(segment2, 'TEST', transaction) transaction.end() }) @@ -427,13 +427,13 @@ test('recording slow queries', async (t) => { ctx.nr.segment = segment segment.setDurationInMillis(503) - ps.recordMetrics(segment, 'TEST') + ps.recordMetrics(segment, 'TEST', transaction) const ps2 = new ParsedStatement('MySql', 'select', null, null) const segment2 = transaction.trace.add('test') segment2.setDurationInMillis(501) - ps2.recordMetrics(segment2, 'TEST') + ps2.recordMetrics(segment2, 'TEST', transaction) transaction.end() }) diff --git a/test/unit/shim/shim.test.js b/test/unit/shim/shim.test.js index 57b0150126..b87c69999d 100644 --- a/test/unit/shim/shim.test.js +++ b/test/unit/shim/shim.test.js @@ -937,7 +937,7 @@ test('Shim', async function (t) { 'should call after hook on record when function is done executing', function (t, end) { const { agent, shim } = t.nr - helper.runInTransaction(agent, function () { + helper.runInTransaction(agent, function (tx) { function testAfter() { return 'result' } @@ -946,13 +946,14 @@ test('Shim', async function (t) { name: 'test segment', callback: shim.LAST, after(args) { - assert.equal(Object.keys(args).length, 6, 'should have 6 args to after hook') - const { fn, name, error, result, segment } = args + assert.equal(Object.keys(args).length, 7, 'should have 6 args to after hook') + const { fn, name, error, result, segment, transaction } = args assert.equal(segment.name, 'test segment') assert.equal(error, undefined) assert.deepEqual(fn, testAfter) assert.equal(name, testAfter.name) assert.equal(result, 'result') + assert.equal(tx.id, transaction.id) } }) }) @@ -969,7 +970,7 @@ test('Shim', async function (t) { function (t, end) { const { agent, shim } = t.nr const err = new Error('test err') - helper.runInTransaction(agent, function () { + helper.runInTransaction(agent, function (tx) { function testAfter() { throw err } @@ -978,13 +979,14 @@ test('Shim', async function (t) { name: 'test segment', callback: shim.LAST, after(args) { - assert.equal(Object.keys(args).length, 6, 'should have 6 args to after hook') - const { fn, name, error, result, segment } = args + assert.equal(Object.keys(args).length, 7, 'should have 7 args to after hook') + const { fn, name, error, result, segment, transaction } = args assert.equal(segment.name, 'test segment') assert.deepEqual(error, err) assert.equal(result, undefined) assert.deepEqual(fn, testAfter) assert.equal(name, testAfter.name) + assert.equal(tx.id, transaction.id) } }) }) @@ -1322,7 +1324,7 @@ test('Shim', async function (t) { name: segmentName, promise: true, after(args) { - plan.equal(Object.keys(args).length, 6, 'should have 6 args to after hook') + plan.equal(Object.keys(args).length, 7, 'should have 6 args to after hook') const { fn, name, error, result, segment } = args plan.deepEqual(fn, toWrap) plan.equal(name, toWrap.name) @@ -1355,7 +1357,7 @@ test('Shim', async function (t) { name: segmentName, promise: true, after(args) { - plan.equal(Object.keys(args).length, 5, 'should have 6 args to after hook') + plan.equal(Object.keys(args).length, 6, 'should have 6 args to after hook') const { fn, name, error, segment } = args plan.deepEqual(fn, toWrap) plan.equal(name, toWrap.name) @@ -1918,22 +1920,22 @@ test('Shim', async function (t) { await t.test('#getActiveSegment', async function (t) { t.beforeEach(function (ctx) { beforeEach(ctx) - ctx.nr.segment = { - probe: function () {}, - transaction: { - active: true, - isActive: function () { - return this.active - } + ctx.nr.transaction = { + agent: {}, + active: true, + isActive: function () { + return this.active } } + ctx.nr.segment = new TraceSegment(ctx.nr.transaction, 'test') }) t.afterEach(afterEach) await t.test( 'should return the segment a function is bound to when transaction is active', function (t) { - const { segment, shim } = t.nr + const { segment, shim, transaction, tracer } = t.nr + tracer.setSegment({ transaction }) const bound = shim.bindSegment(function () {}, segment) assert.equal(shim.getActiveSegment(bound), segment) } @@ -1942,8 +1944,8 @@ test('Shim', async function (t) { await t.test( 'should return the current segment if the function is not bound when transaction is active', function (t) { - const { tracer, segment, shim } = t.nr - tracer.setSegment({ segment }) + const { segment, shim, tracer, transaction } = t.nr + tracer.setSegment({ segment, transaction }) assert.equal( shim.getActiveSegment(function () {}), segment @@ -1954,8 +1956,8 @@ test('Shim', async function (t) { await t.test( 'should return the current segment if no object is provided when transaction is active', function (t) { - const { tracer, segment, shim } = t.nr - tracer.setSegment({ segment }) + const { segment, shim, tracer, transaction } = t.nr + tracer.setSegment({ segment, transaction }) assert.equal(shim.getActiveSegment(), segment) } ) @@ -1963,9 +1965,10 @@ test('Shim', async function (t) { await t.test( 'should return null for a bound function when transaction is not active', function (t) { - const { segment, shim } = t.nr - segment.transaction.active = false - const bound = shim.bindSegment(function () {}, segment) + const { segment, shim, transaction, tracer } = t.nr + transaction.active = false + tracer.setSegment({ transaction }) + const bound = shim.bindSegment(function () {}, { segment }) assert.equal(shim.getActiveSegment(bound), null) } ) @@ -1973,9 +1976,9 @@ test('Shim', async function (t) { await t.test( 'should return null if the function is not bound when transaction is not active', function (t) { - const { tracer, segment, shim } = t.nr - segment.transaction.active = false - tracer.setSegment({ segment }) + const { tracer, segment, shim, transaction } = t.nr + transaction.active = false + tracer.setSegment({ segment, transaction }) assert.equal( shim.getActiveSegment(function () {}), null @@ -1986,9 +1989,9 @@ test('Shim', async function (t) { await t.test( 'should return null if no object is provided when transaction is not active', function (t) { - const { tracer, segment, shim } = t.nr - segment.transaction.active = false - tracer.setSegment({ segment }) + const { tracer, segment, shim, transaction } = t.nr + transaction.active = false + tracer.setSegment({ segment, transaction }) assert.equal(shim.getActiveSegment(), null) } ) @@ -2228,7 +2231,7 @@ test('Shim', async function (t) { const { segment, shim, wrappable } = t.nr shim.applySegment(wrappable.getActiveSegment, segment, true) assert.equal(segment.timer.touched, true) - assert.equal(segment.timer.state, 2) + assert.equal(segment.timer.state, 2) }) await t.test('should not change the active segment if `segment` is `null`', function (t) { diff --git a/test/unit/shim/transaction-shim.test.js b/test/unit/shim/transaction-shim.test.js index 06766ff21e..be55f5bbd1 100644 --- a/test/unit/shim/transaction-shim.test.js +++ b/test/unit/shim/transaction-shim.test.js @@ -441,7 +441,7 @@ test('TransactionShim', async function (t) { assert.ok(!segment.catTransaction) assert.ok(!segment.getAttributes().transaction_guid) - shim.handleMqTracingHeaders(headers, segment) + shim.handleMqTracingHeaders(headers, segment, tx) assert.ok(!tx.incomingCatId) assert.ok(!tx.referringTransactionGuid) @@ -465,7 +465,7 @@ test('TransactionShim', async function (t) { assert.ok(!segment.catTransaction) assert.ok(!segment.getAttributes().transaction_guid) - shim.handleMqTracingHeaders(headers, segment) + shim.handleMqTracingHeaders(headers, segment, tx) assert.ok(!tx.incomingCatId) assert.ok(!tx.referringTransactionGuid) @@ -488,7 +488,7 @@ test('TransactionShim', async function (t) { assert.ok(!segment.getAttributes().transaction_guid) assert.doesNotThrow(function () { - shim.handleMqTracingHeaders(null, segment) + shim.handleMqTracingHeaders(null, segment, tx) }) assert.ok(!tx.incomingCatId) @@ -516,7 +516,7 @@ test('TransactionShim', async function (t) { helper.runInTransaction(agent, shim.BG, function (tx2) { assert.notEqual(tx2, tx) - shim.handleMqTracingHeaders(headers, segment) + shim.handleMqTracingHeaders(headers, segment, tx) }) assert.equal(tx.incomingCatId, '9876#id') @@ -541,7 +541,7 @@ test('TransactionShim', async function (t) { assert.ok(!tx.tripId) assert.ok(!tx.referringPathHash) - shim.handleMqTracingHeaders(headers) + shim.handleMqTracingHeaders(headers, null, tx) assert.equal(tx.incomingCatId, '9876#id') assert.equal(tx.referringTransactionGuid, 'trans id') @@ -568,7 +568,7 @@ test('TransactionShim', async function (t) { helper.runInTransaction(agent, shim.BG, function (tx2) { assert.notEqual(tx2, tx) - shim.handleMqTracingHeaders(headers, segment) + shim.handleMqTracingHeaders(headers, segment, tx) }) assert.equal(tx.incomingCatId, '9876#id') @@ -592,7 +592,7 @@ test('TransactionShim', async function (t) { helper.runInTransaction(agent, function (tx) { const headers = { traceparent, tracestate } const segment = shim.getSegment() - shim.handleMqTracingHeaders(headers, segment) + shim.handleMqTracingHeaders(headers, segment, tx) const outboundHeaders = {} tx.insertDistributedTraceHeaders(outboundHeaders) @@ -615,7 +615,7 @@ test('TransactionShim', async function (t) { helper.runInTransaction(agent, function (tx) { const headers = { traceparent } const segment = shim.getSegment() - shim.handleMqTracingHeaders(headers, segment) + shim.handleMqTracingHeaders(headers, segment, tx) const outboundHeaders = {} tx.insertDistributedTraceHeaders(outboundHeaders) @@ -638,7 +638,7 @@ test('TransactionShim', async function (t) { helper.runInTransaction(agent, function (tx) { const headers = { traceparent, tracestate } const segment = shim.getSegment() - shim.handleMqTracingHeaders(headers, segment) + shim.handleMqTracingHeaders(headers, segment, tx) const outboundHeaders = {} tx.insertDistributedTraceHeaders(outboundHeaders) @@ -660,7 +660,7 @@ test('TransactionShim', async function (t) { helper.runInTransaction(agent, function (tx) { const headers = { traceparent, tracestate } const segment = shim.getSegment() - shim.handleMqTracingHeaders(headers, segment) + shim.handleMqTracingHeaders(headers, segment, tx) const outboundHeaders = {} tx.insertDistributedTraceHeaders(outboundHeaders) @@ -687,7 +687,7 @@ test('TransactionShim', async function (t) { helper.runInTransaction(agent, shim.BG, function (tx2) { assert.notEqual(tx2, tx) - shim.handleMqTracingHeaders(headers, segment) + shim.handleMqTracingHeaders(headers, segment, tx2) }) assert.equal(segment.catId, '6789#app') @@ -702,7 +702,7 @@ test('TransactionShim', async function (t) { 'should attach the CAT info to current segment if not provided - DT disabled, app data is provided', function (t, end) { const { agent, shim } = t.nr - helper.runInTransaction(agent, function () { + helper.runInTransaction(agent, function (tx) { const headers = createCATHeaders(agent.config) const segment = shim.getSegment() delete headers['X-NewRelic-Id'] @@ -712,7 +712,7 @@ test('TransactionShim', async function (t) { assert.ok(!segment.catTransaction) assert.ok(!segment.getAttributes().transaction_guid) - shim.handleMqTracingHeaders(headers) + shim.handleMqTracingHeaders(headers, null, tx) assert.equal(segment.catId, '6789#app') assert.equal(segment.catTransaction, 'app data transaction name') @@ -738,7 +738,7 @@ test('TransactionShim', async function (t) { helper.runInTransaction(agent, shim.BG, function (tx2) { assert.notEqual(tx2, tx) - shim.handleMqTracingHeaders(headers, segment) + shim.handleMqTracingHeaders(headers, segment, tx2) }) assert.equal(segment.catId, '6789#app') @@ -753,7 +753,7 @@ test('TransactionShim', async function (t) { 'should not attach any CAT data to the segment, app data is for an untrusted application', function (t, end) { const { agent, shim } = t.nr - helper.runInTransaction(agent, function () { + helper.runInTransaction(agent, function (tx) { const headers = createCATHeaders(agent.config) const segment = shim.getSegment() delete headers['X-NewRelic-Id'] @@ -764,7 +764,7 @@ test('TransactionShim', async function (t) { assert.ok(!segment.catTransaction) assert.ok(!segment.getAttributes().transaction_guid) - shim.handleMqTracingHeaders(headers) + shim.handleMqTracingHeaders(headers, null, tx) assert.ok(!segment.catId) assert.ok(!segment.catTransaction) diff --git a/test/unit/spans/span-event-aggregator.test.js b/test/unit/spans/span-event-aggregator.test.js index 6d5da42061..c506739326 100644 --- a/test/unit/spans/span-event-aggregator.test.js +++ b/test/unit/spans/span-event-aggregator.test.js @@ -60,7 +60,7 @@ test('SpanAggregator', async (t) => { assert.equal(spanEventAggregator.length, 0) - spanEventAggregator.addSegment(segment, 'p') + spanEventAggregator.addSegment({ segment, transaction: tx, parentId: 'p' }) assert.equal(spanEventAggregator.length, 1) const event = spanEventAggregator.getEvents()[0] @@ -84,7 +84,7 @@ test('SpanAggregator', async (t) => { const segment = agent.tracer.getSegment() assert.equal(spanEventAggregator.length, 0) - spanEventAggregator.addSegment(segment) + spanEventAggregator.addSegment({ segment, transaction: tx }) assert.equal(spanEventAggregator.length, 1) const event = spanEventAggregator.getEvents()[0] @@ -134,20 +134,20 @@ test('SpanAggregator', async (t) => { assert.equal(spanEventAggregator.seen, 0) // First segment is added regardless of priority. - assert.equal(spanEventAggregator.addSegment(segment), true) + assert.equal(spanEventAggregator.addSegment({ segment, transaction: tx }), true) assert.equal(spanEventAggregator.length, 1) assert.equal(spanEventAggregator.seen, 1) // Higher priority should be added. tx.priority = 100 - assert.equal(spanEventAggregator.addSegment(segment), true) + assert.equal(spanEventAggregator.addSegment({ segment, transaction: tx }), true) assert.equal(spanEventAggregator.length, 1) assert.equal(spanEventAggregator.seen, 2) const event1 = spanEventAggregator.getEvents()[0] // Lower priority should not be added. tx.priority = 1 - assert.equal(spanEventAggregator.addSegment(segment), false) + assert.equal(spanEventAggregator.addSegment({ segment, transaction: tx }), false) assert.equal(spanEventAggregator.length, 1) assert.equal(spanEventAggregator.seen, 3) const event2 = spanEventAggregator.getEvents()[0] @@ -173,7 +173,7 @@ test('SpanAggregator', async (t) => { setTimeout(() => { const segment = agent.tracer.getSegment() - spanEventAggregator.addSegment(segment) + spanEventAggregator.addSegment({ segment, transaction: tx }) const payload = spanEventAggregator._toPayloadSync() diff --git a/test/unit/spans/span-event.test.js b/test/unit/spans/span-event.test.js index 2629bf42e8..81e1cfa1ca 100644 --- a/test/unit/spans/span-event.test.js +++ b/test/unit/spans/span-event.test.js @@ -70,7 +70,7 @@ test('fromSegment()', async (t) => { const spanContext = segment.getSpanContext() spanContext.addCustomAttribute('Span Lee', 'no prize') - const span = SpanEvent.fromSegment(segment, 'parent') + const span = SpanEvent.fromSegment(segment, transaction, 'parent') // Should have all the normal properties. assert.ok(span) @@ -136,7 +136,7 @@ test('fromSegment()', async (t) => { res.resume() res.on('end', () => { const segment = agent.tracer.getTransaction().trace.root.children[0] - const span = SpanEvent.fromSegment(segment, 'parent') + const span = SpanEvent.fromSegment(segment, transaction, 'parent') // Should have all the normal properties. assert.ok(span) @@ -238,7 +238,7 @@ test('fromSegment()', async (t) => { dsConn.myDbOp(longQuery, () => { transaction.end() const segment = transaction.trace.root.children[0] - const span = SpanEvent.fromSegment(segment, 'parent') + const span = SpanEvent.fromSegment(segment, transaction, 'parent') // Should have all the normal properties. assert.ok(span) @@ -303,7 +303,7 @@ test('fromSegment()', async (t) => { setTimeout(() => { const segment = agent.tracer.getSegment() - const span = SpanEvent.fromSegment(segment, 'parent') + const span = SpanEvent.fromSegment(segment, transaction, 'parent') const serializedSpan = span.toJSON() const [intrinsics] = serializedSpan @@ -336,7 +336,7 @@ test('fromSegment()', async (t) => { spanContext.addIntrinsicAttribute('intrinsic.1', 1) spanContext.addIntrinsicAttribute('intrinsic.2', 2) - const span = SpanEvent.fromSegment(segment, 'parent') + const span = SpanEvent.fromSegment(segment, transaction, 'parent') const serializedSpan = span.toJSON() const [intrinsics] = serializedSpan @@ -360,7 +360,7 @@ test('fromSegment()', async (t) => { const segment = transaction.trace.root.children[0] assert.ok(segment.name.startsWith('Truncated')) - const span = SpanEvent.fromSegment(segment) + const span = SpanEvent.fromSegment(segment, transaction) assert.ok(span) assert.ok(span instanceof SpanEvent) assert.ok(span instanceof SpanEvent.HttpSpanEvent) @@ -379,7 +379,7 @@ test('fromSegment()', async (t) => { assert.ok(segment.name.startsWith('Truncated')) - const span = SpanEvent.fromSegment(segment) + const span = SpanEvent.fromSegment(segment, transaction) assert.ok(span) assert.ok(span instanceof SpanEvent) assert.ok(span instanceof SpanEvent.DatastoreSpanEvent) diff --git a/test/unit/spans/streaming-span-event.test.js b/test/unit/spans/streaming-span-event.test.js index 4c63ce144c..096aaba9f7 100644 --- a/test/unit/spans/streaming-span-event.test.js +++ b/test/unit/spans/streaming-span-event.test.js @@ -66,7 +66,7 @@ test('fromSegment()', async (t) => { segment.addSpanAttribute('host', 'my-host') segment.addSpanAttribute('port', 22) - const span = StreamingSpanEvent.fromSegment(segment, 'parent') + const span = StreamingSpanEvent.fromSegment(segment, transaction, 'parent') // Should have all the normal properties. assert.ok(span) @@ -131,7 +131,7 @@ test('fromSegment()', async (t) => { res.resume() res.on('end', () => { const segment = agent.tracer.getTransaction().trace.root.children[0] - const span = StreamingSpanEvent.fromSegment(segment, 'parent') + const span = StreamingSpanEvent.fromSegment(segment, transaction, 'parent') // Should have all the normal properties. assert.ok(span) @@ -238,7 +238,7 @@ test('fromSegment()', async (t) => { dsConn.myDbOp(longQuery, () => { transaction.end() const segment = transaction.trace.root.children[0] - const span = StreamingSpanEvent.fromSegment(segment, 'parent') + const span = StreamingSpanEvent.fromSegment(segment, transaction, 'parent') // Should have all the normal properties. assert.ok(span) @@ -329,7 +329,7 @@ test('fromSegment()', async (t) => { const spanContext = agent.tracer.getSpanContext() spanContext.addCustomAttribute('customKey', 'customValue') - const span = StreamingSpanEvent.fromSegment(segment, 'parent') + const span = StreamingSpanEvent.fromSegment(segment, transaction, 'parent') const serializedSpan = span.toStreamingFormat() const { @@ -366,7 +366,7 @@ test('fromSegment()', async (t) => { spanContext.addIntrinsicAttribute('intrinsic.1', 1) spanContext.addIntrinsicAttribute('intrinsic.2', 2) - const span = StreamingSpanEvent.fromSegment(segment, 'parent') + const span = StreamingSpanEvent.fromSegment(segment, transaction, 'parent') const serializedSpan = span.toStreamingFormat() const { intrinsics } = serializedSpan @@ -390,7 +390,7 @@ test('fromSegment()', async (t) => { const segment = transaction.trace.root.children[0] assert.ok(segment.name.startsWith('Truncated')) - const span = StreamingSpanEvent.fromSegment(segment) + const span = StreamingSpanEvent.fromSegment(segment, transaction) assert.ok(span) assert.ok(span instanceof StreamingSpanEvent) @@ -412,7 +412,7 @@ test('fromSegment()', async (t) => { assert.ok(segment.name.startsWith('Truncated')) - const span = StreamingSpanEvent.fromSegment(segment) + const span = StreamingSpanEvent.fromSegment(segment, transaction) assert.ok(span) assert.ok(span instanceof StreamingSpanEvent) diff --git a/test/unit/transaction.test.js b/test/unit/transaction.test.js index 2a78fb1e4c..7078dcb749 100644 --- a/test/unit/transaction.test.js +++ b/test/unit/transaction.test.js @@ -1485,7 +1485,7 @@ test('insertDistributedTraceHeaders', async (t) => { const txn = new Transaction(agent) const lowercaseHexRegex = /^[a-f0-9]+/ - tracer.setSegment({ transaction: txn, segment: txn.trace.root }) + tracer.setSegment({ transaction: txn, segment: txn.trace.root }) const outboundHeaders = createHeadersAndInsertTrace(txn) const traceparent = outboundHeaders.traceparent @@ -1504,7 +1504,7 @@ test('insertDistributedTraceHeaders', async (t) => { const txn = new Transaction(agent) - tracer.setSegment({ transaction: txn, segment: txn.trace.root }) + tracer.setSegment({ transaction: txn, segment: txn.trace.root }) txn.sampled = true const outboundHeaders = createHeadersAndInsertTrace(txn) @@ -1526,7 +1526,7 @@ test('insertDistributedTraceHeaders', async (t) => { txn.acceptTraceContextPayload(traceparent, tracestate) - tracer.setSegment({ transaction: txn, segment: txn.trace.root }) + tracer.setSegment({ transaction: txn, segment: txn.trace.root }) const outboundHeaders = createHeadersAndInsertTrace(txn) const traceparentParts = outboundHeaders.traceparent.split('-')