dd-trace 5.71.0 → 5.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +5 -0
- package/index.d.ts +93 -1
- package/package.json +21 -2
- package/packages/datadog-instrumentations/src/azure-event-hubs.js +37 -0
- package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
- package/packages/datadog-instrumentations/src/cucumber.js +7 -7
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/jest.js +29 -36
- package/packages/datadog-instrumentations/src/mocha/main.js +8 -9
- package/packages/datadog-instrumentations/src/mocha/utils.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/worker.js +2 -2
- package/packages/datadog-instrumentations/src/pg.js +1 -1
- package/packages/datadog-instrumentations/src/playwright.js +5 -5
- package/packages/datadog-instrumentations/src/vitest.js +8 -8
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +73 -27
- package/packages/datadog-plugin-azure-event-hubs/src/index.js +15 -0
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +82 -0
- package/packages/datadog-plugin-azure-functions/src/index.js +37 -0
- package/packages/datadog-plugin-cucumber/src/index.js +3 -3
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -9
- package/packages/datadog-plugin-jest/src/util.js +10 -2
- package/packages/datadog-plugin-mocha/src/index.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +2 -2
- package/packages/datadog-plugin-vitest/src/index.js +2 -2
- package/packages/datadog-plugin-ws/src/server.js +5 -3
- package/packages/dd-trace/src/config.js +108 -26
- package/packages/dd-trace/src/config_defaults.js +12 -0
- package/packages/dd-trace/src/git_properties.js +90 -5
- package/packages/dd-trace/src/noop/proxy.js +3 -0
- package/packages/dd-trace/src/openfeature/constants/constants.js +51 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +45 -0
- package/packages/dd-trace/src/openfeature/index.js +77 -0
- package/packages/dd-trace/src/openfeature/noop.js +101 -0
- package/packages/dd-trace/src/openfeature/writers/base.js +181 -0
- package/packages/dd-trace/src/openfeature/writers/exposures.js +173 -0
- package/packages/dd-trace/src/openfeature/writers/util.js +43 -0
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +100 -0
- package/packages/dd-trace/src/opentelemetry/logs/index.js +87 -0
- package/packages/dd-trace/src/opentelemetry/logs/logger.js +77 -0
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +126 -0
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +173 -0
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +367 -0
- package/packages/dd-trace/src/opentelemetry/protos/common.proto +116 -0
- package/packages/dd-trace/src/opentelemetry/protos/logs.proto +226 -0
- package/packages/dd-trace/src/opentelemetry/protos/logs_service.proto +78 -0
- package/packages/dd-trace/src/opentelemetry/protos/protobuf_loader.js +48 -0
- package/packages/dd-trace/src/opentelemetry/protos/resource.proto +45 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +7 -6
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/test.js +6 -5
- package/packages/dd-trace/src/profiling/config.js +21 -1
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -2
- package/packages/dd-trace/src/profiling/profiler.js +44 -22
- package/packages/dd-trace/src/profiling/profilers/events.js +12 -3
- package/packages/dd-trace/src/profiling/profilers/space.js +35 -24
- package/packages/dd-trace/src/profiling/profilers/wall.js +14 -6
- package/packages/dd-trace/src/proxy.js +22 -1
- package/packages/dd-trace/src/remote_config/capabilities.js +1 -0
- package/packages/dd-trace/src/remote_config/index.js +1 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
- package/packages/dd-trace/src/supported-configurations.json +17 -0
- package/packages/dd-trace/src/telemetry/telemetry.js +13 -1
|
@@ -9,7 +9,8 @@ const tagger = require('./tagger')
|
|
|
9
9
|
const set = require('../../datadog-core/src/utils/src/set')
|
|
10
10
|
const { isTrue, isFalse, normalizeProfilingEnabledValue } = require('./util')
|
|
11
11
|
const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('./plugins/util/tags')
|
|
12
|
-
const { getGitMetadataFromGitProperties, removeUserSensitiveInfo } =
|
|
12
|
+
const { getGitMetadataFromGitProperties, removeUserSensitiveInfo, getRemoteOriginURL, resolveGitHeadSHA } =
|
|
13
|
+
require('./git_properties')
|
|
13
14
|
const { updateConfig } = require('./telemetry')
|
|
14
15
|
const telemetryMetrics = require('./telemetry/metrics')
|
|
15
16
|
const { isInServerlessEnvironment, getIsGCPFunction, getIsAzureFunction } = require('./serverless')
|
|
@@ -17,6 +18,7 @@ const { ORIGIN_KEY } = require('./constants')
|
|
|
17
18
|
const { appendRules } = require('./payload-tagging/config')
|
|
18
19
|
const { getEnvironmentVariable, getEnvironmentVariables } = require('./config-helper')
|
|
19
20
|
const defaults = require('./config_defaults')
|
|
21
|
+
const path = require('path')
|
|
20
22
|
|
|
21
23
|
const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
|
|
22
24
|
|
|
@@ -67,6 +69,8 @@ const VALID_PROPAGATION_BEHAVIOR_EXTRACT = new Set(['continue', 'restart', 'igno
|
|
|
67
69
|
|
|
68
70
|
const VALID_LOG_LEVELS = new Set(['debug', 'info', 'warn', 'error'])
|
|
69
71
|
|
|
72
|
+
const DEFAULT_OTLP_PORT = 4318
|
|
73
|
+
|
|
70
74
|
function getFromOtelSamplerMap (otelTracesSampler, otelTracesSamplerArg) {
|
|
71
75
|
const OTEL_TRACES_SAMPLER_MAPPING = {
|
|
72
76
|
always_on: '1.0',
|
|
@@ -380,30 +384,7 @@ class Config {
|
|
|
380
384
|
}
|
|
381
385
|
|
|
382
386
|
if (this.gitMetadataEnabled) {
|
|
383
|
-
this.
|
|
384
|
-
getEnvironmentVariable('DD_GIT_REPOSITORY_URL') ??
|
|
385
|
-
this.tags[GIT_REPOSITORY_URL]
|
|
386
|
-
)
|
|
387
|
-
this.commitSHA = getEnvironmentVariable('DD_GIT_COMMIT_SHA') ??
|
|
388
|
-
this.tags[GIT_COMMIT_SHA]
|
|
389
|
-
if (!this.repositoryUrl || !this.commitSHA) {
|
|
390
|
-
const DD_GIT_PROPERTIES_FILE = getEnvironmentVariable('DD_GIT_PROPERTIES_FILE') ??
|
|
391
|
-
`${process.cwd()}/git.properties`
|
|
392
|
-
let gitPropertiesString
|
|
393
|
-
try {
|
|
394
|
-
gitPropertiesString = fs.readFileSync(DD_GIT_PROPERTIES_FILE, 'utf8')
|
|
395
|
-
} catch (e) {
|
|
396
|
-
// Only log error if the user has set a git.properties path
|
|
397
|
-
if (getEnvironmentVariable('DD_GIT_PROPERTIES_FILE')) {
|
|
398
|
-
log.error('Error reading DD_GIT_PROPERTIES_FILE: %s', DD_GIT_PROPERTIES_FILE, e)
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
if (gitPropertiesString) {
|
|
402
|
-
const { commitSHA, repositoryUrl } = getGitMetadataFromGitProperties(gitPropertiesString)
|
|
403
|
-
this.commitSHA = this.commitSHA || commitSHA
|
|
404
|
-
this.repositoryUrl = this.repositoryUrl || repositoryUrl
|
|
405
|
-
}
|
|
406
|
-
}
|
|
387
|
+
this._loadGitMetadata()
|
|
407
388
|
}
|
|
408
389
|
}
|
|
409
390
|
|
|
@@ -554,6 +535,7 @@ class Config {
|
|
|
554
535
|
DD_INSTRUMENTATION_TELEMETRY_ENABLED,
|
|
555
536
|
DD_INSTRUMENTATION_CONFIG_ID,
|
|
556
537
|
DD_LOGS_INJECTION,
|
|
538
|
+
DD_LOGS_OTEL_ENABLED,
|
|
557
539
|
DD_LANGCHAIN_SPAN_CHAR_LIMIT,
|
|
558
540
|
DD_LANGCHAIN_SPAN_PROMPT_COMPLETION_SAMPLE_RATE,
|
|
559
541
|
DD_LLMOBS_AGENTLESS_ENABLED,
|
|
@@ -635,7 +617,18 @@ class Config {
|
|
|
635
617
|
OTEL_RESOURCE_ATTRIBUTES,
|
|
636
618
|
OTEL_SERVICE_NAME,
|
|
637
619
|
OTEL_TRACES_SAMPLER,
|
|
638
|
-
OTEL_TRACES_SAMPLER_ARG
|
|
620
|
+
OTEL_TRACES_SAMPLER_ARG,
|
|
621
|
+
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED,
|
|
622
|
+
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
|
|
623
|
+
OTEL_EXPORTER_OTLP_LOGS_HEADERS,
|
|
624
|
+
OTEL_EXPORTER_OTLP_LOGS_PROTOCOL,
|
|
625
|
+
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
|
|
626
|
+
OTEL_EXPORTER_OTLP_PROTOCOL,
|
|
627
|
+
OTEL_EXPORTER_OTLP_ENDPOINT,
|
|
628
|
+
OTEL_EXPORTER_OTLP_HEADERS,
|
|
629
|
+
OTEL_EXPORTER_OTLP_TIMEOUT,
|
|
630
|
+
OTEL_BSP_SCHEDULE_DELAY,
|
|
631
|
+
OTEL_BSP_MAX_EXPORT_BATCH_SIZE
|
|
639
632
|
} = getEnvironmentVariables()
|
|
640
633
|
|
|
641
634
|
const tags = {}
|
|
@@ -649,6 +642,23 @@ class Config {
|
|
|
649
642
|
tagger.add(tags, DD_TRACE_TAGS)
|
|
650
643
|
tagger.add(tags, DD_TRACE_GLOBAL_TAGS)
|
|
651
644
|
|
|
645
|
+
this._setBoolean(env, 'otelLogsEnabled', isTrue(DD_LOGS_OTEL_ENABLED))
|
|
646
|
+
// Set OpenTelemetry logs configuration with specific _LOGS_ vars taking precedence over generic _EXPORTERS_ vars
|
|
647
|
+
if (OTEL_EXPORTER_OTLP_ENDPOINT) {
|
|
648
|
+
// Only set if there's a custom URL, otherwise let calc phase handle the default
|
|
649
|
+
this._setString(env, 'otelUrl', OTEL_EXPORTER_OTLP_ENDPOINT)
|
|
650
|
+
}
|
|
651
|
+
if (OTEL_EXPORTER_OTLP_ENDPOINT || OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) {
|
|
652
|
+
this._setString(env, 'otelLogsUrl', OTEL_EXPORTER_OTLP_LOGS_ENDPOINT || env.otelUrl)
|
|
653
|
+
}
|
|
654
|
+
this._setString(env, 'otelHeaders', OTEL_EXPORTER_OTLP_HEADERS)
|
|
655
|
+
this._setString(env, 'otelLogsHeaders', OTEL_EXPORTER_OTLP_LOGS_HEADERS || env.otelHeaders)
|
|
656
|
+
this._setString(env, 'otelProtocol', OTEL_EXPORTER_OTLP_PROTOCOL)
|
|
657
|
+
this._setString(env, 'otelLogsProtocol', OTEL_EXPORTER_OTLP_LOGS_PROTOCOL || env.otelProtocol)
|
|
658
|
+
env.otelTimeout = maybeInt(OTEL_EXPORTER_OTLP_TIMEOUT)
|
|
659
|
+
env.otelLogsTimeout = maybeInt(OTEL_EXPORTER_OTLP_LOGS_TIMEOUT) || env.otelTimeout
|
|
660
|
+
env.otelLogsBatchTimeout = maybeInt(OTEL_BSP_SCHEDULE_DELAY)
|
|
661
|
+
env.otelLogsMaxExportBatchSize = maybeInt(OTEL_BSP_MAX_EXPORT_BATCH_SIZE)
|
|
652
662
|
this._setBoolean(
|
|
653
663
|
env,
|
|
654
664
|
'apmTracingEnabled',
|
|
@@ -721,6 +731,7 @@ class Config {
|
|
|
721
731
|
env['dynamicInstrumentation.uploadIntervalSeconds'] = maybeFloat(DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS)
|
|
722
732
|
this._envUnprocessed['dynamicInstrumentation.uploadInterval'] = DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS
|
|
723
733
|
this._setString(env, 'env', DD_ENV || tags.env)
|
|
734
|
+
this._setBoolean(env, 'experimental.flaggingProvider.enabled', DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED)
|
|
724
735
|
this._setBoolean(env, 'traceEnabled', DD_TRACE_ENABLED)
|
|
725
736
|
this._setBoolean(env, 'experimental.aiguard.enabled', DD_AI_GUARD_ENABLED)
|
|
726
737
|
this._setString(env, 'experimental.aiguard.endpoint', DD_AI_GUARD_ENDPOINT)
|
|
@@ -970,6 +981,7 @@ class Config {
|
|
|
970
981
|
this._optsUnprocessed['experimental.aiguard.timeout'] = options.experimental?.aiguard?.timeout
|
|
971
982
|
this._setBoolean(opts, 'experimental.enableGetRumData', options.experimental?.enableGetRumData)
|
|
972
983
|
this._setString(opts, 'experimental.exporter', options.experimental?.exporter)
|
|
984
|
+
this._setBoolean(opts, 'experimental.flaggingProvider.enabled', options.experimental?.flaggingProvider?.enabled)
|
|
973
985
|
opts.flushInterval = maybeInt(options.flushInterval)
|
|
974
986
|
this._optsUnprocessed.flushInterval = options.flushInterval
|
|
975
987
|
opts.flushMinSpans = maybeInt(options.flushMinSpans)
|
|
@@ -1156,7 +1168,20 @@ class Config {
|
|
|
1156
1168
|
calc.testManagementAttemptToFixRetries = maybeInt(DD_TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES) ?? 20
|
|
1157
1169
|
this._setBoolean(calc, 'isImpactedTestsEnabled', !isFalse(DD_CIVISIBILITY_IMPACTED_TESTS_DETECTION_ENABLED))
|
|
1158
1170
|
}
|
|
1171
|
+
|
|
1172
|
+
// Disable log injection when OTEL logs are enabled
|
|
1173
|
+
// OTEL logs and DD log injection are mutually exclusive
|
|
1174
|
+
if (this._env.otelLogsEnabled) {
|
|
1175
|
+
this._setBoolean(calc, 'logInjection', false)
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1159
1178
|
calc['dogstatsd.hostname'] = this._getHostname()
|
|
1179
|
+
|
|
1180
|
+
// Compute OTLP logs URL to send payloads to the active Datadog Agent
|
|
1181
|
+
const agentHostname = this._getHostname()
|
|
1182
|
+
calc.otelLogsUrl = `http://${agentHostname}:${DEFAULT_OTLP_PORT}`
|
|
1183
|
+
calc.otelUrl = `http://${agentHostname}:${DEFAULT_OTLP_PORT}`
|
|
1184
|
+
|
|
1160
1185
|
this._setBoolean(calc, 'isGitUploadEnabled',
|
|
1161
1186
|
calc.isIntelligentTestRunnerEnabled && !isFalse(this._isCiVisibilityGitUploadEnabled()))
|
|
1162
1187
|
this._setBoolean(calc, 'spanComputePeerService', this._getSpanComputePeerService())
|
|
@@ -1366,6 +1391,63 @@ class Config {
|
|
|
1366
1391
|
}
|
|
1367
1392
|
}
|
|
1368
1393
|
}
|
|
1394
|
+
|
|
1395
|
+
_loadGitMetadata () {
|
|
1396
|
+
// try to read Git metadata from the environment variables
|
|
1397
|
+
this.repositoryUrl = removeUserSensitiveInfo(
|
|
1398
|
+
getEnvironmentVariable('DD_GIT_REPOSITORY_URL') ??
|
|
1399
|
+
this.tags[GIT_REPOSITORY_URL]
|
|
1400
|
+
)
|
|
1401
|
+
this.commitSHA = getEnvironmentVariable('DD_GIT_COMMIT_SHA') ??
|
|
1402
|
+
this.tags[GIT_COMMIT_SHA]
|
|
1403
|
+
|
|
1404
|
+
// otherwise, try to read Git metadata from the git.properties file
|
|
1405
|
+
if (!this.repositoryUrl || !this.commitSHA) {
|
|
1406
|
+
const DD_GIT_PROPERTIES_FILE = getEnvironmentVariable('DD_GIT_PROPERTIES_FILE') ??
|
|
1407
|
+
`${process.cwd()}/git.properties`
|
|
1408
|
+
let gitPropertiesString
|
|
1409
|
+
try {
|
|
1410
|
+
gitPropertiesString = fs.readFileSync(DD_GIT_PROPERTIES_FILE, 'utf8')
|
|
1411
|
+
} catch (e) {
|
|
1412
|
+
// Only log error if the user has set a git.properties path
|
|
1413
|
+
if (getEnvironmentVariable('DD_GIT_PROPERTIES_FILE')) {
|
|
1414
|
+
log.error('Error reading DD_GIT_PROPERTIES_FILE: %s', DD_GIT_PROPERTIES_FILE, e)
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
if (gitPropertiesString) {
|
|
1418
|
+
const { commitSHA, repositoryUrl } = getGitMetadataFromGitProperties(gitPropertiesString)
|
|
1419
|
+
this.commitSHA = this.commitSHA || commitSHA
|
|
1420
|
+
this.repositoryUrl = this.repositoryUrl || repositoryUrl
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
// otherwise, try to read Git metadata from the .git/ folder
|
|
1424
|
+
if (!this.repositoryUrl || !this.commitSHA) {
|
|
1425
|
+
const DD_GIT_FOLDER_PATH = getEnvironmentVariable('DD_GIT_FOLDER_PATH') ??
|
|
1426
|
+
path.join(process.cwd(), '.git')
|
|
1427
|
+
if (!this.repositoryUrl) {
|
|
1428
|
+
// try to read git config (repository URL)
|
|
1429
|
+
const gitConfigPath = path.join(DD_GIT_FOLDER_PATH, 'config')
|
|
1430
|
+
try {
|
|
1431
|
+
const gitConfigContent = fs.readFileSync(gitConfigPath, 'utf8')
|
|
1432
|
+
if (gitConfigContent) {
|
|
1433
|
+
this.repositoryUrl = getRemoteOriginURL(gitConfigContent)
|
|
1434
|
+
}
|
|
1435
|
+
} catch (e) {
|
|
1436
|
+
// Only log error if the user has set a .git/ path
|
|
1437
|
+
if (getEnvironmentVariable('DD_GIT_FOLDER_PATH')) {
|
|
1438
|
+
log.error('Error reading git config: %s', gitConfigPath, e)
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
if (!this.commitSHA) {
|
|
1443
|
+
// try to read git HEAD (commit SHA)
|
|
1444
|
+
const gitHeadSha = resolveGitHeadSHA(DD_GIT_FOLDER_PATH)
|
|
1445
|
+
if (gitHeadSha) {
|
|
1446
|
+
this.commitSHA = gitHeadSha
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1369
1451
|
}
|
|
1370
1452
|
|
|
1371
1453
|
function handleOtel (tagString) {
|
|
@@ -77,6 +77,7 @@ module.exports = {
|
|
|
77
77
|
'experimental.aiguard.timeout': 10_000, // ms
|
|
78
78
|
'experimental.enableGetRumData': false,
|
|
79
79
|
'experimental.exporter': undefined,
|
|
80
|
+
'experimental.flaggingProvider.enabled': false,
|
|
80
81
|
flushInterval: 2000,
|
|
81
82
|
flushMinSpans: 1000,
|
|
82
83
|
gitMetadataEnabled: true,
|
|
@@ -126,6 +127,17 @@ module.exports = {
|
|
|
126
127
|
isTestManagementEnabled: false,
|
|
127
128
|
isImpactedTestsEnabled: false,
|
|
128
129
|
logInjection: true,
|
|
130
|
+
otelLogsEnabled: false,
|
|
131
|
+
otelUrl: undefined,
|
|
132
|
+
otelLogsUrl: undefined, // Will be computed using agent host
|
|
133
|
+
otelHeaders: undefined,
|
|
134
|
+
otelLogsHeaders: '',
|
|
135
|
+
otelProtocol: 'http/protobuf',
|
|
136
|
+
otelLogsProtocol: 'http/protobuf',
|
|
137
|
+
otelLogsTimeout: 10_000,
|
|
138
|
+
otelTimeout: 10_000,
|
|
139
|
+
otelLogsBatchTimeout: 5000,
|
|
140
|
+
otelLogsMaxExportBatchSize: 512,
|
|
129
141
|
lookup: undefined,
|
|
130
142
|
inferredProxyServicesEnabled: false,
|
|
131
143
|
memcachedCommandEnabled: false,
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
|
|
6
|
+
const gitPropertiesCommitSHARegex = /git\.commit\.sha=([a-f\d]{40})/
|
|
7
|
+
const gitPropertiesRepositoryUrlRegex = /git\.repository_url=([\w\d:@/.-]+)/
|
|
8
|
+
const repositoryUrlRegex = /^([\w\d:@/.-]+)$/
|
|
9
|
+
const remoteOriginRegex = /^\[remote\s+"origin"\]/i
|
|
10
|
+
const gitHeadRefRegex = /ref:\s+(refs\/[A-Za-z0-9._/-]+)/
|
|
11
|
+
const commitSHARegex = /^[0-9a-f]{40}$/
|
|
5
12
|
|
|
6
13
|
function removeUserSensitiveInfo (repositoryUrl) {
|
|
7
14
|
try {
|
|
@@ -21,8 +28,8 @@ function getGitMetadataFromGitProperties (gitPropertiesString) {
|
|
|
21
28
|
if (!gitPropertiesString) {
|
|
22
29
|
return {}
|
|
23
30
|
}
|
|
24
|
-
const commitSHAMatch = gitPropertiesString.match(
|
|
25
|
-
const repositoryUrlMatch = gitPropertiesString.match(
|
|
31
|
+
const commitSHAMatch = gitPropertiesString.match(gitPropertiesCommitSHARegex)
|
|
32
|
+
const repositoryUrlMatch = gitPropertiesString.match(gitPropertiesRepositoryUrlRegex)
|
|
26
33
|
|
|
27
34
|
const repositoryUrl = repositoryUrlMatch ? repositoryUrlMatch[1] : undefined
|
|
28
35
|
|
|
@@ -32,4 +39,82 @@ function getGitMetadataFromGitProperties (gitPropertiesString) {
|
|
|
32
39
|
}
|
|
33
40
|
}
|
|
34
41
|
|
|
35
|
-
|
|
42
|
+
function getRemoteOriginURL (gitConfigContent) {
|
|
43
|
+
if (!gitConfigContent) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
const lines = gitConfigContent.split('\n')
|
|
47
|
+
let index = 0
|
|
48
|
+
|
|
49
|
+
// find the remote origin section
|
|
50
|
+
for (; index < lines.length; index++) {
|
|
51
|
+
const line = lines[index]
|
|
52
|
+
if (line[0] !== '[') continue // fast path
|
|
53
|
+
if (remoteOriginRegex.test(line)) break
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// find the url key/value in the [remote "origin"] section
|
|
57
|
+
index++
|
|
58
|
+
for (; index < lines.length; index++) {
|
|
59
|
+
const line = lines[index]
|
|
60
|
+
if (line[0] === '[') return // abort, section didn't contain a url
|
|
61
|
+
const splitAt = line.indexOf('=')
|
|
62
|
+
if (splitAt === -1) continue
|
|
63
|
+
const key = line.slice(0, splitAt).trim().toLowerCase()
|
|
64
|
+
if (key !== 'url') continue
|
|
65
|
+
const repositoryUrlValue = line.slice(splitAt + 1).trim()
|
|
66
|
+
const repositoryUrlMatch = repositoryUrlValue.match(repositoryUrlRegex)
|
|
67
|
+
if (!repositoryUrlMatch) continue
|
|
68
|
+
return removeUserSensitiveInfo(repositoryUrlMatch[0])
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getGitHeadRef (gitHeadContent) {
|
|
73
|
+
if (!gitHeadContent) {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Extract the ref after 'ref: '
|
|
78
|
+
const gitRefMatch = gitHeadContent.match(gitHeadRefRegex)
|
|
79
|
+
return gitRefMatch?.[1]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function resolveGitHeadSHA (DD_GIT_FOLDER_PATH) {
|
|
83
|
+
const gitHeadPath = path.join(DD_GIT_FOLDER_PATH, 'HEAD')
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const gitHeadContent = fs.readFileSync(gitHeadPath, 'utf8')
|
|
87
|
+
if (!gitHeadContent) {
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const headContent = gitHeadContent.trim()
|
|
92
|
+
|
|
93
|
+
// Handle detached head case
|
|
94
|
+
if (commitSHARegex.test(headContent)) {
|
|
95
|
+
return headContent
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Handle ref case - extract the ref and read the SHA from the ref file
|
|
99
|
+
const gitHeadRef = getGitHeadRef(headContent)
|
|
100
|
+
if (!gitHeadRef) {
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
const gitHeadRefPath = path.join(DD_GIT_FOLDER_PATH, gitHeadRef)
|
|
104
|
+
const gitHeadRefContent = fs.readFileSync(gitHeadRefPath, 'utf8')
|
|
105
|
+
if (gitHeadRefContent) {
|
|
106
|
+
const headRefContent = gitHeadRefContent.trim()
|
|
107
|
+
if (commitSHARegex.test(headRefContent)) {
|
|
108
|
+
return headRefContent
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch {}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
getGitMetadataFromGitProperties,
|
|
116
|
+
removeUserSensitiveInfo,
|
|
117
|
+
getGitHeadRef,
|
|
118
|
+
getRemoteOriginURL,
|
|
119
|
+
resolveGitHeadSHA,
|
|
120
|
+
}
|
|
@@ -4,12 +4,14 @@ const NoopTracer = require('./tracer')
|
|
|
4
4
|
const NoopAppsecSdk = require('../appsec/sdk/noop')
|
|
5
5
|
const NoopDogStatsDClient = require('./dogstatsd')
|
|
6
6
|
const NoopLLMObsSDK = require('../llmobs/noop')
|
|
7
|
+
const NoopFlaggingProvider = require('../openfeature/noop')
|
|
7
8
|
const NoopAIGuardSDK = require('../aiguard/noop')
|
|
8
9
|
|
|
9
10
|
const noop = new NoopTracer()
|
|
10
11
|
const noopAppsec = new NoopAppsecSdk()
|
|
11
12
|
const noopDogStatsDClient = new NoopDogStatsDClient()
|
|
12
13
|
const noopLLMObs = new NoopLLMObsSDK(noop)
|
|
14
|
+
const noopOpenFeatureProvider = new NoopFlaggingProvider()
|
|
13
15
|
const noopAIGuard = new NoopAIGuardSDK()
|
|
14
16
|
|
|
15
17
|
/** @type {import('../../src/index')} Proxy */
|
|
@@ -19,6 +21,7 @@ class NoopProxy {
|
|
|
19
21
|
this.appsec = noopAppsec
|
|
20
22
|
this.dogstatsd = noopDogStatsDClient
|
|
21
23
|
this.llmobs = noopLLMObs
|
|
24
|
+
this.openfeature = noopOpenFeatureProvider
|
|
22
25
|
this.aiguard = noopAIGuard
|
|
23
26
|
this.setBaggageItem = () => {}
|
|
24
27
|
this.getBaggageItem = () => {}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
/**
|
|
5
|
+
* @constant
|
|
6
|
+
* @type {string} Base path for EVP proxy agent endpoint
|
|
7
|
+
*/
|
|
8
|
+
EVP_PROXY_AGENT_BASE_PATH: '/evp_proxy/v2/',
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @constant
|
|
12
|
+
* @type {string} HTTP header name for EVP subdomain routing
|
|
13
|
+
*/
|
|
14
|
+
EVP_SUBDOMAIN_HEADER_NAME: 'X-Datadog-EVP-Subdomain',
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @constant
|
|
18
|
+
* @type {string} EVP subdomain value for event platform intake
|
|
19
|
+
*/
|
|
20
|
+
EVP_SUBDOMAIN_VALUE: 'event-platform-intake',
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @constant
|
|
24
|
+
* @type {string} API endpoint for exposure events EVP track
|
|
25
|
+
*/
|
|
26
|
+
EXPOSURES_ENDPOINT: '/api/v2/exposures',
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @constant
|
|
30
|
+
* @type {number} Maximum payload size for EVP intake (5MB, actual limit is 5.1MB)
|
|
31
|
+
*/
|
|
32
|
+
EVP_PAYLOAD_SIZE_LIMIT: 5 << 20,
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @constant
|
|
36
|
+
* @type {number} Maximum individual event size (999KB, actual limit is 1MB)
|
|
37
|
+
*/
|
|
38
|
+
EVP_EVENT_SIZE_LIMIT: (1 << 20) - 1024,
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @constant
|
|
42
|
+
* @type {string} Channel name for exposure event submission
|
|
43
|
+
*/
|
|
44
|
+
EXPOSURE_CHANNEL: 'ffe:exposure:submit',
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @constant
|
|
48
|
+
* @type {string} Reason code for noop provider evaluations
|
|
49
|
+
*/
|
|
50
|
+
NOOP_REASON: 'STATIC'
|
|
51
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { DatadogNodeServerProvider } = require('@datadog/openfeature-node-server')
|
|
4
|
+
const { channel } = require('dc-polyfill')
|
|
5
|
+
const log = require('../log')
|
|
6
|
+
const { EXPOSURE_CHANNEL } = require('./constants/constants')
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* OpenFeature provider that integrates with Datadog's feature flagging system.
|
|
10
|
+
* Extends DatadogNodeServerProvider to add tracer integration and configuration management.
|
|
11
|
+
*/
|
|
12
|
+
class FlaggingProvider extends DatadogNodeServerProvider {
|
|
13
|
+
/**
|
|
14
|
+
* @param {import('../tracer')} tracer - Datadog tracer instance
|
|
15
|
+
* @param {import('../config')} config - Tracer configuration object
|
|
16
|
+
*/
|
|
17
|
+
constructor (tracer, config) {
|
|
18
|
+
// Call parent constructor with required options
|
|
19
|
+
super({
|
|
20
|
+
exposureChannel: channel(EXPOSURE_CHANNEL)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
this._tracer = tracer
|
|
24
|
+
this._config = config
|
|
25
|
+
|
|
26
|
+
log.debug(this.constructor.name + ' created')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Internal method to update flag configuration from Remote Config.
|
|
31
|
+
* This method is called automatically when Remote Config delivers UFC updates.
|
|
32
|
+
*
|
|
33
|
+
* @internal
|
|
34
|
+
* @param {import('@datadog/openfeature-node-server').UniversalFlagConfigurationV1} ufc
|
|
35
|
+
* - Universal Flag Configuration object
|
|
36
|
+
*/
|
|
37
|
+
_setConfiguration (ufc) {
|
|
38
|
+
if (typeof this.setConfiguration === 'function') {
|
|
39
|
+
this.setConfiguration(ufc)
|
|
40
|
+
}
|
|
41
|
+
log.debug(this.constructor.name + ' provider configuration updated')
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = FlaggingProvider
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const log = require('../log')
|
|
4
|
+
const ExposuresWriter = require('./writers/exposures')
|
|
5
|
+
const { setAgentStrategy } = require('./writers/util')
|
|
6
|
+
const { channel } = require('dc-polyfill')
|
|
7
|
+
|
|
8
|
+
const exposureSubmitCh = channel('ffe:exposure:submit')
|
|
9
|
+
const flushCh = channel('ffe:writers:flush')
|
|
10
|
+
|
|
11
|
+
let exposuresWriter = null
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @private
|
|
15
|
+
* @param {Object|Array<Object>} exposureEvents - Exposure events channel subscriber
|
|
16
|
+
* @returns {void}
|
|
17
|
+
*/
|
|
18
|
+
function _handleExposureSubmit (exposureEvents) {
|
|
19
|
+
if (!exposuresWriter) return
|
|
20
|
+
exposuresWriter.append(exposureEvents)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Channel subscriber for manually flushing the exposures writer
|
|
25
|
+
* @private
|
|
26
|
+
* @returns {void}
|
|
27
|
+
*/
|
|
28
|
+
function _handleFlush () {
|
|
29
|
+
exposuresWriter?.flush()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Enables the OpenFeature module and sets up FF&E writer and channel subscribers
|
|
34
|
+
* @param {import('../config')} config - Tracer configuration object
|
|
35
|
+
* @returns {void}
|
|
36
|
+
*/
|
|
37
|
+
function enable (config) {
|
|
38
|
+
if (exposuresWriter) {
|
|
39
|
+
log.warn(exposuresWriter.constructor.name + ' already enabled')
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
exposuresWriter = new ExposuresWriter(config)
|
|
44
|
+
exposureSubmitCh.subscribe(_handleExposureSubmit)
|
|
45
|
+
flushCh.subscribe(_handleFlush)
|
|
46
|
+
|
|
47
|
+
setAgentStrategy(config, hasAgent => {
|
|
48
|
+
exposuresWriter?.setEnabled(hasAgent)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
log.debug('OpenFeature module enabled')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Disables the OpenFeature module and cleans up resources
|
|
56
|
+
* @returns {void}
|
|
57
|
+
*/
|
|
58
|
+
function disable () {
|
|
59
|
+
if (!exposuresWriter) return
|
|
60
|
+
|
|
61
|
+
if (exposureSubmitCh.hasSubscribers) {
|
|
62
|
+
exposureSubmitCh.unsubscribe(_handleExposureSubmit)
|
|
63
|
+
}
|
|
64
|
+
if (flushCh.hasSubscribers) {
|
|
65
|
+
flushCh.unsubscribe(_handleFlush)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
exposuresWriter.destroy?.()
|
|
69
|
+
exposuresWriter = null
|
|
70
|
+
|
|
71
|
+
log.debug('OpenFeature module disabled')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = {
|
|
75
|
+
enable,
|
|
76
|
+
disable
|
|
77
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { NOOP_REASON } = require('./constants/constants')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* No-op implementation of OpenFeature provider that always returns default values.
|
|
7
|
+
* Used when the OpenFeature provider is not initialized or disabled.
|
|
8
|
+
* https://openfeature.dev/docs/reference/concepts/provider/
|
|
9
|
+
*/
|
|
10
|
+
class NoopFlaggingProvider {
|
|
11
|
+
/**
|
|
12
|
+
* @param {Object} [noopTracer] - Optional noop tracer instance
|
|
13
|
+
*/
|
|
14
|
+
constructor (noopTracer) {
|
|
15
|
+
this._tracer = noopTracer
|
|
16
|
+
this._config = {}
|
|
17
|
+
this.metadata = { name: 'NoopFlaggingProvider' }
|
|
18
|
+
this.status = 'NOT_READY'
|
|
19
|
+
this.runsOn = 'server'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} flagKey - Flag key
|
|
24
|
+
* @param {boolean} defaultValue - Default value to return
|
|
25
|
+
* @param {Object} context - Evaluation context
|
|
26
|
+
* @param {Object} logger - Logger instance
|
|
27
|
+
* @returns {Promise<{value: boolean, reason: string}>} Resolution details
|
|
28
|
+
*/
|
|
29
|
+
resolveBooleanEvaluation (flagKey, defaultValue, context, logger) {
|
|
30
|
+
return Promise.resolve({
|
|
31
|
+
value: defaultValue,
|
|
32
|
+
reason: NOOP_REASON
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} flagKey - Flag key
|
|
38
|
+
* @param {string} defaultValue - Default value to return
|
|
39
|
+
* @param {Object} context - Evaluation context
|
|
40
|
+
* @param {Object} logger - Logger instance
|
|
41
|
+
* @returns {Promise<{value: string, reason: string}>} Resolution details
|
|
42
|
+
*/
|
|
43
|
+
resolveStringEvaluation (flagKey, defaultValue, context, logger) {
|
|
44
|
+
return Promise.resolve({
|
|
45
|
+
value: defaultValue,
|
|
46
|
+
reason: NOOP_REASON
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {string} flagKey - Flag key
|
|
52
|
+
* @param {number} defaultValue - Default value to return
|
|
53
|
+
* @param {Object} context - Evaluation context
|
|
54
|
+
* @param {Object} logger - Logger instance
|
|
55
|
+
* @returns {Promise<{value: number, reason: string}>} Resolution details
|
|
56
|
+
*/
|
|
57
|
+
resolveNumberEvaluation (flagKey, defaultValue, context, logger) {
|
|
58
|
+
return Promise.resolve({
|
|
59
|
+
value: defaultValue,
|
|
60
|
+
reason: NOOP_REASON,
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {string} flagKey - Flag key
|
|
66
|
+
* @param {Object} defaultValue - Default value to return
|
|
67
|
+
* @param {Object} context - Evaluation context
|
|
68
|
+
* @param {Object} logger - Logger instance
|
|
69
|
+
* @returns {Promise<{value: Object, reason: string}>} Resolution details
|
|
70
|
+
*/
|
|
71
|
+
resolveObjectEvaluation (flagKey, defaultValue, context, logger) {
|
|
72
|
+
return Promise.resolve({
|
|
73
|
+
value: defaultValue,
|
|
74
|
+
reason: NOOP_REASON
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @returns {Object} Current configuration
|
|
80
|
+
*/
|
|
81
|
+
getConfiguration () {
|
|
82
|
+
return this._config
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {Object} config - Configuration to set
|
|
87
|
+
*/
|
|
88
|
+
setConfiguration (config) {
|
|
89
|
+
this._config = config
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @internal
|
|
94
|
+
* @param {Object} ufc - Universal Flag Configuration object
|
|
95
|
+
*/
|
|
96
|
+
_setConfiguration (ufc) {
|
|
97
|
+
this.setConfiguration(ufc)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = NoopFlaggingProvider
|