Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions packages/datadog-plugin-fetch/test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,46 @@ describe('Plugin', function () {
return agent.close()
})

describe('with OTel semantics enabled', () => {
beforeEach(() => {
process.env.DD_TRACE_OTEL_SEMANTICS_ENABLED = 'true'
return agent.load('fetch')
.then(() => {
express = require('express')
fetch = globalThis.fetch
})
})

afterEach(() => {
delete process.env.DD_TRACE_OTEL_SEMANTICS_ENABLED
})

// fetch passes a URL object, whose query lives in `search` (not `path`);
// url.full must still retain the (obfuscated) query under OTel semantics.
it('retains the query string in url.full', done => {
const app = express()
app.get('/user', (req, res) => {
res.status(200).send()
})
appListener = server(app, port => {
agent.assertFirstTraceSpan({
meta: {
'span.kind': 'client',
'http.request.method': 'GET',
'url.full': `http://localhost:${port}/user?foo=bar`,
},
metrics: {
'http.response.status_code': 200,
},
})
.then(done)
.catch(done)

fetch(`http://localhost:${port}/user?foo=bar`)
})
})
})

describe('without configuration', () => {
beforeEach(() => {
return agent.load('fetch')
Expand Down
10 changes: 7 additions & 3 deletions packages/datadog-plugin-http/src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const tags = require('../../../ext/tags')
const formats = require('../../../ext/formats')
const HTTP_HEADERS = formats.HTTP_HEADERS
const urlFilter = require('../../dd-trace/src/plugins/util/urlfilter')
const { buildClientHttpUrl } = require('../../dd-trace/src/plugins/util/url')
const log = require('../../dd-trace/src/log')
const { CLIENT_PORT_KEY, COMPONENT, ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../../dd-trace/src/constants')

Expand All @@ -27,9 +28,12 @@ class HttpClientPlugin extends ClientPlugin {
const protocol = options.protocol || agent.protocol || 'http:'
const hostname = options.hostname || options.host || 'localhost'
const host = options.port ? `${hostname}:${options.port}` : hostname
const pathname = options.path || options.pathname
const base = `${protocol}//${host}`
// A URL object (e.g. from the fetch integration) carries the query in
// `options.search`, not `options.path`; keep it so url.full retains the query.
const pathname = options.path || `${options.pathname || ''}${options.search || ''}`
const path = pathname ? pathname.split(/[?#]/)[0] : '/'
const uri = `${protocol}//${host}${path}`
const uri = `${base}${path}`

const allowed = this.config.filter(uri)

Expand All @@ -46,7 +50,7 @@ class HttpClientPlugin extends ClientPlugin {
'resource.name': method,
'span.type': 'http',
'http.method': method,
'http.url': uri,
'http.url': buildClientHttpUrl(this.config, base, pathname, uri),
'out.host': hostname,
},
metrics: {
Expand Down
82 changes: 82 additions & 0 deletions packages/datadog-plugin-http/test/client.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,88 @@ describe('Plugin', () => {
await agent.close()
})

describe('with OTel semantics enabled', () => {
beforeEach(async () => {
process.env.DD_TRACE_OTEL_SEMANTICS_ENABLED = 'true'
tracer = await agent.load('http', { server: false })
http = require(pluginToBeLoaded)
express = require('express')
})

afterEach(() => {
delete process.env.DD_TRACE_OTEL_SEMANTICS_ENABLED
})

it('emits OpenTelemetry client attributes and omits the Datadog ones', done => {
const app = express()
app.get('/user', (req, res) => {
res.status(200).send()
})

appListener = server(app, port => {
agent.assertFirstTraceSpan(span => {
assert.strictEqual(span.type, 'http')
assert.strictEqual(span.resource, 'GET')
assert.strictEqual(span.meta['span.kind'], 'client')
// OpenTelemetry attribute names are present...
assert.strictEqual(span.meta['http.request.method'], 'GET')
assert.strictEqual(span.meta['url.full'], `${protocol}://localhost:${port}/user`)
assert.strictEqual(span.meta['server.address'], 'localhost')
assert.strictEqual(span.metrics['http.response.status_code'], 200)
assert.strictEqual(span.metrics['server.port'], port)
// ...and the Datadog ones are absent.
assert.strictEqual(span.meta['http.method'], undefined)
assert.strictEqual(span.meta['http.url'], undefined)
assert.strictEqual(span.meta['http.status_code'], undefined)
assert.strictEqual(span.meta['out.host'], undefined)
assert.strictEqual(span.meta['error.type'], undefined)
}).then(done).catch(done)

const req = http.request(`${protocol}://localhost:${port}/user`, res => {
res.on('data', () => {})
})
req.end()
})
})

it('sets error.type to the status code on a 4xx client response', done => {
const app = express()
app.get('/bad', (req, res) => {
res.status(400).send()
})

appListener = server(app, port => {
agent.assertFirstTraceSpan(span => {
assert.strictEqual(span.metrics['http.response.status_code'], 400)
assert.strictEqual(span.meta['error.type'], '400')
}).then(done).catch(done)

const req = http.request(`${protocol}://localhost:${port}/bad`, res => {
res.on('data', () => {})
})
req.end()
})
})

it('includes the query string in url.full (obfuscation preserved)', done => {
const app = express()
app.get('/user', (req, res) => {
res.status(200).send()
})

appListener = server(app, port => {
agent.assertFirstTraceSpan(span => {
assert.strictEqual(span.meta['url.full'], `${protocol}://localhost:${port}/user?foo=bar`)
}).then(done).catch(done)

const req = http.request(`${protocol}://localhost:${port}/user?foo=bar`, res => {
res.on('data', () => {})
})
req.end()
})
})
})

describe('without configuration', () => {
beforeEach(async () => {
tracer = await agent.load('http', { server: false })
Expand Down
45 changes: 45 additions & 0 deletions packages/datadog-plugin-http/test/server.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,51 @@ describe('Plugin', () => {
return agent.close()
})

describe('with OTel semantics enabled', () => {
let otelPort

beforeEach(async () => {
process.env.DD_TRACE_OTEL_SEMANTICS_ENABLED = 'true'
await agent.load('http', { client: false })
http = require(pluginToBeLoaded)
})

beforeEach(done => {
appListener = new http.Server(listener).listen(0, 'localhost', () => {
otelPort = appListener.address().port
done()
})
})

afterEach(() => {
delete process.env.DD_TRACE_OTEL_SEMANTICS_ENABLED
})

it('emits OpenTelemetry server attributes and omits the Datadog ones', done => {
agent.assertSomeTraces(traces => {
const span = traces[0][0]
assert.strictEqual(span.name, 'web.request')
assert.strictEqual(span.type, 'web')
assert.strictEqual(span.resource, 'GET')
assert.strictEqual(span.meta['span.kind'], 'server')
// OpenTelemetry attribute names are present...
assert.strictEqual(span.meta['http.request.method'], 'GET')
assert.strictEqual(span.meta['url.path'], '/user')
assert.strictEqual(span.meta['url.scheme'], 'http')
assert.strictEqual(span.meta['server.address'], 'localhost')
assert.strictEqual(span.metrics['server.port'], otelPort)
assert.strictEqual(span.metrics['http.response.status_code'], 200)
// ...and the Datadog ones are absent.
assert.strictEqual(span.meta['http.method'], undefined)
assert.strictEqual(span.meta['http.url'], undefined)
assert.strictEqual(span.meta['http.status_code'], undefined)
assert.strictEqual(span.meta['http.useragent'], undefined)
}).then(done).catch(done)

axios.get(`http://localhost:${otelPort}/user`).catch(done)
})
})

describe('canceled request', () => {
beforeEach(() => {
listener = (req, res) => {
Expand Down
6 changes: 4 additions & 2 deletions packages/datadog-plugin-http2/src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const kinds = require('../../../ext/kinds')
const formats = require('../../../ext/formats')
const { COMPONENT, CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
const urlFilter = require('../../dd-trace/src/plugins/util/urlfilter')
const { buildClientHttpUrl } = require('../../dd-trace/src/plugins/util/url')

const HTTP_HEADERS = formats.HTTP_HEADERS
const HTTP_STATUS_CODE = tags.HTTP_STATUS_CODE
Expand All @@ -33,7 +34,8 @@ class Http2ClientPlugin extends ClientPlugin {
const path = headers[HTTP2_HEADER_PATH] || '/'
const pathname = path.split(/[?#]/)[0]
const method = headers[HTTP2_HEADER_METHOD] || HTTP2_METHOD_GET
const uri = `${sessionDetails.protocol}//${sessionDetails.host}:${sessionDetails.port}${pathname}`
const base = `${sessionDetails.protocol}//${sessionDetails.host}:${sessionDetails.port}`
const uri = `${base}${pathname}`
const allowed = this.config.filter(uri)

const store = storage('legacy').getStore()
Expand All @@ -48,7 +50,7 @@ class Http2ClientPlugin extends ClientPlugin {
'resource.name': method,
'span.type': 'http',
'http.method': method,
'http.url': uri,
'http.url': buildClientHttpUrl(this.config, base, path, uri),
'out.host': sessionDetails.host,
},
metrics: {
Expand Down
41 changes: 41 additions & 0 deletions packages/datadog-plugin-http2/test/client.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,47 @@ describe('Plugin', () => {
return agent.close()
})

describe('with OTel semantics enabled', () => {
beforeEach(() => {
process.env.DD_TRACE_OTEL_SEMANTICS_ENABLED = 'true'
return agent.load('http2', { server: false })
.then(() => {
http2 = require(loadPlugin)
})
})

afterEach(() => {
delete process.env.DD_TRACE_OTEL_SEMANTICS_ENABLED
})

it('emits OpenTelemetry client attributes and omits the Datadog ones', done => {
const app = (stream, headers) => {
stream.respond({ ':status': 200 })
stream.end()
}

appListener = server(app, port => {
agent.assertFirstTraceSpan(span => {
assert.strictEqual(span.meta['span.kind'], 'client')
assert.strictEqual(span.meta['http.request.method'], 'GET')
assert.strictEqual(span.meta['url.full'], `${protocol}://localhost:${port}/user`)
assert.strictEqual(span.meta['server.address'], 'localhost')
assert.strictEqual(span.metrics['http.response.status_code'], 200)
assert.strictEqual(span.metrics['server.port'], port)
assert.strictEqual(span.meta['http.method'], undefined)
assert.strictEqual(span.meta['http.url'], undefined)
assert.strictEqual(span.meta['http.status_code'], undefined)
assert.strictEqual(span.meta['out.host'], undefined)
}).then(done).catch(done)

const client = http2.connect(`${protocol}://localhost:${port}`).on('error', done)
const req = client.request({ ':path': '/user', ':method': 'GET' })
req.on('error', done)
req.end()
})
})
})

describe('without configuration', () => {
beforeEach(() => {
return agent.load('http2', { server: false })
Expand Down
39 changes: 39 additions & 0 deletions packages/datadog-plugin-http2/test/server.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,45 @@ describe('Plugin', () => {
})
})

describe('with OTel semantics enabled', () => {
beforeEach(() => {
process.env.DD_TRACE_OTEL_SEMANTICS_ENABLED = 'true'
return agent.load('http2', { client: false })
.then(() => {
http2 = require(pluginToBeLoaded)
})
})

beforeEach(done => {
appListener = http2.createServer(listener).listen(0, 'localhost', () => {
port = appListener.address().port
done()
})
})

afterEach(() => {
delete process.env.DD_TRACE_OTEL_SEMANTICS_ENABLED
})

it('emits OpenTelemetry server attributes and omits the Datadog ones', done => {
agent.assertSomeTraces(traces => {
const span = traces[0][0]
assert.strictEqual(span.name, 'web.request')
assert.strictEqual(span.meta['span.kind'], 'server')
assert.strictEqual(span.meta['http.request.method'], 'GET')
assert.strictEqual(span.meta['url.path'], '/user')
assert.strictEqual(span.meta['url.scheme'], 'http')
assert.strictEqual(span.meta['server.address'], 'localhost')
assert.strictEqual(span.metrics['http.response.status_code'], 200)
assert.strictEqual(span.meta['http.method'], undefined)
assert.strictEqual(span.meta['http.url'], undefined)
assert.strictEqual(span.meta['http.status_code'], undefined)
}).then(done).catch(done)

request(http2, `http://localhost:${port}/user`).catch(done)
})
})

describe('without configuration', () => {
beforeEach(() => {
return agent.load('http2')
Expand Down
6 changes: 4 additions & 2 deletions packages/datadog-plugin-undici/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const tags = require('../../../ext/tags')
const formats = require('../../../ext/formats')
const HTTP_HEADERS = formats.HTTP_HEADERS
const log = require('../../dd-trace/src/log')
const { buildClientHttpUrl } = require('../../dd-trace/src/plugins/util/url')
const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')

const {
Expand Down Expand Up @@ -60,8 +61,9 @@ class UndiciPlugin extends HttpClientPlugin {
}

const host = port ? `${hostname}:${port}` : hostname
const base = `${protocol}//${host}`
const pathname = path.split(/[?#]/)[0]
const uri = `${protocol}//${host}${pathname}`
const uri = `${base}${pathname}`

const allowed = this.config.filter(uri)
const childOf = store && allowed ? store.span : null
Expand All @@ -71,7 +73,7 @@ class UndiciPlugin extends HttpClientPlugin {
meta: {
'span.kind': 'client',
'http.method': method,
'http.url': uri,
'http.url': buildClientHttpUrl(this.config, base, path, uri),
'out.host': hostname,
},
metrics: {
Expand Down
Loading
Loading