dd-trace 4.3.0 → 4.5.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 +4 -3
- package/index.d.ts +27 -0
- package/package.json +4 -4
- package/packages/datadog-instrumentations/src/aws-sdk.js +5 -0
- package/packages/datadog-instrumentations/src/cassandra-driver.js +6 -3
- package/packages/datadog-instrumentations/src/elasticsearch.js +39 -1
- package/packages/datadog-instrumentations/src/express.js +23 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
- package/packages/datadog-instrumentations/src/kafkajs.js +2 -2
- package/packages/datadog-instrumentations/src/openai.js +50 -0
- package/packages/datadog-instrumentations/src/opensearch.js +2 -1
- package/packages/datadog-instrumentations/src/passport-http.js +22 -0
- package/packages/datadog-instrumentations/src/passport-local.js +22 -0
- package/packages/datadog-instrumentations/src/passport-utils.js +36 -0
- package/packages/datadog-instrumentations/src/pg.js +17 -4
- package/packages/datadog-plugin-aws-sdk/src/base.js +3 -3
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
- package/packages/datadog-plugin-cassandra-driver/src/index.js +6 -6
- package/packages/datadog-plugin-dns/src/lookup.js +1 -1
- package/packages/datadog-plugin-elasticsearch/src/index.js +2 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +1 -1
- package/packages/datadog-plugin-graphql/src/parse.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +0 -5
- package/packages/datadog-plugin-graphql/src/validate.js +1 -1
- package/packages/datadog-plugin-grpc/src/client.js +9 -3
- package/packages/datadog-plugin-grpc/src/server.js +3 -3
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http/src/server.js +38 -34
- package/packages/datadog-plugin-http2/src/client.js +0 -5
- package/packages/datadog-plugin-http2/src/server.js +23 -23
- package/packages/datadog-plugin-kafkajs/src/consumer.js +6 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +8 -1
- package/packages/datadog-plugin-mocha/src/index.js +3 -3
- package/packages/datadog-plugin-moleculer/src/client.js +3 -3
- package/packages/datadog-plugin-moleculer/src/server.js +2 -2
- package/packages/datadog-plugin-mongodb-core/src/index.js +15 -4
- package/packages/datadog-plugin-next/src/index.js +50 -52
- package/packages/datadog-plugin-openai/src/index.js +685 -0
- package/packages/datadog-plugin-openai/src/services.js +43 -0
- package/packages/datadog-plugin-oracledb/src/index.js +3 -10
- package/packages/datadog-plugin-pg/src/index.js +3 -11
- package/packages/datadog-plugin-sharedb/src/index.js +1 -1
- package/packages/dd-trace/src/appsec/channels.js +1 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/origin-types.js +3 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +12 -2
- package/packages/dd-trace/src/appsec/index.js +20 -0
- package/packages/dd-trace/src/appsec/passport.js +110 -0
- package/packages/dd-trace/src/appsec/sdk/track_event.js +14 -5
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -4
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +45 -0
- package/packages/dd-trace/src/config.js +38 -1
- package/packages/dd-trace/src/constants.js +2 -0
- package/packages/dd-trace/src/data_streams_context.js +15 -0
- package/packages/dd-trace/src/datastreams/pathway.js +58 -0
- package/packages/dd-trace/src/datastreams/processor.js +194 -0
- package/packages/dd-trace/src/datastreams/writer.js +66 -0
- package/packages/dd-trace/src/dogstatsd.js +12 -4
- package/packages/dd-trace/src/external-logger/src/index.js +4 -0
- package/packages/dd-trace/src/opentelemetry/span.js +1 -0
- package/packages/dd-trace/src/opentracing/span.js +32 -0
- package/packages/dd-trace/src/opentracing/tracer.js +3 -1
- package/packages/dd-trace/src/plugin_manager.js +7 -2
- package/packages/dd-trace/src/plugins/client.js +1 -0
- package/packages/dd-trace/src/plugins/database.js +2 -1
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/outbound.js +59 -1
- package/packages/dd-trace/src/plugins/server.js +2 -0
- package/packages/dd-trace/src/plugins/tracing.js +5 -1
- package/packages/dd-trace/src/plugins/util/exec.js +2 -0
- package/packages/dd-trace/src/plugins/util/git.js +38 -10
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +36 -2
- package/packages/dd-trace/src/profiling/config.js +34 -7
- package/packages/dd-trace/src/proxy.js +6 -0
- package/packages/dd-trace/src/service-naming/index.js +13 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/index.js +2 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +34 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +27 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/index.js +2 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +31 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +26 -0
- package/packages/dd-trace/src/telemetry/index.js +3 -0
- package/packages/dd-trace/src/telemetry/metrics.js +281 -0
- package/packages/dd-trace/src/tracer.js +19 -1
|
@@ -13,6 +13,8 @@ const {
|
|
|
13
13
|
} = require('./tags')
|
|
14
14
|
|
|
15
15
|
const { normalizeRef } = require('./ci')
|
|
16
|
+
const log = require('../../log')
|
|
17
|
+
const { URL } = require('url')
|
|
16
18
|
|
|
17
19
|
function removeEmptyValues (tags) {
|
|
18
20
|
return Object.keys(tags).reduce((filteredTags, tag) => {
|
|
@@ -39,6 +41,37 @@ function filterSensitiveInfoFromRepository (repositoryUrl) {
|
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
|
|
44
|
+
// The regex is extracted from
|
|
45
|
+
// https://github.com/jonschlinkert/is-git-url/blob/396965ffabf2f46656c8af4c47bef1d69f09292e/index.js#L9C15-L9C87
|
|
46
|
+
function validateGitRepositoryUrl (repoUrl) {
|
|
47
|
+
return /(?:git|ssh|https?|git@[-\w.]+):(\/\/)?(.*?)(\.git)(\/?|#[-\d\w._]+?)$/.test(repoUrl)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function validateGitCommitSha (gitCommitSha) {
|
|
51
|
+
const isValidSha1 = /^[0-9a-f]{40}$/.test(gitCommitSha)
|
|
52
|
+
const isValidSha256 = /^[0-9a-f]{64}$/.test(gitCommitSha)
|
|
53
|
+
return isValidSha1 || isValidSha256
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function removeInvalidGitMetadata (metadata) {
|
|
57
|
+
return Object.keys(metadata).reduce((filteredTags, tag) => {
|
|
58
|
+
if (tag === GIT_REPOSITORY_URL) {
|
|
59
|
+
if (!validateGitRepositoryUrl(metadata[GIT_REPOSITORY_URL])) {
|
|
60
|
+
log.error('DD_GIT_REPOSITORY_URL must be a valid URL')
|
|
61
|
+
return filteredTags
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (tag === GIT_COMMIT_SHA) {
|
|
65
|
+
if (!validateGitCommitSha(metadata[GIT_COMMIT_SHA])) {
|
|
66
|
+
log.error('DD_GIT_COMMIT_SHA must be a full-length git SHA')
|
|
67
|
+
return filteredTags
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
filteredTags[tag] = metadata[tag]
|
|
71
|
+
return filteredTags
|
|
72
|
+
}, {})
|
|
73
|
+
}
|
|
74
|
+
|
|
42
75
|
function getUserProviderGitMetadata () {
|
|
43
76
|
const {
|
|
44
77
|
DD_GIT_COMMIT_SHA,
|
|
@@ -62,7 +95,7 @@ function getUserProviderGitMetadata () {
|
|
|
62
95
|
tag = normalizeRef(DD_GIT_BRANCH)
|
|
63
96
|
}
|
|
64
97
|
|
|
65
|
-
|
|
98
|
+
const metadata = removeEmptyValues({
|
|
66
99
|
[GIT_COMMIT_SHA]: DD_GIT_COMMIT_SHA,
|
|
67
100
|
[GIT_BRANCH]: branch,
|
|
68
101
|
[GIT_REPOSITORY_URL]: filterSensitiveInfoFromRepository(DD_GIT_REPOSITORY_URL),
|
|
@@ -75,6 +108,7 @@ function getUserProviderGitMetadata () {
|
|
|
75
108
|
[GIT_COMMIT_AUTHOR_EMAIL]: DD_GIT_COMMIT_AUTHOR_EMAIL,
|
|
76
109
|
[GIT_COMMIT_AUTHOR_DATE]: DD_GIT_COMMIT_AUTHOR_DATE
|
|
77
110
|
})
|
|
111
|
+
return removeInvalidGitMetadata(metadata)
|
|
78
112
|
}
|
|
79
113
|
|
|
80
|
-
module.exports = { getUserProviderGitMetadata }
|
|
114
|
+
module.exports = { getUserProviderGitMetadata, validateGitRepositoryUrl, validateGitCommitSha }
|
|
@@ -12,7 +12,7 @@ const WallProfiler = require('./profilers/wall')
|
|
|
12
12
|
const SpaceProfiler = require('./profilers/space')
|
|
13
13
|
const { oomExportStrategies, snapshotKinds } = require('./constants')
|
|
14
14
|
const { tagger } = require('./tagger')
|
|
15
|
-
const { isTrue } = require('../util')
|
|
15
|
+
const { isFalse, isTrue } = require('../util')
|
|
16
16
|
|
|
17
17
|
class Config {
|
|
18
18
|
constructor (options = {}) {
|
|
@@ -32,6 +32,8 @@ class Config {
|
|
|
32
32
|
DD_PROFILING_SOURCE_MAP,
|
|
33
33
|
DD_PROFILING_UPLOAD_PERIOD,
|
|
34
34
|
DD_PROFILING_PPROF_PREFIX,
|
|
35
|
+
DD_PROFILING_HEAP_ENABLED,
|
|
36
|
+
DD_PROFILING_WALLTIME_ENABLED,
|
|
35
37
|
DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED,
|
|
36
38
|
DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
|
|
37
39
|
DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT,
|
|
@@ -53,7 +55,7 @@ class Config {
|
|
|
53
55
|
const endpointCollection = coalesce(options.endpointCollection,
|
|
54
56
|
DD_PROFILING_ENDPOINT_COLLECTION_ENABLED, false)
|
|
55
57
|
const pprofPrefix = coalesce(options.pprofPrefix,
|
|
56
|
-
DD_PROFILING_PPROF_PREFIX)
|
|
58
|
+
DD_PROFILING_PPROF_PREFIX, '')
|
|
57
59
|
|
|
58
60
|
this.enabled = enabled
|
|
59
61
|
this.service = service
|
|
@@ -88,7 +90,7 @@ class Config {
|
|
|
88
90
|
], this)
|
|
89
91
|
|
|
90
92
|
const oomMonitoringEnabled = isTrue(coalesce(options.oomMonitoring,
|
|
91
|
-
DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED,
|
|
93
|
+
DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED, true))
|
|
92
94
|
const heapLimitExtensionSize = coalesce(options.oomHeapLimitExtensionSize,
|
|
93
95
|
Number(DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE), 0)
|
|
94
96
|
const maxHeapExtensionCount = coalesce(options.oomMaxHeapExtensionCount,
|
|
@@ -106,10 +108,9 @@ class Config {
|
|
|
106
108
|
exportCommand
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
const profilers =
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
])
|
|
111
|
+
const profilers = options.profilers
|
|
112
|
+
? options.profilers
|
|
113
|
+
: getProfilers({ DD_PROFILING_HEAP_ENABLED, DD_PROFILING_WALLTIME_ENABLED, DD_PROFILING_PROFILERS })
|
|
113
114
|
|
|
114
115
|
this.profilers = ensureProfilers(profilers, this)
|
|
115
116
|
}
|
|
@@ -117,6 +118,32 @@ class Config {
|
|
|
117
118
|
|
|
118
119
|
module.exports = { Config }
|
|
119
120
|
|
|
121
|
+
function getProfilers ({ DD_PROFILING_HEAP_ENABLED, DD_PROFILING_WALLTIME_ENABLED, DD_PROFILING_PROFILERS }) {
|
|
122
|
+
// First consider "legacy" DD_PROFILING_PROFILERS env variable, defaulting to wall + space
|
|
123
|
+
// Use a Set to avoid duplicates
|
|
124
|
+
const profilers = new Set(coalesce(DD_PROFILING_PROFILERS, 'wall,space').split(','))
|
|
125
|
+
|
|
126
|
+
// Add/remove wall depending on the value of DD_PROFILING_WALLTIME_ENABLED
|
|
127
|
+
if (DD_PROFILING_WALLTIME_ENABLED != null) {
|
|
128
|
+
if (isTrue(DD_PROFILING_WALLTIME_ENABLED)) {
|
|
129
|
+
profilers.add('wall')
|
|
130
|
+
} else if (isFalse(DD_PROFILING_WALLTIME_ENABLED)) {
|
|
131
|
+
profilers.delete('wall')
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Add/remove wall depending on the value of DD_PROFILING_HEAP_ENABLED
|
|
136
|
+
if (DD_PROFILING_HEAP_ENABLED != null) {
|
|
137
|
+
if (isTrue(DD_PROFILING_HEAP_ENABLED)) {
|
|
138
|
+
profilers.add('space')
|
|
139
|
+
} else if (isFalse(DD_PROFILING_HEAP_ENABLED)) {
|
|
140
|
+
profilers.delete('space')
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return [...profilers]
|
|
145
|
+
}
|
|
146
|
+
|
|
120
147
|
function getExportStrategy (name, options) {
|
|
121
148
|
const strategy = Object.values(oomExportStrategies).find(value => value === name)
|
|
122
149
|
if (strategy === undefined) {
|
|
@@ -64,6 +64,12 @@ class Tracer extends NoopProxy {
|
|
|
64
64
|
this._pluginManager.configure(config)
|
|
65
65
|
setStartupLogPluginManager(this._pluginManager)
|
|
66
66
|
telemetry.start(config, this._pluginManager)
|
|
67
|
+
|
|
68
|
+
if (config.isManualApiEnabled) {
|
|
69
|
+
const TestApiManualPlugin = require('./ci-visibility/test-api-manual/test-api-manual-plugin')
|
|
70
|
+
this._testApiManualPlugin = new TestApiManualPlugin(this)
|
|
71
|
+
this._testApiManualPlugin.configure({ ...config, enabled: true })
|
|
72
|
+
}
|
|
67
73
|
}
|
|
68
74
|
} catch (e) {
|
|
69
75
|
log.error(e)
|
|
@@ -3,7 +3,7 @@ const { schemaDefinitions } = require('./schemas')
|
|
|
3
3
|
class SchemaManager {
|
|
4
4
|
constructor () {
|
|
5
5
|
this.schemas = schemaDefinitions
|
|
6
|
-
this.config = { spanAttributeSchema: 'v0' }
|
|
6
|
+
this.config = { spanAttributeSchema: 'v0', traceRemoveIntegrationServiceNamesEnabled: false }
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
get schema () {
|
|
@@ -14,6 +14,10 @@ class SchemaManager {
|
|
|
14
14
|
return this.config.spanAttributeSchema
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
get shouldUseConsistentServiceNaming () {
|
|
18
|
+
return this.config.traceRemoveIntegrationServiceNamesEnabled && this.version === 'v0'
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
opName (type, kind, plugin, ...opNameArgs) {
|
|
18
22
|
return this.schema.getOpName(type, kind, plugin, ...opNameArgs)
|
|
19
23
|
}
|
|
@@ -22,6 +26,14 @@ class SchemaManager {
|
|
|
22
26
|
return this.schema.getServiceName(type, kind, plugin, this.config.service, ...serviceNameArgs)
|
|
23
27
|
}
|
|
24
28
|
|
|
29
|
+
shortCircuitServiceName (pluginConfig, ...args) {
|
|
30
|
+
// We're short-circuiting, so we do not obey custom service functions
|
|
31
|
+
if (typeof pluginConfig.service === 'function') {
|
|
32
|
+
return this.config.service
|
|
33
|
+
}
|
|
34
|
+
return pluginConfig.service || this.config.service
|
|
35
|
+
}
|
|
36
|
+
|
|
25
37
|
configure (config = {}) {
|
|
26
38
|
this.config = config
|
|
27
39
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const SchemaDefinition = require('../definition')
|
|
2
2
|
const messaging = require('./messaging')
|
|
3
3
|
const storage = require('./storage')
|
|
4
|
+
const web = require('./web')
|
|
4
5
|
|
|
5
|
-
module.exports = new SchemaDefinition({ messaging, storage })
|
|
6
|
+
module.exports = new SchemaDefinition({ messaging, storage, web })
|
|
@@ -16,7 +16,16 @@ function mysqlServiceName (service, config, dbConfig, system) {
|
|
|
16
16
|
if (typeof config.service === 'function') {
|
|
17
17
|
return config.service(dbConfig)
|
|
18
18
|
}
|
|
19
|
-
return config.service
|
|
19
|
+
return config.service || fromSystem(service, system)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function withSuffixFunction (suffix) {
|
|
23
|
+
return (service, config, params) => {
|
|
24
|
+
if (typeof config.service === 'function') {
|
|
25
|
+
return config.service(params)
|
|
26
|
+
}
|
|
27
|
+
return config.service || `${service}-${suffix}`
|
|
28
|
+
}
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
const redisConfig = {
|
|
@@ -28,6 +37,14 @@ const redisConfig = {
|
|
|
28
37
|
|
|
29
38
|
const storage = {
|
|
30
39
|
client: {
|
|
40
|
+
'cassandra-driver': {
|
|
41
|
+
opName: () => 'cassandra.query',
|
|
42
|
+
serviceName: (service, config, system) => config.service || fromSystem(service, system)
|
|
43
|
+
},
|
|
44
|
+
elasticsearch: {
|
|
45
|
+
opName: () => 'elasticsearch.query',
|
|
46
|
+
serviceName: (service, config) => config.service || `${service}-elasticsearch`
|
|
47
|
+
},
|
|
31
48
|
ioredis: redisConfig,
|
|
32
49
|
mariadb: {
|
|
33
50
|
opName: () => 'mariadb.query',
|
|
@@ -37,6 +54,10 @@ const storage = {
|
|
|
37
54
|
opName: () => 'memcached.command',
|
|
38
55
|
serviceName: (service, config, system) => config.service || fromSystem(service, system)
|
|
39
56
|
},
|
|
57
|
+
'mongodb-core': {
|
|
58
|
+
opName: () => 'mongodb.query',
|
|
59
|
+
serviceName: (service, config) => config.service || `${service}-mongodb`
|
|
60
|
+
},
|
|
40
61
|
mysql: {
|
|
41
62
|
opName: () => 'mysql.query',
|
|
42
63
|
serviceName: mysqlServiceName
|
|
@@ -45,6 +66,18 @@ const storage = {
|
|
|
45
66
|
opName: () => 'mysql.query',
|
|
46
67
|
serviceName: mysqlServiceName
|
|
47
68
|
},
|
|
69
|
+
opensearch: {
|
|
70
|
+
opName: () => 'opensearch.query',
|
|
71
|
+
serviceName: (service, config) => config.service || `${service}-opensearch`
|
|
72
|
+
},
|
|
73
|
+
oracledb: {
|
|
74
|
+
opName: () => 'oracle.query',
|
|
75
|
+
serviceName: withSuffixFunction('oracle')
|
|
76
|
+
},
|
|
77
|
+
pg: {
|
|
78
|
+
opName: () => 'pg.query',
|
|
79
|
+
serviceName: withSuffixFunction('postgres')
|
|
80
|
+
},
|
|
48
81
|
redis: redisConfig,
|
|
49
82
|
tedious: {
|
|
50
83
|
opName: () => 'tedious.request',
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const { identityService } = require('../util')
|
|
2
|
+
const { DD_MAJOR } = require('../../../../../../version')
|
|
3
|
+
|
|
4
|
+
const web = {
|
|
5
|
+
client: {
|
|
6
|
+
grpc: {
|
|
7
|
+
opName: () => DD_MAJOR <= 2 ? 'grpc.request' : 'grpc.client',
|
|
8
|
+
serviceName: identityService
|
|
9
|
+
},
|
|
10
|
+
moleculer: {
|
|
11
|
+
opName: () => 'moleculer.call',
|
|
12
|
+
serviceName: identityService
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
server: {
|
|
16
|
+
grpc: {
|
|
17
|
+
opName: () => DD_MAJOR <= 2 ? 'grpc.request' : 'grpc.server',
|
|
18
|
+
serviceName: identityService
|
|
19
|
+
},
|
|
20
|
+
moleculer: {
|
|
21
|
+
opName: () => 'moleculer.action',
|
|
22
|
+
serviceName: identityService
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = web
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const SchemaDefinition = require('../definition')
|
|
2
2
|
const messaging = require('./messaging')
|
|
3
3
|
const storage = require('./storage')
|
|
4
|
+
const web = require('./web')
|
|
4
5
|
|
|
5
|
-
module.exports = new SchemaDefinition({ messaging, storage })
|
|
6
|
+
module.exports = new SchemaDefinition({ messaging, storage, web })
|
|
@@ -14,8 +14,23 @@ const mySQLNaming = {
|
|
|
14
14
|
serviceName: identityService
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
function withFunction (service, config, params) {
|
|
18
|
+
if (typeof config.service === 'function') {
|
|
19
|
+
return config.service(params)
|
|
20
|
+
}
|
|
21
|
+
return configWithFallback(service, config)
|
|
22
|
+
}
|
|
23
|
+
|
|
17
24
|
const storage = {
|
|
18
25
|
client: {
|
|
26
|
+
'cassandra-driver': {
|
|
27
|
+
opName: () => 'cassandra.query',
|
|
28
|
+
serviceName: configWithFallback
|
|
29
|
+
},
|
|
30
|
+
elasticsearch: {
|
|
31
|
+
opName: () => 'elasticsearch.query',
|
|
32
|
+
serviceName: configWithFallback
|
|
33
|
+
},
|
|
19
34
|
ioredis: redisNaming,
|
|
20
35
|
mariadb: {
|
|
21
36
|
opName: () => 'mariadb.query',
|
|
@@ -25,8 +40,24 @@ const storage = {
|
|
|
25
40
|
opName: () => 'memcached.command',
|
|
26
41
|
serviceName: configWithFallback
|
|
27
42
|
},
|
|
43
|
+
'mongodb-core': {
|
|
44
|
+
opName: () => 'mongodb.query',
|
|
45
|
+
serviceName: configWithFallback
|
|
46
|
+
},
|
|
28
47
|
mysql: mySQLNaming,
|
|
29
48
|
mysql2: mySQLNaming,
|
|
49
|
+
opensearch: {
|
|
50
|
+
opName: () => 'opensearch.query',
|
|
51
|
+
serviceName: configWithFallback
|
|
52
|
+
},
|
|
53
|
+
oracledb: {
|
|
54
|
+
opName: () => 'oracle.query',
|
|
55
|
+
serviceName: withFunction
|
|
56
|
+
},
|
|
57
|
+
pg: {
|
|
58
|
+
opName: () => 'postgresql.query',
|
|
59
|
+
serviceName: withFunction
|
|
60
|
+
},
|
|
30
61
|
redis: redisNaming,
|
|
31
62
|
tedious: {
|
|
32
63
|
opName: () => 'mssql.query',
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const { identityService } = require('../util')
|
|
2
|
+
|
|
3
|
+
const web = {
|
|
4
|
+
client: {
|
|
5
|
+
grpc: {
|
|
6
|
+
opName: () => 'grpc.client.request',
|
|
7
|
+
serviceName: identityService
|
|
8
|
+
},
|
|
9
|
+
moleculer: {
|
|
10
|
+
opName: () => 'moleculer.client.request',
|
|
11
|
+
serviceName: identityService
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
server: {
|
|
15
|
+
grpc: {
|
|
16
|
+
opName: () => 'grpc.server.request',
|
|
17
|
+
serviceName: identityService
|
|
18
|
+
},
|
|
19
|
+
moleculer: {
|
|
20
|
+
opName: () => 'moleculer.server.request',
|
|
21
|
+
serviceName: identityService
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = web
|
|
@@ -6,6 +6,8 @@ const os = require('os')
|
|
|
6
6
|
const dependencies = require('./dependencies')
|
|
7
7
|
const { sendData } = require('./send-data')
|
|
8
8
|
|
|
9
|
+
const { manager: metricsManager } = require('./metrics')
|
|
10
|
+
|
|
9
11
|
const telemetryStartChannel = dc.channel('datadog:telemetry:start')
|
|
10
12
|
const telemetryStopChannel = dc.channel('datadog:telemetry:stop')
|
|
11
13
|
|
|
@@ -121,6 +123,7 @@ function start (aConfig, thePluginManager) {
|
|
|
121
123
|
dependencies.start(config, application, host)
|
|
122
124
|
sendData(config, application, host, 'app-started', appStarted())
|
|
123
125
|
interval = setInterval(() => {
|
|
126
|
+
metricsManager.send(config, application, host)
|
|
124
127
|
sendData(config, application, host, 'app-heartbeat')
|
|
125
128
|
}, heartbeatInterval)
|
|
126
129
|
interval.unref()
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { version } = require('../../../../package.json')
|
|
4
|
+
|
|
5
|
+
const { sendData } = require('./send-data')
|
|
6
|
+
|
|
7
|
+
function getId (type, namespace, name, tags) {
|
|
8
|
+
return `${type}:${namespace}.${name}:${tagArray(tags).sort().join(',')}`
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function tagArray (tags = {}) {
|
|
12
|
+
if (Array.isArray(tags)) return tags
|
|
13
|
+
const list = []
|
|
14
|
+
for (const [key, value] of Object.entries(tags)) {
|
|
15
|
+
list.push(`${key}:${value}`.toLowerCase())
|
|
16
|
+
}
|
|
17
|
+
return list
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function now () {
|
|
21
|
+
return Date.now() / 1e3
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function mapToJsonArray (map) {
|
|
25
|
+
return Array.from(map.values()).map(v => v.toJSON())
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class Metric {
|
|
29
|
+
constructor (namespace, metric, common, tags) {
|
|
30
|
+
this.namespace = namespace.toString()
|
|
31
|
+
this.metric = common ? metric : `nodejs.${metric}`
|
|
32
|
+
this.tags = tagArray(tags)
|
|
33
|
+
if (common) {
|
|
34
|
+
this.tags.push('lib_language:nodejs')
|
|
35
|
+
this.tags.push(`version:${process.version}`)
|
|
36
|
+
} else {
|
|
37
|
+
this.tags.push(`lib_version:${version}`)
|
|
38
|
+
}
|
|
39
|
+
this.common = common
|
|
40
|
+
|
|
41
|
+
this.points = []
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
toString () {
|
|
45
|
+
const { namespace, metric } = this
|
|
46
|
+
return `${namespace}.${metric}`
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
reset () {
|
|
50
|
+
this.points = []
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
track () {
|
|
54
|
+
throw new Error('not implemented')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
toJSON () {
|
|
58
|
+
const { metric, points, interval, type, tags, common } = this
|
|
59
|
+
return {
|
|
60
|
+
metric,
|
|
61
|
+
points,
|
|
62
|
+
interval,
|
|
63
|
+
type,
|
|
64
|
+
tags,
|
|
65
|
+
common
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
class CountMetric extends Metric {
|
|
71
|
+
get type () {
|
|
72
|
+
return 'count'
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
inc (value) {
|
|
76
|
+
return this.track(value)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
dec (value = -1) {
|
|
80
|
+
return this.track(value)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
track (value = 1) {
|
|
84
|
+
if (this.points.length) {
|
|
85
|
+
this.points[0][1] += value
|
|
86
|
+
} else {
|
|
87
|
+
this.points.push([now(), value])
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
class DistributionMetric extends Metric {
|
|
93
|
+
get type () {
|
|
94
|
+
return 'distribution'
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
track (value = 1) {
|
|
98
|
+
this.points.push(value)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
toJSON () {
|
|
102
|
+
const { metric, points, tags, common } = this
|
|
103
|
+
return {
|
|
104
|
+
metric,
|
|
105
|
+
points,
|
|
106
|
+
common,
|
|
107
|
+
tags
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
class GaugeMetric extends Metric {
|
|
113
|
+
get type () {
|
|
114
|
+
return 'gauge'
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
mark (value) {
|
|
118
|
+
return this.track(value)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
track (value = 1) {
|
|
122
|
+
this.points.push([now(), value])
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
class RateMetric extends Metric {
|
|
127
|
+
constructor (namespace, metric, common, tags, interval) {
|
|
128
|
+
super(namespace, metric, common, tags)
|
|
129
|
+
|
|
130
|
+
this.interval = interval
|
|
131
|
+
this.rate = 0
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
get type () {
|
|
135
|
+
return 'rate'
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
reset () {
|
|
139
|
+
super.reset()
|
|
140
|
+
this.rate = 0
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
track (value = 1) {
|
|
144
|
+
this.rate += value
|
|
145
|
+
const rate = this.interval ? (this.rate / this.interval) : 0.0
|
|
146
|
+
this.points = [[now(), rate]]
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const metricsTypes = {
|
|
151
|
+
count: CountMetric,
|
|
152
|
+
distribution: DistributionMetric,
|
|
153
|
+
gauge: GaugeMetric,
|
|
154
|
+
rate: RateMetric
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
class MetricsCollection extends Map {
|
|
158
|
+
constructor (namespace) {
|
|
159
|
+
super()
|
|
160
|
+
this.namespace = namespace
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
reset () {
|
|
164
|
+
for (const metric of this.values()) {
|
|
165
|
+
metric.reset()
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
toString () {
|
|
170
|
+
return this.namespace
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
toJSON () {
|
|
174
|
+
if (!this.size) return
|
|
175
|
+
const { namespace } = this
|
|
176
|
+
return {
|
|
177
|
+
namespace,
|
|
178
|
+
series: mapToJsonArray(this)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function getMetric (collection, type, name, tags, interval) {
|
|
184
|
+
const metricId = getId(type, collection, name, tags)
|
|
185
|
+
|
|
186
|
+
let metric = collection.get(metricId)
|
|
187
|
+
if (metric) return metric
|
|
188
|
+
|
|
189
|
+
const Factory = metricsTypes[type]
|
|
190
|
+
if (!Factory) {
|
|
191
|
+
throw new Error(`Unknown metric type ${type}`)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
metric = new Factory(collection, name, true, tags, interval)
|
|
195
|
+
collection.set(metricId, metric)
|
|
196
|
+
|
|
197
|
+
return metric
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
class Namespace {
|
|
201
|
+
constructor (namespace) {
|
|
202
|
+
this.distributions = new MetricsCollection(namespace)
|
|
203
|
+
this.metrics = new MetricsCollection(namespace)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
reset () {
|
|
207
|
+
this.metrics.reset()
|
|
208
|
+
this.distributions.reset()
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
count (name, tags) {
|
|
212
|
+
return getMetric(this.metrics, 'count', name, tags)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
gauge (name, tags) {
|
|
216
|
+
return getMetric(this.metrics, 'gauge', name, tags)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
rate (name, interval, tags) {
|
|
220
|
+
return getMetric(this.metrics, 'rate', name, tags, interval)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
distribution (name, tags) {
|
|
224
|
+
return getMetric(this.distributions, 'distribution', name, tags)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
toJSON () {
|
|
228
|
+
const { distributions, metrics } = this
|
|
229
|
+
return {
|
|
230
|
+
distributions: distributions.toJSON(),
|
|
231
|
+
metrics: metrics.toJSON()
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
class NamespaceManager extends Map {
|
|
237
|
+
namespace (name) {
|
|
238
|
+
let ns = this.get(name)
|
|
239
|
+
if (ns) return ns
|
|
240
|
+
|
|
241
|
+
ns = new Namespace(name)
|
|
242
|
+
this.set(name, ns)
|
|
243
|
+
return ns
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
toJSON () {
|
|
247
|
+
return mapToJsonArray(this)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
send (config, application, host) {
|
|
251
|
+
for (const namespace of this.values()) {
|
|
252
|
+
const { metrics, distributions } = namespace.toJSON()
|
|
253
|
+
|
|
254
|
+
if (metrics) {
|
|
255
|
+
sendData(config, application, host, 'generate-metrics', metrics)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (distributions) {
|
|
259
|
+
sendData(config, application, host, 'distributions', distributions)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// TODO: This could also be clear() but then it'd have to rebuild all
|
|
263
|
+
// metric instances on every send. This may be desirable if we want tags
|
|
264
|
+
// with high cardinality and variability over time.
|
|
265
|
+
namespace.reset()
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const manager = new NamespaceManager()
|
|
271
|
+
|
|
272
|
+
module.exports = {
|
|
273
|
+
CountMetric,
|
|
274
|
+
DistributionMetric,
|
|
275
|
+
GaugeMetric,
|
|
276
|
+
RateMetric,
|
|
277
|
+
MetricsCollection,
|
|
278
|
+
Namespace,
|
|
279
|
+
NamespaceManager,
|
|
280
|
+
manager
|
|
281
|
+
}
|