diff --git a/packages/datadog-plugin-next/src/index.js b/packages/datadog-plugin-next/src/index.js index d1e80f658df..3d6bba7d512 100644 --- a/packages/datadog-plugin-next/src/index.js +++ b/packages/datadog-plugin-next/src/index.js @@ -10,10 +10,10 @@ const errorPages = new Set(['/404', '/500', '/_error', '/_not-found', '/_not-fou class NextPlugin extends ServerPlugin { static id = 'next' + #requestsBySpanId = new WeakMap() constructor (...args) { super(...args) - this._requests = new WeakMap() this.addSub('apm:next:page:load', message => this.pageLoad(message)) } @@ -35,7 +35,9 @@ class NextPlugin extends ServerPlugin { analyticsSampler.sample(span, this.config.measured, true) - this._requests.set(span, req) + // Store request by span ID to handle cases where child spans are activated + const spanId = span.context()._spanId + this.#requestsBySpanId.set(spanId, req) return { ...store, span } } @@ -89,7 +91,13 @@ class NextPlugin extends ServerPlugin { if (!store) return const span = store.span - const req = this._requests.get(span) + + const spanId = span.context()._spanId + const parentSpanId = span.context()._parentId + + // Try current span first, then parent span. + // This handles cases where pageLoad runs in a child span context + const req = this.#requestsBySpanId.get(spanId) ?? this.#requestsBySpanId.get(parentSpanId) // safeguard against missing req in complicated timeout scenarios if (!req) return diff --git a/packages/datadog-plugin-next/test/index.spec.js b/packages/datadog-plugin-next/test/index.spec.js index b57cdc31492..2c5e2c114c4 100644 --- a/packages/datadog-plugin-next/test/index.spec.js +++ b/packages/datadog-plugin-next/test/index.spec.js @@ -277,6 +277,35 @@ describe('Plugin', function () { .get(`http://127.0.0.1:${port}/api/hello/world`) .catch(done) }) + + it('should handle child spans and still find the request object', done => { + agent + .assertSomeTraces(traces => { + const spans = traces[0] + + const nextRequestSpan = spans.find(span => span.name === 'next.request') + assert.ok(nextRequestSpan, 'next.request span should exist') + + assert.strictEqual(nextRequestSpan.resource, 'GET /api/hello/[name]') + assert.strictEqual(nextRequestSpan.meta['next.page'], '/api/hello/[name]') + assert.strictEqual(nextRequestSpan.meta['http.method'], 'GET') + assert.strictEqual(nextRequestSpan.meta['http.status_code'], '200') + + const webRequestSpan = spans.find(span => span.name === 'web.request') + assert.ok(webRequestSpan, 'web.request span should exist') + assert.strictEqual(webRequestSpan.resource, 'GET /api/hello/[name]') + + const childSpan = spans.find(span => span.name === 'child.operation') + assert.ok(childSpan, 'child span should exist') + assert.strictEqual(childSpan.parent_id.toString(), nextRequestSpan.span_id.toString()) + }) + .then(done) + .catch(done) + + axios + .get(`http://127.0.0.1:${port}/api/hello/world?createChildSpan=true`) + .catch(done) + }) }) describe('for pages', () => { diff --git a/packages/datadog-plugin-next/test/pages/api/hello/[name].js b/packages/datadog-plugin-next/test/pages/api/hello/[name].js index 729cdfdb0a7..1b43c15c1a7 100644 --- a/packages/datadog-plugin-next/test/pages/api/hello/[name].js +++ b/packages/datadog-plugin-next/test/pages/api/hello/[name].js @@ -2,6 +2,17 @@ export default (req, res) => { const tracer = require('../../../../../dd-trace') + + if (req.query.createChildSpan === 'true') { + const childSpan = tracer.startSpan('child.operation', { + childOf: tracer.scope().active() + }) + + tracer.scope().activate(childSpan, () => { + childSpan.finish() + }) + } + const span = tracer.scope().active() const name = span && span.context()._name