Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3.25.0 #3316

Merged
merged 19 commits into from
Jun 30, 2023
Merged

v3.25.0 #3316

Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/release-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:

injection-image-publish:
runs-on: ubuntu-latest
needs: ['publish']
needs: ['dev_release']
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
Expand Down
1 change: 1 addition & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ package:
stage: deploy
variables:
PRODUCT_NAME: auto_inject-node
PACKAGE_FILTER: js # product name is "node" but package name ends "js"

deploy_to_reliability_env:
stage: deploy
Expand Down
5 changes: 4 additions & 1 deletion docs/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@ tracer.init({
obfuscatorKeyRegex: '.*',
obfuscatorValueRegex: '.*',
blockedTemplateHtml: './blocked.html',
blockedTemplateJson: './blocked.json'
blockedTemplateJson: './blocked.json',
eventTracking: {
mode: 'safe'
}
}
});

Expand Down
27 changes: 27 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,19 @@ export declare interface TracerOptions {
* Specifies a path to a custom blocking template json file.
*/
blockedTemplateJson?: string,

/**
* Controls the automated user event tracking configuration
*/
eventTracking?: {
/**
* Controls the automated user event tracking mode. Possible values are disabled, safe and extended.
* On safe mode, any detected Personally Identifiable Information (PII) about the user will be redacted from the event.
* On extended mode, no redaction will take place.
* @default 'safe'
*/
mode?: 'safe' | 'extended' | 'disabled'
}
};

/**
Expand Down Expand Up @@ -1450,6 +1463,20 @@ declare namespace plugins {
};
}

/**
* This plugin automatically instruments the
* [openai](https://platform.openai.com/docs/api-reference?lang=node.js) module.
*
* Note that for logs to work you'll need to set the `DD_API_KEY` environment variable.
* You'll also need to adjust any firewall settings to allow the tracer to communicate
* with `http-intake.logs.datadoghq.com`.
*
* Note that for metrics to work you'll need to enable
* [DogStatsD](https://docs.datadoghq.com/developers/dogstatsd/?tab=hostagent#setup)
* in the agent.
*/
interface openai extends Instrumentation {}

/**
* This plugin automatically instruments the
* [opensearch](https://github.com/opensearch-project/opensearch-js) module.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict'

/* eslint-disable */

function runTests () {
const promises = global.tests.map(async (test) => {
let testStatus = 'pass'
let testError = null
global.beforeEachHooks.forEach(beforeEach => {
beforeEach(test.description)
})
try {
await test.fn()
console.log(`✓ ${test.description}`)
} catch (e) {
testError = e
testStatus = 'fail'
console.log(`x ${test.description}: ${e}`)
}
global.afterEachHooks.forEach(afterEach => {
afterEach(testStatus, testError)
})
})
return Promise.all(promises)
}

runTests()
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict'

global.tests = []
global.beforeEachHooks = []
global.afterEachHooks = []

function describe (description, cb) {
cb()
}

function test (description, fn) {
global.tests.push({ description, fn })
}

function beforeEach (fn) {
global.beforeEachHooks.push(fn)
}

function afterEach (fn) {
global.afterEachHooks.push(fn)
}

global.describe = describe
global.test = test
global.beforeEach = beforeEach
global.afterEach = afterEach
global.assert = {
equal: (a, b) => {
if (a !== b) {
throw new Error(`${a} is not equal to ${b}`)
}
}
}
48 changes: 48 additions & 0 deletions integration-tests/ci-visibility/test-api-manual/test.fake.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* eslint-disable */
const { channel } = require('diagnostics_channel')
const tracer = require('dd-trace')

const testStartCh = channel('dd-trace:ci:manual:test:start')
const testFinishCh = channel('dd-trace:ci:manual:test:finish')
const testAddTagsCh = channel('dd-trace:ci:manual:test:addTags')
const testSuite = __filename

describe('can run tests', () => {
beforeEach((testName) => {
testStartCh.publish({ testName, testSuite })
})
afterEach((status, error) => {
testFinishCh.publish({ status, error })
})
test('first test will pass', () => {
testAddTagsCh.publish({ 'test.custom.tag': 'custom.value' })
assert.equal(1, 1)
})
test('second test will fail', () => {
assert.equal(1, 2)
})
test('async test will pass', () => {
return new Promise((resolve) => {
setTimeout(() => {
assert.equal(1, 1)
resolve()
}, 10)
})
})
test('integration test', () => {
// Just for testing purposes, so we don't create a custom span
if (!process.env.DD_CIVISIBILITY_MANUAL_API_ENABLED) {
return Promise.resolve()
}
const testSpan = tracer.scope().active()
const childSpan = tracer.startSpan('custom.span', {
childOf: testSpan
})
return new Promise((resolve) => {
setTimeout(() => {
childSpan.finish()
resolve()
}, 10)
})
})
})
50 changes: 50 additions & 0 deletions integration-tests/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class FakeAgent extends EventEmitter {
async start () {
const app = express()
app.use(bodyParser.raw({ limit: Infinity, type: 'application/msgpack' }))
app.use(bodyParser.json({ limit: Infinity, type: 'application/json' }))
app.put('/v0.4/traces', (req, res) => {
if (req.body.length === 0) return res.status(200).send()
res.status(200).send({ rate_by_service: { 'service:,env:': 1 } })
Expand All @@ -43,6 +44,13 @@ class FakeAgent extends EventEmitter {
files: req.files
})
})
app.post('/telemetry/proxy/api/v2/apmtelemetry', (req, res) => {
res.status(200).send()
this.emit('telemetry', {
headers: req.headers,
payload: req.body
})
})

return new Promise((resolve, reject) => {
const timeoutObj = setTimeout(() => {
Expand Down Expand Up @@ -103,6 +111,48 @@ class FakeAgent extends EventEmitter {

return resultPromise
}

assertTelemetryReceived (fn, timeout, requestType, expectedMessageCount = 1) {
timeout = timeout || 5000
let resultResolve
let resultReject
let msgCount = 0
const errors = []

const timeoutObj = setTimeout(() => {
resultReject([...errors, new Error('timeout')])
}, timeout)

const resultPromise = new Promise((resolve, reject) => {
resultResolve = () => {
clearTimeout(timeoutObj)
resolve()
}
resultReject = (e) => {
clearTimeout(timeoutObj)
reject(e)
}
})

const messageHandler = msg => {
if (msg.payload.request_type !== requestType) return
msgCount += 1
try {
fn(msg)
if (msgCount === expectedMessageCount) {
resultResolve()
}
} catch (e) {
errors.push(e)
}
if (msgCount === expectedMessageCount) {
this.removeListener('telemetry', messageHandler)
}
}
this.on('telemetry', messageHandler)

return resultPromise
}
}

function spawnProc (filename, options = {}) {
Expand Down
56 changes: 54 additions & 2 deletions integration-tests/opentelemetry.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ const { join } = require('path')
const { assert } = require('chai')
const { satisfies } = require('semver')

function check (agent, proc, timeout, onMessage = () => { }) {
function check (agent, proc, timeout, onMessage = () => { }, isMetrics) {
const messageReceiver = isMetrics
? agent.assertTelemetryReceived(onMessage, timeout, 'generate-metrics')
: agent.assertMessageReceived(onMessage, timeout)

return Promise.all([
agent.assertMessageReceived(onMessage, timeout),
messageReceiver,
new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error('Process timed out'))
Expand Down Expand Up @@ -38,6 +42,11 @@ function eachEqual (spans, expected, fn) {
return spans.every((span, i) => fn(span) === expected[i])
}

function nearNow (ts, now = Date.now(), range = 1000) {
const delta = Math.abs(now - ts)
return delta < range && delta >= 0
}

describe('opentelemetry', () => {
let agent
let proc
Expand Down Expand Up @@ -84,6 +93,49 @@ describe('opentelemetry', () => {
})
})

it('should capture telemetry', () => {
proc = fork(join(cwd, 'opentelemetry/basic.js'), {
cwd,
env: {
DD_TRACE_AGENT_PORT: agent.port,
DD_TRACE_OTEL_ENABLED: 1,
DD_TELEMETRY_HEARTBEAT_INTERVAL: 1,
TIMEOUT: 1500
}
})

return check(agent, proc, timeout, ({ payload }) => {
assert.strictEqual(payload.request_type, 'generate-metrics')

const metrics = payload.payload
assert.strictEqual(metrics.namespace, 'tracers')

const spanCreated = metrics.series.find(({ metric }) => metric === 'span_created')
const spanFinished = metrics.series.find(({ metric }) => metric === 'span_finished')

// Validate common fields between start and finish
for (const series of [spanCreated, spanFinished]) {
assert.ok(series)

assert.strictEqual(series.points.length, 1)
assert.strictEqual(series.points[0].length, 2)

const [ts, value] = series.points[0]
assert.ok(nearNow(ts, Date.now() / 1e3))
assert.strictEqual(value, 1)

assert.strictEqual(series.type, 'count')
assert.strictEqual(series.common, true)
assert.deepStrictEqual(series.tags, [
'integration_name:otel',
'otel_enabled:true',
'lib_language:nodejs',
`version:${process.version}`
])
}
}, true)
})

it('should work within existing datadog-traced http request', async () => {
proc = fork(join(cwd, 'opentelemetry/server.js'), {
cwd,
Expand Down
5 changes: 5 additions & 0 deletions integration-tests/opentelemetry/basic.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict'

const TIMEOUT = Number(process.env.TIMEOUT || 0)

const tracer = require('dd-trace').init()

const { TracerProvider } = tracer
Expand All @@ -16,5 +18,8 @@ const otelTracer = ot.trace.getTracer(
otelTracer.startActiveSpan('otel-sub', otelSpan => {
setImmediate(() => {
otelSpan.end()

// Allow the process to be held open to gather telemetry metrics
setTimeout(() => {}, TIMEOUT)
})
})
Loading