dd-trace 2.0.0 → 2.1.1
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 +2 -2
- package/packages/datadog-instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/mysql.js +2 -2
- package/packages/datadog-instrumentations/src/mysql2.js +76 -0
- package/packages/datadog-instrumentations/src/q.js +9 -1
- package/packages/datadog-plugin-aws-sdk/src/helpers.js +4 -3
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +48 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +56 -6
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +33 -6
- package/packages/datadog-plugin-mysql/src/index.js +4 -4
- package/packages/datadog-plugin-mysql2/src/index.js +5 -88
- package/packages/datadog-plugin-next/src/index.js +10 -6
- package/packages/datadog-plugin-winston/src/index.js +30 -12
- package/packages/dd-trace/lib/version.js +1 -1
- package/packages/dd-trace/src/appsec/recommended.json +5708 -1
- package/packages/dd-trace/src/profiling/config.js +5 -1
- package/packages/dd-trace/src/profiling/profiler.js +15 -6
- package/packages/dd-trace/src/profiling/profilers/cpu.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/heap.js +3 -2
- package/scripts/publish_docs.js +1 -1
- package/scripts/tracer-runner.js +13 -0
- package/packages/dd-trace/src/profiling/mapper.js +0 -91
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"node": ">=12"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@datadog/native-appsec": "^0.8.
|
|
64
|
+
"@datadog/native-appsec": "^0.8.1",
|
|
65
65
|
"@datadog/native-metrics": "^1.1.0",
|
|
66
66
|
"@datadog/pprof": "^0.3.0",
|
|
67
67
|
"@datadog/sketches-js": "^1.0.4",
|
|
@@ -20,9 +20,9 @@ addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connect
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const sql = arguments[0].sql ? arguments[0].sql : arguments[0]
|
|
23
|
-
const
|
|
23
|
+
const conf = this.config
|
|
24
24
|
|
|
25
|
-
startCh.publish(
|
|
25
|
+
startCh.publish({ sql, conf })
|
|
26
26
|
|
|
27
27
|
try {
|
|
28
28
|
const res = query.apply(this, arguments)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
channel,
|
|
5
|
+
addHook,
|
|
6
|
+
AsyncResource
|
|
7
|
+
} = require('./helpers/instrument')
|
|
8
|
+
const shimmer = require('../../datadog-shimmer')
|
|
9
|
+
|
|
10
|
+
addHook({ name: 'mysql2', file: 'lib/connection.js', versions: ['>=1'] }, Connection => {
|
|
11
|
+
const startCh = channel('apm:mysql2:query:start')
|
|
12
|
+
const asyncEndCh = channel('apm:mysql2:query:async-end')
|
|
13
|
+
const endCh = channel('apm:mysql2:query:end')
|
|
14
|
+
const errorCh = channel('apm:mysql2:query:error')
|
|
15
|
+
|
|
16
|
+
shimmer.wrap(Connection.prototype, 'addCommand', addCommand => function (cmd) {
|
|
17
|
+
if (!startCh.hasSubscribers) return addCommand.apply(this, arguments)
|
|
18
|
+
|
|
19
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
20
|
+
const name = cmd && cmd.constructor && cmd.constructor.name
|
|
21
|
+
const isCommand = typeof cmd.execute === 'function'
|
|
22
|
+
const isQuery = isCommand && (name === 'Execute' || name === 'Query')
|
|
23
|
+
|
|
24
|
+
// TODO: consider supporting all commands and not just queries
|
|
25
|
+
cmd.execute = isQuery
|
|
26
|
+
? wrapExecute(cmd, cmd.execute, asyncResource, this.config)
|
|
27
|
+
: bindExecute(cmd, cmd.execute, asyncResource)
|
|
28
|
+
|
|
29
|
+
return asyncResource.bind(addCommand, this).apply(this, arguments)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
return Connection
|
|
33
|
+
|
|
34
|
+
function bindExecute (cmd, execute, asyncResource) {
|
|
35
|
+
return asyncResource.bind(function executeWithTrace (packet, connection) {
|
|
36
|
+
if (this.onResult) {
|
|
37
|
+
this.onResult = asyncResource.bind(this.onResult)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return execute.apply(this, arguments)
|
|
41
|
+
}, cmd)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function wrapExecute (cmd, execute, asyncResource, config) {
|
|
45
|
+
return asyncResource.bind(function executeWithTrace (packet, connection) {
|
|
46
|
+
const sql = cmd.statement ? cmd.statement.query : cmd.sql
|
|
47
|
+
|
|
48
|
+
startCh.publish({ sql, conf: config })
|
|
49
|
+
|
|
50
|
+
if (this.onResult) {
|
|
51
|
+
const onResult = asyncResource.bind(this.onResult)
|
|
52
|
+
|
|
53
|
+
this.onResult = AsyncResource.bind(function (error) {
|
|
54
|
+
if (error) {
|
|
55
|
+
errorCh.publish(error)
|
|
56
|
+
}
|
|
57
|
+
asyncEndCh.publish(undefined)
|
|
58
|
+
onResult.apply(this, arguments)
|
|
59
|
+
}, 'bound-anonymous-fn', this)
|
|
60
|
+
} else {
|
|
61
|
+
this.on('error', AsyncResource.bind(error => errorCh.publish(error)))
|
|
62
|
+
this.on('end', AsyncResource.bind(() => asyncEndCh.publish(undefined)))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.execute = execute
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
return execute.apply(this, arguments)
|
|
69
|
+
} catch (err) {
|
|
70
|
+
errorCh.publish(err)
|
|
71
|
+
} finally {
|
|
72
|
+
endCh.publish(undefined)
|
|
73
|
+
}
|
|
74
|
+
}, cmd)
|
|
75
|
+
}
|
|
76
|
+
})
|
|
@@ -6,8 +6,16 @@ const shimmer = require('../../datadog-shimmer')
|
|
|
6
6
|
|
|
7
7
|
addHook({
|
|
8
8
|
name: 'q',
|
|
9
|
-
versions: ['
|
|
9
|
+
versions: ['1']
|
|
10
10
|
}, Q => {
|
|
11
11
|
shimmer.wrap(Q.makePromise.prototype, 'then', wrapThen)
|
|
12
12
|
return Q
|
|
13
13
|
})
|
|
14
|
+
|
|
15
|
+
addHook({
|
|
16
|
+
name: 'q',
|
|
17
|
+
versions: ['>=2']
|
|
18
|
+
}, Q => {
|
|
19
|
+
shimmer.wrap(Q.Promise.prototype, 'then', wrapThen)
|
|
20
|
+
return Q
|
|
21
|
+
})
|
|
@@ -10,7 +10,8 @@ const services = {
|
|
|
10
10
|
s3: getService(require('./services/s3')),
|
|
11
11
|
redshift: getService(require('./services/redshift')),
|
|
12
12
|
sns: getService(require('./services/sns')),
|
|
13
|
-
sqs: getService(require('./services/sqs'))
|
|
13
|
+
sqs: getService(require('./services/sqs')),
|
|
14
|
+
eventbridge: getService(require('./services/eventbridge'))
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
function getService (Service) {
|
|
@@ -78,8 +79,8 @@ const helpers = {
|
|
|
78
79
|
requestInject (span, request, serviceName, tracer) {
|
|
79
80
|
if (!span) return
|
|
80
81
|
|
|
81
|
-
const
|
|
82
|
-
if (
|
|
82
|
+
const service = services[serviceName] && services[serviceName]
|
|
83
|
+
if (service && service.requestInject) service.requestInject(span, request, tracer)
|
|
83
84
|
},
|
|
84
85
|
|
|
85
86
|
wrapCb (cb, serviceName, tags, request, tracer, childOf) {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const log = require('../../../dd-trace/src/log')
|
|
3
|
+
class EventBridge {
|
|
4
|
+
generateTags (params, operation, response) {
|
|
5
|
+
if (!params || !params.source) return {}
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
'resource.name': `${operation} ${params.source}`,
|
|
9
|
+
'aws.eventbridge.source': params.source
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* requestInject
|
|
15
|
+
* @param {*} span
|
|
16
|
+
* @param {*} request
|
|
17
|
+
* @param {*} tracer
|
|
18
|
+
*
|
|
19
|
+
* Docs: https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutEventsRequestEntry.html
|
|
20
|
+
* We cannot use the traceHeader field as that's reserved for X-Ray.
|
|
21
|
+
* Detail must be a valid JSON string
|
|
22
|
+
* Max size per event is 256kb (https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-putevent-size.html)
|
|
23
|
+
*/
|
|
24
|
+
requestInject (span, request, tracer) {
|
|
25
|
+
const operation = request.operation
|
|
26
|
+
if (operation === 'putEvents' &&
|
|
27
|
+
request.params &&
|
|
28
|
+
request.params.Entries &&
|
|
29
|
+
request.params.Entries.length > 0 &&
|
|
30
|
+
request.params.Entries[0].Detail) {
|
|
31
|
+
try {
|
|
32
|
+
const details = JSON.parse(request.params.Entries[0].Detail)
|
|
33
|
+
details._datadog = {}
|
|
34
|
+
tracer.inject(span, 'text_map', details._datadog)
|
|
35
|
+
const finalData = JSON.stringify(details)
|
|
36
|
+
const byteSize = Buffer.byteLength(finalData)
|
|
37
|
+
if (byteSize >= (1024 * 256)) {
|
|
38
|
+
log.info('Payload size too large to pass context')
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
request.params.Entries[0].Detail = finalData
|
|
42
|
+
} catch (e) {
|
|
43
|
+
log.error(e)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
module.exports = EventBridge
|
|
@@ -1,15 +1,65 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
-
|
|
2
|
+
const log = require('../../../dd-trace/src/log')
|
|
3
3
|
class Kinesis {
|
|
4
4
|
generateTags (params, operation, response) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
if (!params || !params.StreamName) return tags
|
|
5
|
+
if (!params || !params.StreamName) return {}
|
|
8
6
|
|
|
9
|
-
return
|
|
7
|
+
return {
|
|
10
8
|
'resource.name': `${operation} ${params.StreamName}`,
|
|
11
9
|
'aws.kinesis.stream_name': params.StreamName
|
|
12
|
-
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// AWS-SDK will b64 kinesis payloads
|
|
14
|
+
// or will accept an already b64 encoded payload
|
|
15
|
+
// This method handles both
|
|
16
|
+
_tryParse (body) {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(body)
|
|
19
|
+
} catch (e) {
|
|
20
|
+
log.info('Not JSON string. Trying Base64 encoded JSON string')
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(Buffer.from(body, 'base64').toString('ascii'), true)
|
|
24
|
+
} catch (e) {
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
requestInject (span, request, tracer) {
|
|
30
|
+
const operation = request.operation
|
|
31
|
+
if (operation === 'putRecord' || operation === 'putRecords') {
|
|
32
|
+
if (!request.params) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const traceData = {}
|
|
37
|
+
tracer.inject(span, 'text_map', traceData)
|
|
38
|
+
let injectPath
|
|
39
|
+
if (request.params.Records && request.params.Records.length > 0) {
|
|
40
|
+
injectPath = request.params.Records[0]
|
|
41
|
+
} else if (request.params.Data) {
|
|
42
|
+
injectPath = request.params
|
|
43
|
+
} else {
|
|
44
|
+
log.error('No valid payload passed, unable to pass trace context')
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
const parsedData = this._tryParse(injectPath.Data)
|
|
48
|
+
if (parsedData) {
|
|
49
|
+
parsedData._datadog = traceData
|
|
50
|
+
const finalData = JSON.stringify(parsedData)
|
|
51
|
+
const byteSize = Buffer.byteLength(finalData, 'ascii')
|
|
52
|
+
// Kinesis max payload size is 1MB
|
|
53
|
+
// So we must ensure adding DD context won't go over that (512b is an estimate)
|
|
54
|
+
if (byteSize >= 1048576) {
|
|
55
|
+
log.info('Payload size too large to pass context')
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
injectPath.Data = finalData
|
|
59
|
+
} else {
|
|
60
|
+
log.error('Unable to parse payload, unable to pass trace context')
|
|
61
|
+
}
|
|
62
|
+
}
|
|
13
63
|
}
|
|
14
64
|
}
|
|
15
65
|
|
|
@@ -1,21 +1,48 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
+
const log = require('../../../dd-trace/src/log')
|
|
2
3
|
|
|
3
4
|
class Sns {
|
|
4
5
|
generateTags (params, operation, response) {
|
|
5
|
-
|
|
6
|
+
if (!params) return {}
|
|
6
7
|
|
|
7
|
-
if (!params) return
|
|
8
|
+
if (!params.TopicArn && !(response.data && response.data.TopicArn)) return {}
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return Object.assign(tags, {
|
|
10
|
+
return {
|
|
12
11
|
'resource.name': `${operation} ${params.TopicArn || response.data.TopicArn}`,
|
|
13
12
|
'aws.sns.topic_arn': params.TopicArn || response.data.TopicArn
|
|
14
|
-
}
|
|
13
|
+
}
|
|
15
14
|
|
|
16
15
|
// TODO: should arn be sanitized or quantized in some way here,
|
|
17
16
|
// for example if it contains a phone number?
|
|
18
17
|
}
|
|
18
|
+
|
|
19
|
+
requestInject (span, request, tracer) {
|
|
20
|
+
const operation = request.operation
|
|
21
|
+
if (operation === 'publish' || operation === 'publishBatch') {
|
|
22
|
+
if (!request.params) {
|
|
23
|
+
request.params = {}
|
|
24
|
+
}
|
|
25
|
+
let injectPath
|
|
26
|
+
if (request.params.PublishBatchRequestEntries && request.params.PublishBatchRequestEntries.length > 0) {
|
|
27
|
+
injectPath = request.params.PublishBatchRequestEntries[0]
|
|
28
|
+
} else if (request.params.Message) {
|
|
29
|
+
injectPath = request.params
|
|
30
|
+
}
|
|
31
|
+
if (!injectPath.MessageAttributes) {
|
|
32
|
+
injectPath.MessageAttributes = {}
|
|
33
|
+
}
|
|
34
|
+
if (Object.keys(injectPath.MessageAttributes).length >= 10) { // SNS quota
|
|
35
|
+
log.info('Message attributes full, skipping trace context injection')
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
const ddInfo = {}
|
|
39
|
+
tracer.inject(span, 'text_map', ddInfo)
|
|
40
|
+
injectPath.MessageAttributes._datadog = {
|
|
41
|
+
DataType: 'String',
|
|
42
|
+
StringValue: JSON.stringify(ddInfo)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
19
46
|
}
|
|
20
47
|
|
|
21
48
|
module.exports = Sns
|
|
@@ -12,7 +12,7 @@ class MySQLPlugin extends Plugin {
|
|
|
12
12
|
constructor (...args) {
|
|
13
13
|
super(...args)
|
|
14
14
|
|
|
15
|
-
this.addSub(
|
|
15
|
+
this.addSub(`apm:${this.constructor.name}:query:start`, ({ sql, conf }) => {
|
|
16
16
|
const store = storage.getStore()
|
|
17
17
|
const childOf = store ? store.span : store
|
|
18
18
|
const span = this.tracer.startSpan('mysql.query', {
|
|
@@ -37,18 +37,18 @@ class MySQLPlugin extends Plugin {
|
|
|
37
37
|
this.enter(span, store)
|
|
38
38
|
})
|
|
39
39
|
|
|
40
|
-
this.addSub(
|
|
40
|
+
this.addSub(`apm:${this.constructor.name}:query:end`, () => {
|
|
41
41
|
this.exit()
|
|
42
42
|
})
|
|
43
43
|
|
|
44
|
-
this.addSub(
|
|
44
|
+
this.addSub(`apm:${this.constructor.name}:query:error`, err => {
|
|
45
45
|
if (err) {
|
|
46
46
|
const span = storage.getStore().span
|
|
47
47
|
span.setTag('error', err)
|
|
48
48
|
}
|
|
49
49
|
})
|
|
50
50
|
|
|
51
|
-
this.addSub(
|
|
51
|
+
this.addSub(`apm:${this.constructor.name}:query:async-end`, () => {
|
|
52
52
|
const span = storage.getStore().span
|
|
53
53
|
span.finish()
|
|
54
54
|
})
|
|
@@ -1,94 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
|
|
3
|
+
const MySQLPlugin = require('../../datadog-plugin-mysql/src')
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
return
|
|
9
|
-
const name = cmd && cmd.constructor && cmd.constructor.name
|
|
10
|
-
const isCommand = typeof cmd.execute === 'function'
|
|
11
|
-
const isSupported = name === 'Execute' || name === 'Query'
|
|
12
|
-
|
|
13
|
-
if (isCommand && isSupported) {
|
|
14
|
-
cmd.execute = wrapExecute(tracer, config, cmd.execute)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return addCommand.apply(this, arguments)
|
|
18
|
-
}
|
|
5
|
+
class MySQL2Plugin extends MySQLPlugin {
|
|
6
|
+
static get name () {
|
|
7
|
+
return 'mysql2'
|
|
19
8
|
}
|
|
20
9
|
}
|
|
21
10
|
|
|
22
|
-
|
|
23
|
-
const scope = tracer.scope()
|
|
24
|
-
const childOf = scope.active()
|
|
25
|
-
|
|
26
|
-
return function executeWithTrace (packet, connection) {
|
|
27
|
-
const connectionConfig = (connection && connection.config) || {}
|
|
28
|
-
const sql = this.statement ? this.statement.query : this.sql
|
|
29
|
-
const span = tracer.startSpan('mysql.query', {
|
|
30
|
-
childOf,
|
|
31
|
-
tags: {
|
|
32
|
-
[Tags.SPAN_KIND]: Tags.SPAN_KIND_RPC_CLIENT,
|
|
33
|
-
'service.name': config.service || `${tracer._service}-mysql`,
|
|
34
|
-
'resource.name': sql,
|
|
35
|
-
'span.type': 'sql',
|
|
36
|
-
'span.kind': 'client',
|
|
37
|
-
'db.type': 'mysql',
|
|
38
|
-
'db.user': connectionConfig.user,
|
|
39
|
-
'db.name': connectionConfig.database,
|
|
40
|
-
'out.host': connectionConfig.host,
|
|
41
|
-
'out.port': connectionConfig.port
|
|
42
|
-
}
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
analyticsSampler.sample(span, config.measured)
|
|
46
|
-
|
|
47
|
-
if (typeof this.onResult === 'function') {
|
|
48
|
-
this.onResult = wrapCallback(tracer, span, childOf, this.onResult)
|
|
49
|
-
} else {
|
|
50
|
-
this.on('error', error => span.addTags({ error }))
|
|
51
|
-
this.on('end', () => span.finish())
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
this.execute = execute
|
|
55
|
-
|
|
56
|
-
return scope.bind(execute, span).apply(this, arguments)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function wrapCallback (tracer, span, parent, done) {
|
|
61
|
-
return tracer.scope().bind((...args) => {
|
|
62
|
-
const [ error ] = args
|
|
63
|
-
span.addTags({ error })
|
|
64
|
-
|
|
65
|
-
span.finish()
|
|
66
|
-
|
|
67
|
-
done(...args)
|
|
68
|
-
}, parent)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
module.exports = [
|
|
72
|
-
{
|
|
73
|
-
name: 'mysql2',
|
|
74
|
-
file: 'lib/connection.js',
|
|
75
|
-
versions: ['>=1'],
|
|
76
|
-
patch (Connection, tracer, config) {
|
|
77
|
-
this.wrap(Connection.prototype, 'addCommand', createWrapAddCommand(tracer, config))
|
|
78
|
-
},
|
|
79
|
-
unpatch (Connection) {
|
|
80
|
-
this.unwrap(Connection.prototype, 'addCommand')
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
name: 'mysql2',
|
|
85
|
-
file: 'lib/commands/command.js',
|
|
86
|
-
versions: ['>=1'],
|
|
87
|
-
patch (Command, tracer, config) {
|
|
88
|
-
tracer.scope().bind(Command.prototype)
|
|
89
|
-
},
|
|
90
|
-
unpatch (Command, tracer) {
|
|
91
|
-
tracer.scope().unbind(Command.prototype)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
]
|
|
11
|
+
module.exports = MySQL2Plugin
|
|
@@ -115,10 +115,10 @@ function trace (tracer, config, req, res, handler) {
|
|
|
115
115
|
// TODO: Use CLS when it will be available in core.
|
|
116
116
|
span._nextReq = req
|
|
117
117
|
|
|
118
|
-
promise.then(
|
|
119
|
-
span
|
|
120
|
-
finish(span, config, req, res)
|
|
121
|
-
|
|
118
|
+
promise.then(
|
|
119
|
+
() => finish(span, config, req, res),
|
|
120
|
+
err => finish(span, config, req, res, err)
|
|
121
|
+
)
|
|
122
122
|
|
|
123
123
|
return promise
|
|
124
124
|
}
|
|
@@ -132,7 +132,8 @@ function addPage (req, page) {
|
|
|
132
132
|
})
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
function finish (span, config, req, res) {
|
|
135
|
+
function finish (span, config, req, res, err) {
|
|
136
|
+
span.setTag('error', err || !config.validateStatus(res.statusCode))
|
|
136
137
|
span.addTags({
|
|
137
138
|
'http.status_code': res.statusCode
|
|
138
139
|
})
|
|
@@ -142,8 +143,11 @@ function finish (span, config, req, res) {
|
|
|
142
143
|
|
|
143
144
|
function normalizeConfig (config) {
|
|
144
145
|
const hooks = getHooks(config)
|
|
146
|
+
const validateStatus = typeof config.validateStatus === 'function'
|
|
147
|
+
? config.validateStatus
|
|
148
|
+
: code => code < 500
|
|
145
149
|
|
|
146
|
-
return Object.assign({}, config, { hooks })
|
|
150
|
+
return Object.assign({}, config, { hooks, validateStatus })
|
|
147
151
|
}
|
|
148
152
|
|
|
149
153
|
function getHooks (config) {
|
|
@@ -2,18 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
const { LOG } = require('../../../ext/formats')
|
|
4
4
|
|
|
5
|
+
function chunkProxy (chunk, holder) {
|
|
6
|
+
return new Proxy(chunk, {
|
|
7
|
+
get (target, p, receiver) {
|
|
8
|
+
switch (p) {
|
|
9
|
+
case Symbol.toStringTag:
|
|
10
|
+
return Object.prototype.toString.call(target).slice(8, -1)
|
|
11
|
+
case 'dd':
|
|
12
|
+
return holder.dd
|
|
13
|
+
default:
|
|
14
|
+
return Reflect.get(target, p, receiver)
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
ownKeys (target) {
|
|
18
|
+
return ['dd', ...Reflect.ownKeys(target)]
|
|
19
|
+
},
|
|
20
|
+
getOwnPropertyDescriptor (target, p) {
|
|
21
|
+
return Reflect.getOwnPropertyDescriptor(p === 'dd' ? holder : target, p)
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
5
26
|
function createWrapWrite (tracer, config) {
|
|
6
27
|
return function wrapWrite (write) {
|
|
7
28
|
return function writeWithTrace (chunk, encoding, callback) {
|
|
8
29
|
const span = tracer.scope().active()
|
|
9
30
|
|
|
10
|
-
|
|
31
|
+
const holder = {}
|
|
32
|
+
tracer.inject(span, LOG, holder)
|
|
33
|
+
arguments[0] = chunkProxy(chunk, holder)
|
|
11
34
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
delete chunk.dd
|
|
15
|
-
|
|
16
|
-
return result
|
|
35
|
+
return write.apply(this, arguments)
|
|
17
36
|
}
|
|
18
37
|
}
|
|
19
38
|
}
|
|
@@ -42,15 +61,14 @@ function createWrapLog (tracer, config) {
|
|
|
42
61
|
return function logWithTrace (level, msg, meta, callback) {
|
|
43
62
|
const span = tracer.scope().active()
|
|
44
63
|
|
|
45
|
-
meta =
|
|
64
|
+
meta = meta || {}
|
|
46
65
|
|
|
47
|
-
|
|
66
|
+
const holder = {}
|
|
67
|
+
tracer.inject(span, LOG, holder)
|
|
48
68
|
|
|
49
|
-
|
|
69
|
+
arguments[2] = chunkProxy(meta, holder)
|
|
50
70
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return result
|
|
71
|
+
return log.apply(this, arguments)
|
|
54
72
|
}
|
|
55
73
|
}
|
|
56
74
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports = '2.
|
|
1
|
+
module.exports = '2.1.1'
|