dd-trace 5.0.0-pre-471478b → 5.0.0-pre-2fed3ce
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/package.json +1 -1
- package/packages/datadog-instrumentations/src/cassandra-driver.js +6 -3
- package/packages/datadog-instrumentations/src/elasticsearch.js +2 -2
- package/packages/datadog-instrumentations/src/http2/client.js +25 -26
- package/packages/datadog-instrumentations/src/kafkajs.js +11 -2
- package/packages/datadog-instrumentations/src/redis.js +48 -5
- package/packages/datadog-plugin-cypress/src/plugin.js +1 -1
- package/packages/datadog-plugin-http2/src/client.js +46 -29
- package/packages/datadog-plugin-kafkajs/src/producer.js +6 -1
- package/packages/datadog-plugin-openai/src/services.js +14 -10
- package/packages/datadog-plugin-router/src/index.js +1 -1
- package/packages/dd-trace/src/dogstatsd.js +14 -1
- package/packages/dd-trace/src/metrics.js +2 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
- package/packages/dd-trace/src/plugins/outbound.js +5 -1
- package/packages/dd-trace/src/plugins/plugin.js +28 -0
- package/packages/dd-trace/src/plugins/tracing.js +33 -14
- package/packages/dd-trace/src/plugins/util/ci.js +1 -1
- package/packages/dd-trace/src/plugins/util/test.js +55 -11
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -22
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +3 -2
- package/packages/dd-trace/src/telemetry/metrics.js +76 -20
- package/packages/diagnostics_channel/src/index.js +64 -0
package/package.json
CHANGED
|
@@ -28,7 +28,8 @@ addHook({ name: 'cassandra-driver', versions: ['>=3.0.0'] }, cassandra => {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
return asyncResource.runInAsyncScope(() => {
|
|
31
|
-
|
|
31
|
+
const contactPoints = this.options && this.options.contactPoints
|
|
32
|
+
startCh.publish({ keyspace: this.keyspace, query: queries, contactPoints })
|
|
32
33
|
try {
|
|
33
34
|
const res = batch.apply(this, arguments)
|
|
34
35
|
if (typeof res === 'function' || !res) {
|
|
@@ -56,7 +57,8 @@ addHook({ name: 'cassandra-driver', versions: ['>=4.4'] }, cassandra => {
|
|
|
56
57
|
}
|
|
57
58
|
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
58
59
|
return asyncResource.runInAsyncScope(() => {
|
|
59
|
-
|
|
60
|
+
const contactPoints = this.options && this.options.contactPoints
|
|
61
|
+
startCh.publish({ keyspace: this.keyspace, query, contactPoints })
|
|
60
62
|
const promise = _execute.apply(this, arguments)
|
|
61
63
|
|
|
62
64
|
const promiseAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
@@ -88,7 +90,8 @@ addHook({ name: 'cassandra-driver', versions: ['3 - 4.3'] }, cassandra => {
|
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
return asyncResource.runInAsyncScope(() => {
|
|
91
|
-
|
|
93
|
+
const contactPoints = this.options && this.options.contactPoints
|
|
94
|
+
startCh.publish({ keyspace: this.keyspace, query, contactPoints })
|
|
92
95
|
|
|
93
96
|
const lastIndex = arguments.length - 1
|
|
94
97
|
let cb = arguments[lastIndex]
|
|
@@ -34,7 +34,7 @@ function createWrapGetConnection (name) {
|
|
|
34
34
|
return function wrapRequest (request) {
|
|
35
35
|
return function () {
|
|
36
36
|
const connection = request.apply(this, arguments)
|
|
37
|
-
if (connectCh.hasSubscribers && connection
|
|
37
|
+
if (connectCh.hasSubscribers && connection && connection.url) {
|
|
38
38
|
connectCh.publish(connection.url)
|
|
39
39
|
}
|
|
40
40
|
return connection
|
|
@@ -49,7 +49,7 @@ function createWrapSelect () {
|
|
|
49
49
|
if (arguments.length === 1) {
|
|
50
50
|
const cb = arguments[0]
|
|
51
51
|
arguments[0] = function (err, connection) {
|
|
52
|
-
if (connectCh.hasSubscribers && connection
|
|
52
|
+
if (connectCh.hasSubscribers && connection && connection.host) {
|
|
53
53
|
connectCh.publish({ hostname: connection.host.host, port: connection.host.port })
|
|
54
54
|
}
|
|
55
55
|
cb(err, connection)
|
|
@@ -1,32 +1,27 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const shimmer = require('../../../datadog-shimmer')
|
|
4
|
-
const { addHook, channel
|
|
4
|
+
const { addHook, channel } = require('../helpers/instrument')
|
|
5
5
|
|
|
6
6
|
const connectChannel = channel('apm:http2:client:connect:start')
|
|
7
7
|
const startChannel = channel('apm:http2:client:request:start')
|
|
8
|
-
const
|
|
8
|
+
const endChannel = channel('apm:http2:client:request:end')
|
|
9
|
+
const asyncStartChannel = channel('apm:http2:client:request:asyncStart')
|
|
10
|
+
const asyncEndChannel = channel('apm:http2:client:request:asyncEnd')
|
|
9
11
|
const errorChannel = channel('apm:http2:client:request:error')
|
|
10
|
-
const responseChannel = channel('apm:http2:client:response')
|
|
11
12
|
|
|
12
|
-
function createWrapEmit (
|
|
13
|
+
function createWrapEmit (ctx) {
|
|
13
14
|
return function wrapEmit (emit) {
|
|
14
15
|
return function (event, arg1) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
case 'response':
|
|
18
|
-
responseChannel.publish(arg1)
|
|
19
|
-
break
|
|
20
|
-
case 'error':
|
|
21
|
-
errorChannel.publish(arg1)
|
|
22
|
-
case 'close': // eslint-disable-line no-fallthrough
|
|
23
|
-
finishChannel.publish()
|
|
24
|
-
break
|
|
25
|
-
}
|
|
26
|
-
})
|
|
16
|
+
ctx.eventName = event
|
|
17
|
+
ctx.eventData = arg1
|
|
27
18
|
|
|
28
|
-
return
|
|
29
|
-
|
|
19
|
+
return asyncStartChannel.runStores(ctx, () => {
|
|
20
|
+
try {
|
|
21
|
+
return emit.apply(this, arguments)
|
|
22
|
+
} finally {
|
|
23
|
+
asyncEndChannel.publish(ctx)
|
|
24
|
+
}
|
|
30
25
|
})
|
|
31
26
|
}
|
|
32
27
|
}
|
|
@@ -35,17 +30,21 @@ function createWrapEmit (requestResource, parentResource) {
|
|
|
35
30
|
function createWrapRequest (authority, options) {
|
|
36
31
|
return function wrapRequest (request) {
|
|
37
32
|
return function (headers) {
|
|
38
|
-
const
|
|
39
|
-
const requestResource = new AsyncResource('bound-anonymous-fn')
|
|
33
|
+
const ctx = { headers, authority, options }
|
|
40
34
|
|
|
41
|
-
return
|
|
42
|
-
|
|
35
|
+
return startChannel.runStores(ctx, () => {
|
|
36
|
+
try {
|
|
37
|
+
const req = request.apply(this, arguments)
|
|
43
38
|
|
|
44
|
-
|
|
39
|
+
shimmer.wrap(req, 'emit', createWrapEmit(ctx))
|
|
45
40
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
return req
|
|
42
|
+
} catch (e) {
|
|
43
|
+
ctx.error = e
|
|
44
|
+
errorChannel.publish(ctx)
|
|
45
|
+
} finally {
|
|
46
|
+
endChannel.publish(ctx)
|
|
47
|
+
}
|
|
49
48
|
})
|
|
50
49
|
}
|
|
51
50
|
}
|
|
@@ -16,10 +16,19 @@ const consumerFinishCh = channel('apm:kafkajs:consume:finish')
|
|
|
16
16
|
const consumerErrorCh = channel('apm:kafkajs:consume:error')
|
|
17
17
|
|
|
18
18
|
addHook({ name: 'kafkajs', versions: ['>=1.4'] }, (obj) => {
|
|
19
|
-
|
|
19
|
+
class Kafka extends obj.Kafka {
|
|
20
|
+
constructor (options) {
|
|
21
|
+
super(options)
|
|
22
|
+
this._brokers = (options.brokers && typeof options.brokers !== 'function')
|
|
23
|
+
? options.brokers.join(',') : undefined
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
obj.Kafka = Kafka
|
|
27
|
+
|
|
20
28
|
shimmer.wrap(Kafka.prototype, 'producer', createProducer => function () {
|
|
21
29
|
const producer = createProducer.apply(this, arguments)
|
|
22
30
|
const send = producer.send
|
|
31
|
+
const bootstrapServers = this._brokers
|
|
23
32
|
|
|
24
33
|
producer.send = function () {
|
|
25
34
|
const innerAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
@@ -36,7 +45,7 @@ addHook({ name: 'kafkajs', versions: ['>=1.4'] }, (obj) => {
|
|
|
36
45
|
message.headers = message.headers || {}
|
|
37
46
|
}
|
|
38
47
|
}
|
|
39
|
-
producerStartCh.publish({ topic, messages })
|
|
48
|
+
producerStartCh.publish({ topic, messages, bootstrapServers })
|
|
40
49
|
|
|
41
50
|
const result = send.apply(this, arguments)
|
|
42
51
|
|
|
@@ -11,6 +11,8 @@ const startCh = channel('apm:redis:command:start')
|
|
|
11
11
|
const finishCh = channel('apm:redis:command:finish')
|
|
12
12
|
const errorCh = channel('apm:redis:command:error')
|
|
13
13
|
|
|
14
|
+
let createClientUrl
|
|
15
|
+
|
|
14
16
|
function wrapAddCommand (addCommand) {
|
|
15
17
|
return function (command) {
|
|
16
18
|
if (!startCh.hasSubscribers) {
|
|
@@ -22,7 +24,7 @@ function wrapAddCommand (addCommand) {
|
|
|
22
24
|
|
|
23
25
|
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
24
26
|
return asyncResource.runInAsyncScope(() => {
|
|
25
|
-
start(this, name, args)
|
|
27
|
+
start(this, name, args, this._url)
|
|
26
28
|
|
|
27
29
|
const res = addCommand.apply(this, arguments)
|
|
28
30
|
const onResolve = asyncResource.bind(() => finish(finishCh, errorCh))
|
|
@@ -35,12 +37,53 @@ function wrapAddCommand (addCommand) {
|
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
function wrapCommandQueueClass (cls) {
|
|
41
|
+
const ret = class RedisCommandQueue extends cls {
|
|
42
|
+
constructor () {
|
|
43
|
+
super(arguments)
|
|
44
|
+
if (createClientUrl) {
|
|
45
|
+
try {
|
|
46
|
+
const parsed = new URL(createClientUrl)
|
|
47
|
+
if (parsed) {
|
|
48
|
+
this._url = { host: parsed.hostname, port: +parsed.port || 6379 }
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
// ignore
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
this._url = this._url || { host: 'localhost', port: 6379 }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return ret
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function wrapCreateClient (request) {
|
|
61
|
+
return function (opts) {
|
|
62
|
+
createClientUrl = opts && opts.url
|
|
63
|
+
const ret = request.apply(this, arguments)
|
|
64
|
+
createClientUrl = undefined
|
|
65
|
+
return ret
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
addHook({ name: '@node-redis/client', file: 'dist/lib/client/commands-queue.js', versions: ['>=1'] }, redis => {
|
|
70
|
+
redis.default = wrapCommandQueueClass(redis.default)
|
|
39
71
|
shimmer.wrap(redis.default.prototype, 'addCommand', wrapAddCommand)
|
|
40
72
|
return redis
|
|
41
73
|
})
|
|
42
74
|
|
|
43
|
-
addHook({ name: '@node-redis/client', file: 'dist/lib/client/
|
|
75
|
+
addHook({ name: '@node-redis/client', file: 'dist/lib/client/index.js', versions: ['>=1'] }, redis => {
|
|
76
|
+
shimmer.wrap(redis.default, 'create', wrapCreateClient)
|
|
77
|
+
return redis
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
addHook({ name: '@redis/client', file: 'dist/lib/client/index.js', versions: ['>=1.1'] }, redis => {
|
|
81
|
+
shimmer.wrap(redis.default, 'create', wrapCreateClient)
|
|
82
|
+
return redis
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
addHook({ name: '@redis/client', file: 'dist/lib/client/commands-queue.js', versions: ['>=1.1'] }, redis => {
|
|
86
|
+
redis.default = wrapCommandQueueClass(redis.default)
|
|
44
87
|
shimmer.wrap(redis.default.prototype, 'addCommand', wrapAddCommand)
|
|
45
88
|
return redis
|
|
46
89
|
})
|
|
@@ -106,9 +149,9 @@ addHook({ name: 'redis', versions: ['>=0.12 <2.6'] }, redis => {
|
|
|
106
149
|
return redis
|
|
107
150
|
})
|
|
108
151
|
|
|
109
|
-
function start (client, command, args) {
|
|
152
|
+
function start (client, command, args, url = {}) {
|
|
110
153
|
const db = client.selected_db
|
|
111
|
-
const connectionOptions = client.connection_options || client.connection_option || client.connectionOption ||
|
|
154
|
+
const connectionOptions = client.connection_options || client.connection_option || client.connectionOption || url
|
|
112
155
|
startCh.publish({ db, command, args, connectionOptions })
|
|
113
156
|
}
|
|
114
157
|
|
|
@@ -37,7 +37,7 @@ const CYPRESS_STATUS_TO_TEST_STATUS = {
|
|
|
37
37
|
function getTestSpanMetadata (tracer, testName, testSuite, cypressConfig) {
|
|
38
38
|
const childOf = getTestParentSpan(tracer)
|
|
39
39
|
|
|
40
|
-
const commonTags = getTestCommonTags(testName, testSuite, cypressConfig.version)
|
|
40
|
+
const commonTags = getTestCommonTags(testName, testSuite, cypressConfig.version, TEST_FRAMEWORK_NAME)
|
|
41
41
|
|
|
42
42
|
return {
|
|
43
43
|
childOf,
|
|
@@ -8,7 +8,6 @@ const log = require('../../dd-trace/src/log')
|
|
|
8
8
|
const tags = require('../../../ext/tags')
|
|
9
9
|
const kinds = require('../../../ext/kinds')
|
|
10
10
|
const formats = require('../../../ext/formats')
|
|
11
|
-
const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
|
|
12
11
|
const { COMPONENT, CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
13
12
|
const urlFilter = require('../../dd-trace/src/plugins/util/urlfilter')
|
|
14
13
|
|
|
@@ -25,32 +24,11 @@ const HTTP2_HEADER_STATUS = ':status'
|
|
|
25
24
|
const HTTP2_METHOD_GET = 'GET'
|
|
26
25
|
|
|
27
26
|
class Http2ClientPlugin extends ClientPlugin {
|
|
28
|
-
static get id () {
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
constructor (...args) {
|
|
33
|
-
super(...args)
|
|
34
|
-
|
|
35
|
-
this.addSub('apm:http2:client:response', (headers) => {
|
|
36
|
-
const span = storage.getStore().span
|
|
37
|
-
const status = headers && headers[HTTP2_HEADER_STATUS]
|
|
38
|
-
|
|
39
|
-
span.setTag(HTTP_STATUS_CODE, status)
|
|
40
|
-
|
|
41
|
-
if (!this.config.validateStatus(status)) {
|
|
42
|
-
this.addError()
|
|
43
|
-
}
|
|
27
|
+
static get id () { return 'http2' }
|
|
28
|
+
static get prefix () { return `apm:http2:client:request` }
|
|
44
29
|
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
addTraceSub (eventName, handler) {
|
|
50
|
-
this.addSub(`apm:${this.constructor.id}:client:${this.operation}:${eventName}`, handler)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
start ({ authority, options, headers = {} }) {
|
|
30
|
+
bindStart (message) {
|
|
31
|
+
const { authority, options, headers = {} } = message
|
|
54
32
|
const sessionDetails = extractSessionDetails(authority, options)
|
|
55
33
|
const path = headers[HTTP2_HEADER_PATH] || '/'
|
|
56
34
|
const pathname = path.split(/[?#]/)[0]
|
|
@@ -75,7 +53,7 @@ class Http2ClientPlugin extends ClientPlugin {
|
|
|
75
53
|
metrics: {
|
|
76
54
|
[CLIENT_PORT_KEY]: parseInt(sessionDetails.port)
|
|
77
55
|
}
|
|
78
|
-
})
|
|
56
|
+
}, false)
|
|
79
57
|
|
|
80
58
|
// TODO: Figure out a better way to do this for any span.
|
|
81
59
|
if (!allowed) {
|
|
@@ -88,14 +66,53 @@ class Http2ClientPlugin extends ClientPlugin {
|
|
|
88
66
|
this.tracer.inject(span, HTTP_HEADERS, headers)
|
|
89
67
|
}
|
|
90
68
|
|
|
91
|
-
|
|
69
|
+
message.parentStore = store
|
|
70
|
+
message.currentStore = { ...store, span }
|
|
71
|
+
|
|
72
|
+
return message.currentStore
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
bindAsyncStart ({ eventName, eventData, currentStore, parentStore }) {
|
|
76
|
+
switch (eventName) {
|
|
77
|
+
case 'response':
|
|
78
|
+
this._onResponse(currentStore, eventData)
|
|
79
|
+
return parentStore
|
|
80
|
+
case 'error':
|
|
81
|
+
this._onError(currentStore, eventData)
|
|
82
|
+
return parentStore
|
|
83
|
+
case 'close':
|
|
84
|
+
this._onClose(currentStore, eventData)
|
|
85
|
+
return parentStore
|
|
86
|
+
}
|
|
92
87
|
|
|
93
|
-
|
|
88
|
+
return storage.getStore()
|
|
94
89
|
}
|
|
95
90
|
|
|
96
91
|
configure (config) {
|
|
97
92
|
return super.configure(normalizeConfig(config))
|
|
98
93
|
}
|
|
94
|
+
|
|
95
|
+
_onResponse (store, headers) {
|
|
96
|
+
const status = headers && headers[HTTP2_HEADER_STATUS]
|
|
97
|
+
|
|
98
|
+
store.span.setTag(HTTP_STATUS_CODE, status)
|
|
99
|
+
|
|
100
|
+
if (!this.config.validateStatus(status)) {
|
|
101
|
+
storage.run(store, () => this.addError())
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
addHeaderTags(store.span, headers, HTTP_RESPONSE_HEADERS, this.config)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
_onError ({ span }, error) {
|
|
108
|
+
span.setTag('error', error)
|
|
109
|
+
span.finish()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
_onClose ({ span }) {
|
|
113
|
+
this.tagPeerService(span)
|
|
114
|
+
span.finish()
|
|
115
|
+
}
|
|
99
116
|
}
|
|
100
117
|
|
|
101
118
|
function extractSessionDetails (authority, options) {
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
|
|
4
4
|
const { encodePathwayContext } = require('../../dd-trace/src/datastreams/pathway')
|
|
5
|
+
const BOOTSTRAP_SERVERS_KEY = 'messaging.kafka.bootstrap.servers'
|
|
5
6
|
|
|
6
7
|
class KafkajsProducerPlugin extends ProducerPlugin {
|
|
7
8
|
static get id () { return 'kafkajs' }
|
|
8
9
|
static get operation () { return 'produce' }
|
|
10
|
+
static get peerServicePrecursors () { return [BOOTSTRAP_SERVERS_KEY] }
|
|
9
11
|
|
|
10
|
-
start ({ topic, messages }) {
|
|
12
|
+
start ({ topic, messages, bootstrapServers }) {
|
|
11
13
|
let pathwayCtx
|
|
12
14
|
if (this.config.dsmEnabled) {
|
|
13
15
|
const dataStreamsContext = this.tracer
|
|
@@ -24,6 +26,9 @@ class KafkajsProducerPlugin extends ProducerPlugin {
|
|
|
24
26
|
'kafka.batch_size': messages.length
|
|
25
27
|
}
|
|
26
28
|
})
|
|
29
|
+
if (bootstrapServers) {
|
|
30
|
+
span.setTag(BOOTSTRAP_SERVERS_KEY, bootstrapServers)
|
|
31
|
+
}
|
|
27
32
|
for (const message of messages) {
|
|
28
33
|
if (typeof message === 'object') {
|
|
29
34
|
if (this.config.dsmEnabled) message.headers['dd-pathway-ctx'] = pathwayCtx
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const DogStatsDClient = require('../../dd-trace/src/dogstatsd')
|
|
3
|
+
const { DogStatsDClient, NoopDogStatsDClient } = require('../../dd-trace/src/dogstatsd')
|
|
4
4
|
const ExternalLogger = require('../../dd-trace/src/external-logger/src')
|
|
5
5
|
|
|
6
6
|
const FLUSH_INTERVAL = 10 * 1000
|
|
@@ -10,15 +10,19 @@ let logger = null
|
|
|
10
10
|
let interval = null
|
|
11
11
|
|
|
12
12
|
module.exports.init = function (tracerConfig) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
if (tracerConfig.dogstatsd) {
|
|
14
|
+
metrics = new DogStatsDClient({
|
|
15
|
+
host: tracerConfig.dogstatsd.hostname,
|
|
16
|
+
port: tracerConfig.dogstatsd.port,
|
|
17
|
+
tags: [
|
|
18
|
+
`service:${tracerConfig.tags.service}`,
|
|
19
|
+
`env:${tracerConfig.tags.env}`,
|
|
20
|
+
`version:${tracerConfig.tags.version}`
|
|
21
|
+
]
|
|
22
|
+
})
|
|
23
|
+
} else {
|
|
24
|
+
metrics = new NoopDogStatsDClient()
|
|
25
|
+
}
|
|
22
26
|
|
|
23
27
|
logger = new ExternalLogger({
|
|
24
28
|
ddsource: 'openai',
|
|
@@ -143,4 +143,17 @@ class DogStatsDClient {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
class NoopDogStatsDClient {
|
|
147
|
+
gauge () { }
|
|
148
|
+
|
|
149
|
+
increment () { }
|
|
150
|
+
|
|
151
|
+
distribution () { }
|
|
152
|
+
|
|
153
|
+
flush () { }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = {
|
|
157
|
+
DogStatsDClient,
|
|
158
|
+
NoopDogStatsDClient
|
|
159
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
const { URL, format } = require('url')
|
|
6
6
|
const v8 = require('v8')
|
|
7
7
|
const os = require('os')
|
|
8
|
-
const
|
|
8
|
+
const { DogStatsDClient } = require('./dogstatsd')
|
|
9
9
|
const log = require('./log')
|
|
10
10
|
const Histogram = require('./histogram')
|
|
11
11
|
const { performance } = require('perf_hooks')
|
|
@@ -67,7 +67,7 @@ module.exports = {
|
|
|
67
67
|
}))
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
client = new
|
|
70
|
+
client = new DogStatsDClient(clientConfig)
|
|
71
71
|
|
|
72
72
|
time = process.hrtime()
|
|
73
73
|
|
|
@@ -111,7 +111,12 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
111
111
|
const childOf = getTestParentSpan(this.tracer)
|
|
112
112
|
|
|
113
113
|
let testTags = {
|
|
114
|
-
...getTestCommonTags(
|
|
114
|
+
...getTestCommonTags(
|
|
115
|
+
testName,
|
|
116
|
+
testSuite,
|
|
117
|
+
this.frameworkVersion,
|
|
118
|
+
this.constructor.id
|
|
119
|
+
),
|
|
115
120
|
[COMPONENT]: this.constructor.id,
|
|
116
121
|
...extraTags
|
|
117
122
|
}
|
|
@@ -62,13 +62,17 @@ class OutboundPlugin extends TracingPlugin {
|
|
|
62
62
|
|
|
63
63
|
finish () {
|
|
64
64
|
const span = this.activeSpan
|
|
65
|
+
this.tagPeerService(span)
|
|
66
|
+
super.finish(...arguments)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
tagPeerService (span) {
|
|
65
70
|
if (this.tracer._computePeerService) {
|
|
66
71
|
const peerData = this.getPeerService(span.context()._tags)
|
|
67
72
|
if (peerData) {
|
|
68
73
|
span.addTags(peerData)
|
|
69
74
|
}
|
|
70
75
|
}
|
|
71
|
-
super.finish(...arguments)
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
connect (url) {
|
|
@@ -25,9 +25,31 @@ class Subscription {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
class StoreBinding {
|
|
29
|
+
constructor (event, transform) {
|
|
30
|
+
this._channel = dc.channel(event)
|
|
31
|
+
this._transform = data => {
|
|
32
|
+
const store = storage.getStore()
|
|
33
|
+
|
|
34
|
+
return !store || !store.noop
|
|
35
|
+
? transform(data)
|
|
36
|
+
: store
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
enable () {
|
|
41
|
+
this._channel.bindStore(storage, this._transform)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
disable () {
|
|
45
|
+
this._channel.unbindStore(storage, this._transform)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
28
49
|
module.exports = class Plugin {
|
|
29
50
|
constructor (tracer, tracerConfig) {
|
|
30
51
|
this._subscriptions = []
|
|
52
|
+
this._bindings = []
|
|
31
53
|
this._enabled = false
|
|
32
54
|
this._tracer = tracer
|
|
33
55
|
this.config = {} // plugin-specific configuration, unset until .configure() is called
|
|
@@ -53,6 +75,10 @@ module.exports = class Plugin {
|
|
|
53
75
|
this._subscriptions.push(new Subscription(channelName, handler))
|
|
54
76
|
}
|
|
55
77
|
|
|
78
|
+
addBind (channelName, transform) {
|
|
79
|
+
this._bindings.push(new StoreBinding(channelName, transform))
|
|
80
|
+
}
|
|
81
|
+
|
|
56
82
|
addError (error) {
|
|
57
83
|
const store = storage.getStore()
|
|
58
84
|
|
|
@@ -71,9 +97,11 @@ module.exports = class Plugin {
|
|
|
71
97
|
if (config.enabled && !this._enabled) {
|
|
72
98
|
this._enabled = true
|
|
73
99
|
this._subscriptions.forEach(sub => sub.enable())
|
|
100
|
+
this._bindings.forEach(sub => sub.enable())
|
|
74
101
|
} else if (!config.enabled && this._enabled) {
|
|
75
102
|
this._enabled = false
|
|
76
103
|
this._subscriptions.forEach(sub => sub.disable())
|
|
104
|
+
this._bindings.forEach(sub => sub.disable())
|
|
77
105
|
}
|
|
78
106
|
}
|
|
79
107
|
}
|
|
@@ -13,17 +13,7 @@ class TracingPlugin extends Plugin {
|
|
|
13
13
|
this.component = this.constructor.component || this.constructor.id
|
|
14
14
|
this.operation = this.constructor.operation
|
|
15
15
|
|
|
16
|
-
this.
|
|
17
|
-
this.start(message)
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
this.addTraceSub('error', err => {
|
|
21
|
-
this.error(err)
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
this.addTraceSub('finish', message => {
|
|
25
|
-
this.finish(message)
|
|
26
|
-
})
|
|
16
|
+
this.addTraceSubs()
|
|
27
17
|
}
|
|
28
18
|
|
|
29
19
|
get activeSpan () {
|
|
@@ -65,19 +55,45 @@ class TracingPlugin extends Plugin {
|
|
|
65
55
|
this.addError(error)
|
|
66
56
|
}
|
|
67
57
|
|
|
58
|
+
addTraceSubs () {
|
|
59
|
+
const events = ['start', 'end', 'asyncStart', 'asyncEnd', 'error', 'finish']
|
|
60
|
+
|
|
61
|
+
for (const event of events) {
|
|
62
|
+
const bindName = `bind${event.charAt(0).toUpperCase()}${event.slice(1)}`
|
|
63
|
+
|
|
64
|
+
if (this[event]) {
|
|
65
|
+
this.addTraceSub(event, message => {
|
|
66
|
+
this[event](message)
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (this[bindName]) {
|
|
71
|
+
this.addTraceBind(event, message => this[bindName](message))
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
68
76
|
addTraceSub (eventName, handler) {
|
|
69
|
-
this.
|
|
77
|
+
const prefix = this.constructor.prefix || `apm:${this.component}:${this.operation}`
|
|
78
|
+
this.addSub(`${prefix}:${eventName}`, handler)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
addTraceBind (eventName, transform) {
|
|
82
|
+
const prefix = this.constructor.prefix || `apm:${this.component}:${this.operation}`
|
|
83
|
+
this.addBind(`${prefix}:${eventName}`, transform)
|
|
70
84
|
}
|
|
71
85
|
|
|
72
86
|
addError (error) {
|
|
73
87
|
const span = this.activeSpan
|
|
74
88
|
|
|
75
89
|
if (!span._spanContext._tags['error']) {
|
|
90
|
+
// Errors may be wrapped in a context.
|
|
91
|
+
error = (error && error.error) || error
|
|
76
92
|
span.setTag('error', error || 1)
|
|
77
93
|
}
|
|
78
94
|
}
|
|
79
95
|
|
|
80
|
-
startSpan (name, { childOf, kind, meta, metrics, service, resource, type } = {}) {
|
|
96
|
+
startSpan (name, { childOf, kind, meta, metrics, service, resource, type } = {}, enter = true) {
|
|
81
97
|
const store = storage.getStore()
|
|
82
98
|
|
|
83
99
|
if (store && childOf === undefined) {
|
|
@@ -100,7 +116,10 @@ class TracingPlugin extends Plugin {
|
|
|
100
116
|
|
|
101
117
|
analyticsSampler.sample(span, this.config.measured)
|
|
102
118
|
|
|
103
|
-
|
|
119
|
+
// TODO: Remove this after migration to TracingChannel is done.
|
|
120
|
+
if (enter) {
|
|
121
|
+
storage.enterWith({ ...store, span })
|
|
122
|
+
}
|
|
104
123
|
|
|
105
124
|
return span
|
|
106
125
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
2
|
const fs = require('fs')
|
|
3
|
+
const { URL } = require('url')
|
|
4
|
+
const log = require('../../log')
|
|
3
5
|
|
|
4
6
|
const istanbul = require('istanbul-lib-coverage')
|
|
5
7
|
const ignore = require('ignore')
|
|
6
8
|
|
|
7
9
|
const { getGitMetadata } = require('./git')
|
|
8
|
-
const { getUserProviderGitMetadata } = require('./user-provided-git')
|
|
10
|
+
const { getUserProviderGitMetadata, validateGitRepositoryUrl, validateGitCommitSha } = require('./user-provided-git')
|
|
9
11
|
const { getCIMetadata } = require('./ci')
|
|
10
12
|
const { getRuntimeAndOSMetadata } = require('./env')
|
|
11
13
|
const {
|
|
@@ -16,7 +18,8 @@ const {
|
|
|
16
18
|
GIT_COMMIT_AUTHOR_EMAIL,
|
|
17
19
|
GIT_COMMIT_AUTHOR_NAME,
|
|
18
20
|
GIT_COMMIT_MESSAGE,
|
|
19
|
-
CI_WORKSPACE_PATH
|
|
21
|
+
CI_WORKSPACE_PATH,
|
|
22
|
+
CI_PIPELINE_URL
|
|
20
23
|
} = require('./tags')
|
|
21
24
|
const id = require('../../id')
|
|
22
25
|
|
|
@@ -104,7 +107,8 @@ module.exports = {
|
|
|
104
107
|
mergeCoverage,
|
|
105
108
|
fromCoverageMapToCoverage,
|
|
106
109
|
getTestLineStart,
|
|
107
|
-
getCallSites
|
|
110
|
+
getCallSites,
|
|
111
|
+
removeInvalidMetadata
|
|
108
112
|
}
|
|
109
113
|
|
|
110
114
|
// Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
|
|
@@ -116,6 +120,39 @@ function getPkgManager () {
|
|
|
116
120
|
}
|
|
117
121
|
}
|
|
118
122
|
|
|
123
|
+
function validateUrl (url) {
|
|
124
|
+
try {
|
|
125
|
+
const urlObject = new URL(url)
|
|
126
|
+
return (urlObject.protocol === 'https:' || urlObject.protocol === 'http:')
|
|
127
|
+
} catch (e) {
|
|
128
|
+
return false
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function removeInvalidMetadata (metadata) {
|
|
133
|
+
return Object.keys(metadata).reduce((filteredTags, tag) => {
|
|
134
|
+
if (tag === GIT_REPOSITORY_URL) {
|
|
135
|
+
if (!validateGitRepositoryUrl(metadata[GIT_REPOSITORY_URL])) {
|
|
136
|
+
log.error('DD_GIT_REPOSITORY_URL must be a valid URL')
|
|
137
|
+
return filteredTags
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (tag === GIT_COMMIT_SHA) {
|
|
141
|
+
if (!validateGitCommitSha(metadata[GIT_COMMIT_SHA])) {
|
|
142
|
+
log.error('DD_GIT_COMMIT_SHA must be a full-length git SHA')
|
|
143
|
+
return filteredTags
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (tag === CI_PIPELINE_URL) {
|
|
147
|
+
if (!validateUrl(metadata[CI_PIPELINE_URL])) {
|
|
148
|
+
return filteredTags
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
filteredTags[tag] = metadata[tag]
|
|
152
|
+
return filteredTags
|
|
153
|
+
}, {})
|
|
154
|
+
}
|
|
155
|
+
|
|
119
156
|
function getTestEnvironmentMetadata (testFramework, config) {
|
|
120
157
|
// TODO: eventually these will come from the tracer (generally available)
|
|
121
158
|
const ciMetadata = getCIMetadata()
|
|
@@ -155,7 +192,7 @@ function getTestEnvironmentMetadata (testFramework, config) {
|
|
|
155
192
|
if (config && config.service) {
|
|
156
193
|
metadata['service.name'] = config.service
|
|
157
194
|
}
|
|
158
|
-
return metadata
|
|
195
|
+
return removeInvalidMetadata(metadata)
|
|
159
196
|
}
|
|
160
197
|
|
|
161
198
|
function getTestParametersString (parametersByTestName, testName) {
|
|
@@ -173,6 +210,13 @@ function getTestParametersString (parametersByTestName, testName) {
|
|
|
173
210
|
}
|
|
174
211
|
}
|
|
175
212
|
|
|
213
|
+
function getTestTypeFromFramework (testFramework) {
|
|
214
|
+
if (testFramework === 'playwright' || testFramework === 'cypress') {
|
|
215
|
+
return 'browser'
|
|
216
|
+
}
|
|
217
|
+
return 'test'
|
|
218
|
+
}
|
|
219
|
+
|
|
176
220
|
function finishAllTraceSpans (span) {
|
|
177
221
|
span.context()._trace.started.forEach(traceSpan => {
|
|
178
222
|
if (traceSpan !== span) {
|
|
@@ -188,10 +232,10 @@ function getTestParentSpan (tracer) {
|
|
|
188
232
|
})
|
|
189
233
|
}
|
|
190
234
|
|
|
191
|
-
function getTestCommonTags (name, suite, version) {
|
|
235
|
+
function getTestCommonTags (name, suite, version, testFramework) {
|
|
192
236
|
return {
|
|
193
237
|
[SPAN_TYPE]: 'test',
|
|
194
|
-
[TEST_TYPE]:
|
|
238
|
+
[TEST_TYPE]: getTestTypeFromFramework(testFramework),
|
|
195
239
|
[SAMPLING_RULE_DECISION]: 1,
|
|
196
240
|
[SAMPLING_PRIORITY]: AUTO_KEEP,
|
|
197
241
|
[TEST_NAME]: name,
|
|
@@ -269,12 +313,12 @@ function getCodeOwnersForFilename (filename, entries) {
|
|
|
269
313
|
return null
|
|
270
314
|
}
|
|
271
315
|
|
|
272
|
-
function getTestLevelCommonTags (command, testFrameworkVersion) {
|
|
316
|
+
function getTestLevelCommonTags (command, testFrameworkVersion, testFramework) {
|
|
273
317
|
return {
|
|
274
318
|
[TEST_FRAMEWORK_VERSION]: testFrameworkVersion,
|
|
275
319
|
[LIBRARY_VERSION]: ddTraceVersion,
|
|
276
320
|
[TEST_COMMAND]: command,
|
|
277
|
-
[TEST_TYPE]:
|
|
321
|
+
[TEST_TYPE]: getTestTypeFromFramework(testFramework)
|
|
278
322
|
}
|
|
279
323
|
}
|
|
280
324
|
|
|
@@ -284,7 +328,7 @@ function getTestSessionCommonTags (command, testFrameworkVersion, testFramework)
|
|
|
284
328
|
[RESOURCE_NAME]: `test_session.${command}`,
|
|
285
329
|
[TEST_MODULE]: testFramework,
|
|
286
330
|
[TEST_TOOLCHAIN]: getPkgManager(),
|
|
287
|
-
...getTestLevelCommonTags(command, testFrameworkVersion)
|
|
331
|
+
...getTestLevelCommonTags(command, testFrameworkVersion, testFramework)
|
|
288
332
|
}
|
|
289
333
|
}
|
|
290
334
|
|
|
@@ -293,7 +337,7 @@ function getTestModuleCommonTags (command, testFrameworkVersion, testFramework)
|
|
|
293
337
|
[SPAN_TYPE]: 'test_module_end',
|
|
294
338
|
[RESOURCE_NAME]: `test_module.${command}`,
|
|
295
339
|
[TEST_MODULE]: testFramework,
|
|
296
|
-
...getTestLevelCommonTags(command, testFrameworkVersion)
|
|
340
|
+
...getTestLevelCommonTags(command, testFrameworkVersion, testFramework)
|
|
297
341
|
}
|
|
298
342
|
}
|
|
299
343
|
|
|
@@ -303,7 +347,7 @@ function getTestSuiteCommonTags (command, testFrameworkVersion, testSuite, testF
|
|
|
303
347
|
[RESOURCE_NAME]: `test_suite.${testSuite}`,
|
|
304
348
|
[TEST_MODULE]: testFramework,
|
|
305
349
|
[TEST_SUITE]: testSuite,
|
|
306
|
-
...getTestLevelCommonTags(command, testFrameworkVersion)
|
|
350
|
+
...getTestLevelCommonTags(command, testFrameworkVersion, testFramework)
|
|
307
351
|
}
|
|
308
352
|
}
|
|
309
353
|
|
|
@@ -13,7 +13,6 @@ const {
|
|
|
13
13
|
} = require('./tags')
|
|
14
14
|
|
|
15
15
|
const { normalizeRef } = require('./ci')
|
|
16
|
-
const log = require('../../log')
|
|
17
16
|
const { URL } = require('url')
|
|
18
17
|
|
|
19
18
|
function removeEmptyValues (tags) {
|
|
@@ -53,25 +52,6 @@ function validateGitCommitSha (gitCommitSha) {
|
|
|
53
52
|
return isValidSha1 || isValidSha256
|
|
54
53
|
}
|
|
55
54
|
|
|
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
|
-
|
|
75
55
|
function getUserProviderGitMetadata () {
|
|
76
56
|
const {
|
|
77
57
|
DD_GIT_COMMIT_SHA,
|
|
@@ -95,7 +75,7 @@ function getUserProviderGitMetadata () {
|
|
|
95
75
|
tag = normalizeRef(DD_GIT_BRANCH)
|
|
96
76
|
}
|
|
97
77
|
|
|
98
|
-
|
|
78
|
+
return removeEmptyValues({
|
|
99
79
|
[GIT_COMMIT_SHA]: DD_GIT_COMMIT_SHA,
|
|
100
80
|
[GIT_BRANCH]: branch,
|
|
101
81
|
[GIT_REPOSITORY_URL]: filterSensitiveInfoFromRepository(DD_GIT_REPOSITORY_URL),
|
|
@@ -108,7 +88,6 @@ function getUserProviderGitMetadata () {
|
|
|
108
88
|
[GIT_COMMIT_AUTHOR_EMAIL]: DD_GIT_COMMIT_AUTHOR_EMAIL,
|
|
109
89
|
[GIT_COMMIT_AUTHOR_DATE]: DD_GIT_COMMIT_AUTHOR_DATE
|
|
110
90
|
})
|
|
111
|
-
return removeInvalidGitMetadata(metadata)
|
|
112
91
|
}
|
|
113
92
|
|
|
114
93
|
module.exports = { getUserProviderGitMetadata, validateGitRepositoryUrl, validateGitCommitSha }
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
const { identityService } = require('../util')
|
|
2
|
+
const { DD_MAJOR } = require('../../../../../../version')
|
|
2
3
|
|
|
3
4
|
const web = {
|
|
4
5
|
client: {
|
|
5
6
|
grpc: {
|
|
6
|
-
opName: () => 'grpc.client',
|
|
7
|
+
opName: () => DD_MAJOR <= 2 ? 'grpc.request' : 'grpc.client',
|
|
7
8
|
serviceName: identityService
|
|
8
9
|
},
|
|
9
10
|
moleculer: {
|
|
@@ -13,7 +14,7 @@ const web = {
|
|
|
13
14
|
},
|
|
14
15
|
server: {
|
|
15
16
|
grpc: {
|
|
16
|
-
opName: () => 'grpc.server',
|
|
17
|
+
opName: () => DD_MAJOR <= 2 ? 'grpc.request' : 'grpc.server',
|
|
17
18
|
serviceName: identityService
|
|
18
19
|
},
|
|
19
20
|
moleculer: {
|
|
@@ -89,6 +89,26 @@ class CountMetric extends Metric {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
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
|
+
|
|
92
112
|
class GaugeMetric extends Metric {
|
|
93
113
|
get type () {
|
|
94
114
|
return 'gauge'
|
|
@@ -129,11 +149,12 @@ class RateMetric extends Metric {
|
|
|
129
149
|
|
|
130
150
|
const metricsTypes = {
|
|
131
151
|
count: CountMetric,
|
|
152
|
+
distribution: DistributionMetric,
|
|
132
153
|
gauge: GaugeMetric,
|
|
133
154
|
rate: RateMetric
|
|
134
155
|
}
|
|
135
156
|
|
|
136
|
-
class
|
|
157
|
+
class MetricsCollection extends Map {
|
|
137
158
|
constructor (namespace) {
|
|
138
159
|
super()
|
|
139
160
|
this.namespace = namespace
|
|
@@ -146,43 +167,68 @@ class Namespace extends Map {
|
|
|
146
167
|
}
|
|
147
168
|
|
|
148
169
|
toString () {
|
|
149
|
-
return
|
|
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
|
+
}
|
|
150
180
|
}
|
|
181
|
+
}
|
|
151
182
|
|
|
152
|
-
|
|
153
|
-
|
|
183
|
+
function getMetric (collection, type, name, tags, interval) {
|
|
184
|
+
const metricId = getId(type, collection, name, tags)
|
|
154
185
|
|
|
155
|
-
|
|
156
|
-
|
|
186
|
+
let metric = collection.get(metricId)
|
|
187
|
+
if (metric) return metric
|
|
157
188
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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)
|
|
162
196
|
|
|
163
|
-
|
|
164
|
-
|
|
197
|
+
return metric
|
|
198
|
+
}
|
|
165
199
|
|
|
166
|
-
|
|
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()
|
|
167
209
|
}
|
|
168
210
|
|
|
169
211
|
count (name, tags) {
|
|
170
|
-
return this.
|
|
212
|
+
return getMetric(this.metrics, 'count', name, tags)
|
|
171
213
|
}
|
|
172
214
|
|
|
173
215
|
gauge (name, tags) {
|
|
174
|
-
return this.
|
|
216
|
+
return getMetric(this.metrics, 'gauge', name, tags)
|
|
175
217
|
}
|
|
176
218
|
|
|
177
219
|
rate (name, interval, tags) {
|
|
178
|
-
return this.
|
|
220
|
+
return getMetric(this.metrics, 'rate', name, tags, interval)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
distribution (name, tags) {
|
|
224
|
+
return getMetric(this.distributions, 'distribution', name, tags)
|
|
179
225
|
}
|
|
180
226
|
|
|
181
227
|
toJSON () {
|
|
182
|
-
const {
|
|
228
|
+
const { distributions, metrics } = this
|
|
183
229
|
return {
|
|
184
|
-
|
|
185
|
-
|
|
230
|
+
distributions: distributions.toJSON(),
|
|
231
|
+
metrics: metrics.toJSON()
|
|
186
232
|
}
|
|
187
233
|
}
|
|
188
234
|
}
|
|
@@ -203,7 +249,15 @@ class NamespaceManager extends Map {
|
|
|
203
249
|
|
|
204
250
|
send (config, application, host) {
|
|
205
251
|
for (const namespace of this.values()) {
|
|
206
|
-
|
|
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
|
+
}
|
|
207
261
|
|
|
208
262
|
// TODO: This could also be clear() but then it'd have to rebuild all
|
|
209
263
|
// metric instances on every send. This may be desirable if we want tags
|
|
@@ -217,8 +271,10 @@ const manager = new NamespaceManager()
|
|
|
217
271
|
|
|
218
272
|
module.exports = {
|
|
219
273
|
CountMetric,
|
|
274
|
+
DistributionMetric,
|
|
220
275
|
GaugeMetric,
|
|
221
276
|
RateMetric,
|
|
277
|
+
MetricsCollection,
|
|
222
278
|
Namespace,
|
|
223
279
|
NamespaceManager,
|
|
224
280
|
manager
|
|
@@ -54,4 +54,68 @@ if (major === '19' && minor === '9') {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
if (!Channel.prototype.runStores) {
|
|
58
|
+
const ActiveChannelPrototype = getActiveChannelPrototype()
|
|
59
|
+
|
|
60
|
+
Channel.prototype.bindStore = ActiveChannelPrototype.bindStore = function (store, transform) {
|
|
61
|
+
if (!this._stores) {
|
|
62
|
+
this._stores = new Map()
|
|
63
|
+
}
|
|
64
|
+
this._stores.set(store, transform)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
Channel.prototype.unbindStore = ActiveChannelPrototype.runStores = function (store) {
|
|
68
|
+
if (!this._stores) return
|
|
69
|
+
this._stores.delete(store)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Channel.prototype.runStores = ActiveChannelPrototype.runStores = function (data, fn, thisArg, ...args) {
|
|
73
|
+
if (!this._stores) return Reflect.apply(fn, thisArg, args)
|
|
74
|
+
|
|
75
|
+
let run = () => {
|
|
76
|
+
this.publish(data)
|
|
77
|
+
return Reflect.apply(fn, thisArg, args)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
for (const entry of this._stores.entries()) {
|
|
81
|
+
const store = entry[0]
|
|
82
|
+
const transform = entry[1]
|
|
83
|
+
run = wrapStoreRun(store, data, run, transform)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return run()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function defaultTransform (data) {
|
|
91
|
+
return data
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function wrapStoreRun (store, data, next, transform = defaultTransform) {
|
|
95
|
+
return () => {
|
|
96
|
+
let context
|
|
97
|
+
try {
|
|
98
|
+
context = transform(data)
|
|
99
|
+
} catch (err) {
|
|
100
|
+
process.nextTick(() => {
|
|
101
|
+
throw err
|
|
102
|
+
})
|
|
103
|
+
return next()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return store.run(context, next)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getActiveChannelPrototype () {
|
|
111
|
+
const dummyChannel = channel('foo')
|
|
112
|
+
const listener = () => {}
|
|
113
|
+
|
|
114
|
+
dummyChannel.subscribe(listener)
|
|
115
|
+
const ActiveChannelPrototype = Object.getPrototypeOf(dummyChannel)
|
|
116
|
+
dummyChannel.unsubscribe(listener)
|
|
117
|
+
|
|
118
|
+
return ActiveChannelPrototype
|
|
119
|
+
}
|
|
120
|
+
|
|
57
121
|
module.exports = dc
|