diff --git a/package.json b/package.json index f1372653304..bfbd7adb574 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ "import-in-the-middle": "^3.0.0" }, "optionalDependencies": { - "@datadog/libdatadog": "0.8.1", + "@datadog/libdatadog": "0.9.2", "@datadog/native-appsec": "11.0.1", "@datadog/native-iast-taint-tracking": "4.1.0", "@datadog/native-metrics": "3.1.1", diff --git a/packages/dd-trace/src/crashtracking/index.js b/packages/dd-trace/src/crashtracking/index.js index 2ba38e72658..5addf3864ef 100644 --- a/packages/dd-trace/src/crashtracking/index.js +++ b/packages/dd-trace/src/crashtracking/index.js @@ -1,9 +1,15 @@ 'use strict' +const { existsSync } = require('node:fs') const { isMainThread } = require('worker_threads') const log = require('../log') -if (isMainThread) { +// libdatadog v29 crashtracker segfaults during init on ARM64 musl (Alpine). +// The segfault bypasses JS try/catch so we must avoid loading it entirely. +// See: https://github.com/DataDog/libdatadog-nodejs/issues/114 +const isArm64Musl = process.arch === 'arm64' && existsSync('/etc/alpine-release') + +if (isMainThread && !isArm64Musl) { try { module.exports = require('./crashtracker') } catch (e) { diff --git a/packages/dd-trace/src/exporters/common/docker.js b/packages/dd-trace/src/exporters/common/docker.js index fd3700c19c0..311b9581cae 100644 --- a/packages/dd-trace/src/exporters/common/docker.js +++ b/packages/dd-trace/src/exporters/common/docker.js @@ -35,6 +35,7 @@ if (inodePath) { const entityId = containerId ? `ci-${containerId}` : inode && `in-${inode}` module.exports = { + containerId, entityId, inject (carrier) { diff --git a/packages/dd-trace/src/tracer_metadata.js b/packages/dd-trace/src/tracer_metadata.js index 3226f8cf590..9a780cdf756 100644 --- a/packages/dd-trace/src/tracer_metadata.js +++ b/packages/dd-trace/src/tracer_metadata.js @@ -11,13 +11,22 @@ function storeConfig (config) { return } + const { containerId } = require('./exporters/common/docker') + const processTags = require('./process-tags') + + const processTagsSerialized = config.propagateProcessTags?.enabled + ? (processTags.serialized || null) + : null + const metadata = new processDiscovery.TracerMetadata( config.tags['runtime-id'], tracerVersion, config.hostname, config.service || null, config.env || null, - config.version || null + config.version || null, + processTagsSerialized, + containerId || null ) return processDiscovery.storeMetadata(metadata) diff --git a/packages/dd-trace/test/tracer_metadata.spec.js b/packages/dd-trace/test/tracer_metadata.spec.js new file mode 100644 index 00000000000..90b38205fc7 --- /dev/null +++ b/packages/dd-trace/test/tracer_metadata.spec.js @@ -0,0 +1,123 @@ +'use strict' + +const assert = require('node:assert/strict') +const { describe, it, beforeEach, afterEach } = require('mocha') +const proxyquire = require('proxyquire') +const sinon = require('sinon') + +describe('tracer_metadata', () => { + let storeConfig + let storeMetadataStub + let TracerMetadataStub + let processDiscoveryStub + let libdatadogStub + let dockerStub + let processTagsStub + + const baseConfig = { + tags: { 'runtime-id': 'test-runtime-id' }, + hostname: 'test-host', + service: 'test-service', + env: 'test-env', + version: '1.0.0', + propagateProcessTags: { enabled: false }, + } + + beforeEach(() => { + storeMetadataStub = sinon.stub().returns({ handle: 'mock-handle' }) + TracerMetadataStub = sinon.stub() + + processDiscoveryStub = { + TracerMetadata: TracerMetadataStub, + storeMetadata: storeMetadataStub, + } + + libdatadogStub = { + maybeLoad: sinon.stub().withArgs('process-discovery').returns(processDiscoveryStub), + } + + dockerStub = { containerId: undefined } + processTagsStub = { serialized: 'tag1:val1,tag2:val2' } + + storeConfig = proxyquire('../src/tracer_metadata', { + '@datadog/libdatadog': libdatadogStub, + './exporters/common/docker': dockerStub, + './process-tags': processTagsStub, + }) + }) + + afterEach(() => { + sinon.restore() + }) + + it('calls storeMetadata with correct base fields', () => { + storeConfig(baseConfig) + + sinon.assert.calledOnce(TracerMetadataStub) + const args = TracerMetadataStub.firstCall.args + assert.strictEqual(args[0], 'test-runtime-id') + assert.strictEqual(args[2], 'test-host') + assert.strictEqual(args[3], 'test-service') + assert.strictEqual(args[4], 'test-env') + assert.strictEqual(args[5], '1.0.0') + + sinon.assert.calledOnce(storeMetadataStub) + }) + + it('passes null for process_tags when propagateProcessTags is disabled', () => { + storeConfig({ ...baseConfig, propagateProcessTags: { enabled: false } }) + + const args = TracerMetadataStub.firstCall.args + assert.strictEqual(args[6], null) + }) + + it('passes serialized process tags when propagateProcessTags is enabled', () => { + storeConfig({ ...baseConfig, propagateProcessTags: { enabled: true } }) + + const args = TracerMetadataStub.firstCall.args + assert.strictEqual(args[6], 'tag1:val1,tag2:val2') + }) + + it('passes container_id when available', () => { + dockerStub.containerId = 'abc123container' + + storeConfig(baseConfig) + + const args = TracerMetadataStub.firstCall.args + assert.strictEqual(args[7], 'abc123container') + }) + + it('passes null for container_id when not in a container', () => { + dockerStub.containerId = undefined + + storeConfig(baseConfig) + + const args = TracerMetadataStub.firstCall.args + assert.strictEqual(args[7], null) + }) + + it('passes null for service when config.service is falsy', () => { + storeConfig({ ...baseConfig, service: undefined }) + + const args = TracerMetadataStub.firstCall.args + assert.strictEqual(args[3], null) + }) + + it('returns undefined and does not throw when process-discovery is unavailable', () => { + libdatadogStub.maybeLoad.returns(undefined) + + const result = storeConfig(baseConfig) + assert.strictEqual(result, undefined) + }) + + it('returns undefined and does not throw when libdatadog throws', () => { + const storeConfigWithThrow = proxyquire('../src/tracer_metadata', { + '@datadog/libdatadog': { maybeLoad: () => { throw new Error('load error') } }, + './exporters/common/docker': dockerStub, + './process-tags': processTagsStub, + }) + + const result = storeConfigWithThrow(baseConfig) + assert.strictEqual(result, undefined) + }) +}) diff --git a/yarn.lock b/yarn.lock index 0c077ff9e6d..eb3d8574f0d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -197,10 +197,10 @@ dependencies: spark-md5 "^3.0.2" -"@datadog/libdatadog@0.8.1": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@datadog/libdatadog/-/libdatadog-0.8.1.tgz#370c7aaf03102399740dcdee52c118da41847c1f" - integrity sha512-WeWPw24cVRmEWfRRw3+h4k7f8mf9vbWEq6MYg1eOd+FycG7q4LaV8/uKROWoKFoD6ScoNvZXxOjKHW3AdYgM+A== +"@datadog/libdatadog@0.9.2": + version "0.9.2" + resolved "https://registry.yarnpkg.com/@datadog/libdatadog/-/libdatadog-0.9.2.tgz#d7a0193ab656bd9cc40649f300ef6c54d9bea52d" + integrity sha512-grOerTYuU3wHuFIOBGg3jB144A3KEthEdVEL3meeiXYo7E7fBXXGRgAOwVE42VXFXfl0r8kDKCL7KupBc511tg== "@datadog/native-appsec@11.0.1": version "11.0.1"