newrelic 2.1.0 → 2.2.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/.eslintrc +1 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +5 -0
- package/NEWS.md +42 -6
- package/README.md +24 -12
- package/api.js +4 -5
- package/index.js +6 -2
- package/lib/agent.js +13 -1
- package/lib/collector/api.js +11 -9
- package/lib/config.js +67 -7
- package/lib/db/tracer.js +3 -3
- package/lib/feature_flags.js +1 -0
- package/lib/instrumentation/core/async_hooks.js +92 -0
- package/lib/instrumentation/core/child_process.js +35 -0
- package/lib/instrumentation/core/globals.js +4 -23
- package/lib/instrumentation/core/http.js +1 -1
- package/lib/instrumentation/hapi.js +67 -33
- package/lib/metrics/names.js +2 -1
- package/lib/shim/shim.js +36 -6
- package/lib/shim/webframework-shim.js +4 -2
- package/lib/stats/index.js +1 -1
- package/lib/system-info.js +5 -5
- package/lib/transaction/index.js +10 -2
- package/lib/transaction/tracer/index.js +1 -0
- package/lib/util/logger.js +4 -4
- package/lib/utilization/common.js +1 -1
- package/newrelic.js +1 -2
- package/package.json +2 -1
- package/stub_api.js +26 -9
package/.eslintrc
CHANGED
package/NEWS.md
CHANGED
|
@@ -1,14 +1,50 @@
|
|
|
1
1
|
|
|
2
|
-
### v2.
|
|
2
|
+
### v2.2.0 (2017-08-22):
|
|
3
|
+
* Added support for ignoring ranges of status codes.
|
|
4
|
+
|
|
5
|
+
The configuration `error_collector.ignore_status_codes` can now take ranges
|
|
6
|
+
of numbers. For example, `ignore_status_codes: ['400-404']` would ignore 400,
|
|
7
|
+
401, 402, 403, and 404.
|
|
8
|
+
|
|
9
|
+
* Fixed a bug when a custom collector port was provided in the configuration
|
|
10
|
+
that prevented redirected connections from working.
|
|
11
|
+
|
|
12
|
+
* Fixed a bug in `Shim#record` that could cause an exception when trying to
|
|
13
|
+
create a new segment as part of an ended/inactive transaction.
|
|
14
|
+
|
|
15
|
+
* Fixed issue with custom Hapi handlers causing an error.
|
|
16
|
+
|
|
17
|
+
Previously custom Hapi handlers defined using the `server.handler()` method
|
|
18
|
+
were causing the Hapi server to return a 500 error. Now they are correctly
|
|
19
|
+
handled and recorded as middleware functions.
|
|
20
|
+
|
|
21
|
+
* Transaction state is now maintained in `ChildProcess` event listeners.
|
|
22
|
+
|
|
23
|
+
* Updated examples and documentation regarding custom transaction creation.
|
|
24
|
+
|
|
25
|
+
All examples and documentation now point at the `newrelic.start*Transaction`
|
|
26
|
+
methods.
|
|
27
|
+
|
|
28
|
+
* Reducing logging verbosity in the SQL query obfuscator.
|
|
29
|
+
|
|
30
|
+
* Experimental instrumentation for `async/await`
|
|
31
|
+
|
|
32
|
+
This is experimental instrumentation and has not yet been tested in a wide
|
|
33
|
+
array of production environments. The feature is currently off by default
|
|
34
|
+
behind a feature flag. To enable this experimental instrumentation, add
|
|
35
|
+
`await_support: true` to the `feature_flag` setting in your agent config
|
|
36
|
+
file.
|
|
37
|
+
|
|
38
|
+
### v2.1.0 (2017-08-08):
|
|
3
39
|
* Improved metadata collection for AWS, Azure, GCE, and Pivotal Cloud Foundry.
|
|
4
40
|
|
|
5
41
|
* Fixed a bug in PG query obfuscation for `$` placeholders.
|
|
6
42
|
|
|
7
|
-
The agent used to mis-detect `$1` value placeholders as unmatched
|
|
8
|
-
strings causing the whole query to be obfuscated to just `?`.
|
|
9
|
-
placeholders are now correctly detected and obfuscated.
|
|
43
|
+
The agent used to mis-detect `$1` value placeholders as unmatched
|
|
44
|
+
dollar-quoted strings causing the whole query to be obfuscated to just `?`.
|
|
45
|
+
These placeholders are now correctly detected and obfuscated.
|
|
10
46
|
|
|
11
|
-
### v2.0.2 (2017-08-01)
|
|
47
|
+
### v2.0.2 (2017-08-01):
|
|
12
48
|
* Improved documentation for `newrelic.start*Transaction` and `TransactionHandle.`
|
|
13
49
|
|
|
14
50
|
Formatting for the `startWebTransaction` and `startBackgroundTransaction`
|
|
@@ -22,7 +58,7 @@
|
|
|
22
58
|
|
|
23
59
|
* Fixed unhandled rejection error caused by `ioredis` instrumentation.
|
|
24
60
|
|
|
25
|
-
### v2.0.1 (2017-07-25)
|
|
61
|
+
### v2.0.1 (2017-07-25):
|
|
26
62
|
* Fixed issue with transaction events not including correct duration values.
|
|
27
63
|
|
|
28
64
|
This issue was introduced in v2.0.0, and it has affected web transactions histogram
|
package/README.md
CHANGED
|
@@ -656,7 +656,7 @@ application, the agent can't tell when they should begin and end.
|
|
|
656
656
|
Read more at:
|
|
657
657
|
https://docs.newrelic.com/docs/agents/nodejs-agent/supported-features/nodejs-custom-instrumentation
|
|
658
658
|
|
|
659
|
-
#### newrelic.
|
|
659
|
+
#### newrelic.startWebTransaction(url, handle)
|
|
660
660
|
|
|
661
661
|
`url` is the name of the web transaction. It should be pretty static, not
|
|
662
662
|
including anything like user ids or any other data that is very specific to the
|
|
@@ -669,16 +669,22 @@ called within an active background transaction, it will create a new,
|
|
|
669
669
|
independent transaction and any calls within the `handle` will be bound to the
|
|
670
670
|
new web transaction.
|
|
671
671
|
|
|
672
|
-
Custom transactions
|
|
673
|
-
Timing for custom transaction starts from when the returned wrapped function is
|
|
674
|
-
called until `endTransaction()` is called.
|
|
672
|
+
Custom transactions can be ended within the `handle` in one of three ways:
|
|
675
673
|
|
|
676
|
-
|
|
674
|
+
1. Call `transaction.end()`. The `transaction` can be received by calling
|
|
675
|
+
`newrelic.getTransaction()` first thing in the handler function. Then,
|
|
676
|
+
when you call `transaction.end()` timing will stop.
|
|
677
|
+
2. Return a promise. The transaction will end when the promise resolves or
|
|
678
|
+
rejects.
|
|
679
|
+
3. Do neither. If no promise is returned, and `getTransaction()` isn't
|
|
680
|
+
called, the transaction will end immediately after the handle returns.
|
|
681
|
+
|
|
682
|
+
#### newrelic.startBackgroundTransaction(name [, group], handle)
|
|
677
683
|
|
|
678
684
|
`name` is the name of the job. It should be pretty static, and not include job
|
|
679
685
|
ids or anything very specific to that run of the job. `group` is optional, and
|
|
680
686
|
allows you to group types of jobs together. This should follow similar rules as
|
|
681
|
-
the `name`. `handle` is a function that
|
|
687
|
+
the `name`. `handle` is a function that encompasses your background job. Both
|
|
682
688
|
custom and auto instrumentation will be captured as part of the transaction.
|
|
683
689
|
|
|
684
690
|
If called within an active background transaction, it will act as a nested
|
|
@@ -686,14 +692,20 @@ tracer. If called within an active web transaction, it will create a new
|
|
|
686
692
|
transaction and any calls within the `handle` will be bound to the new,
|
|
687
693
|
independent background transaction.
|
|
688
694
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
695
|
+
1. Call `transaction.end()`. The `transaction` can be received by calling
|
|
696
|
+
`newrelic.getTransaction()` first thing in the handler function. Then,
|
|
697
|
+
when you call `transaction.end()` timing will stop.
|
|
698
|
+
2. Return a promise. The transaction will end when the promise resolves or
|
|
699
|
+
rejects.
|
|
700
|
+
3. Do neither. If no promise is returned, and `getTransaction()` isn't
|
|
701
|
+
called, the transaction will end immediately after the handle returns.
|
|
692
702
|
|
|
693
|
-
#### newrelic.
|
|
703
|
+
#### newrelic.getTransaction()
|
|
694
704
|
|
|
695
|
-
This takes no arguments and
|
|
696
|
-
|
|
705
|
+
This takes no arguments and returns the currently active transaction. If the
|
|
706
|
+
returned transaction is a custom one, started with either `startWebTransaction`
|
|
707
|
+
or `startBackgroundTransaction`, then `transaction.end()` must be called to
|
|
708
|
+
end the transaction.
|
|
697
709
|
|
|
698
710
|
#### newrelic.createTracer(name, handle)
|
|
699
711
|
|
package/api.js
CHANGED
|
@@ -91,6 +91,7 @@ API.prototype.setTransactionName = function setTransactionName(name) {
|
|
|
91
91
|
return
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
logger.trace('Setting transaction %s name to %s', transaction.id, name)
|
|
94
95
|
transaction.forceName = NAMES.CUSTOM + '/' + name
|
|
95
96
|
}
|
|
96
97
|
|
|
@@ -623,8 +624,7 @@ API.prototype.createTracer = function createTracer(name, callback) {
|
|
|
623
624
|
}
|
|
624
625
|
|
|
625
626
|
API.prototype.createWebTransaction = util.deprecate(
|
|
626
|
-
createWebTransaction,
|
|
627
|
-
[
|
|
627
|
+
createWebTransaction, [
|
|
628
628
|
'API#createWebTransaction is being deprecated!',
|
|
629
629
|
'Please use API#startWebTransaction for transaction creation',
|
|
630
630
|
'and API#getTransaction for transaction management including',
|
|
@@ -884,8 +884,7 @@ function startBackgroundTransaction(name, group, handle) {
|
|
|
884
884
|
}
|
|
885
885
|
|
|
886
886
|
API.prototype.createBackgroundTransaction = util.deprecate(
|
|
887
|
-
createBackgroundTransaction,
|
|
888
|
-
[
|
|
887
|
+
createBackgroundTransaction, [
|
|
889
888
|
'API#createBackgroundTransaction is being deprecated!',
|
|
890
889
|
'Please use API#startBackgroundTransaction for transaction creation',
|
|
891
890
|
'and API#getTransaction for transaction management including',
|
|
@@ -1074,7 +1073,7 @@ API.prototype.recordMetric = function recordMetric(name, value) {
|
|
|
1074
1073
|
|
|
1075
1074
|
for (var i = 0, l = required.length; i < l; ++i) {
|
|
1076
1075
|
if (typeof value[required[i]] !== 'number') {
|
|
1077
|
-
logger.warn('Metric object must include
|
|
1076
|
+
logger.warn('Metric object must include %s as a number', required[i])
|
|
1078
1077
|
return
|
|
1079
1078
|
}
|
|
1080
1079
|
|
package/index.js
CHANGED
|
@@ -17,6 +17,9 @@ if (require.cache.__NR_cache) {
|
|
|
17
17
|
'Attempting to load a second copy of newrelic from %s, using cache instead',
|
|
18
18
|
__dirname
|
|
19
19
|
)
|
|
20
|
+
if (require.cache.__NR_cache.agent) {
|
|
21
|
+
require.cache.__NR_cache.agent.recordSupportability('Agent/DoubleLoad')
|
|
22
|
+
}
|
|
20
23
|
module.exports = require.cache.__NR_cache
|
|
21
24
|
} else {
|
|
22
25
|
initialize()
|
|
@@ -88,9 +91,10 @@ function initialize() {
|
|
|
88
91
|
agent.start(function cb_start(error) {
|
|
89
92
|
if (!error) {
|
|
90
93
|
// TODO: After deprecating Node 0.10 and 0.12, simplify this regex.
|
|
94
|
+
// TODO: As new versions come out, make sure to update Angler metrics.
|
|
91
95
|
var nodeMajor = /^v?((?:0\.)?\d+)/.exec(process.version)
|
|
92
|
-
agent.
|
|
93
|
-
'
|
|
96
|
+
agent.recordSupportability(
|
|
97
|
+
'Version/' + ((nodeMajor && nodeMajor[1]) || 'unknown')
|
|
94
98
|
)
|
|
95
99
|
|
|
96
100
|
return logger.debug("New Relic for Node.js is connected to New Relic.")
|
package/lib/agent.js
CHANGED
|
@@ -107,6 +107,7 @@ function Agent(config) {
|
|
|
107
107
|
this.segmentsCreatedInHarvest = 0
|
|
108
108
|
this.segmentsClearedInHarvest = 0
|
|
109
109
|
this.activeTransactions = 0
|
|
110
|
+
this.transactionCreatedInHarvest = 0
|
|
110
111
|
|
|
111
112
|
// Hidden class optimizations.
|
|
112
113
|
this.harvesterHandle = null
|
|
@@ -286,9 +287,11 @@ Agent.prototype.harvest = function harvest(callback) {
|
|
|
286
287
|
harvestCleared: this.segmentsClearedInHarvest,
|
|
287
288
|
activeTransactions: this.activeTransactions
|
|
288
289
|
}, 'Entity stats on harvest')
|
|
290
|
+
this.recordSupportability('Transactions/Created', this.transactionCreatedInHarvest)
|
|
289
291
|
|
|
290
292
|
this.segmentsCreatedInHarvest = 0
|
|
291
293
|
this.segmentsClearedInHarvest = 0
|
|
294
|
+
this.transactionCreatedInHarvest = 0
|
|
292
295
|
|
|
293
296
|
if (!this.collector.isConnected()) {
|
|
294
297
|
return process.nextTick(function cb_nextTick() {
|
|
@@ -994,7 +997,7 @@ Agent.prototype._transactionFinished = function _transactionFinished(transaction
|
|
|
994
997
|
logger.debug('Ignoring %s (%s).', transaction.name, transaction.id)
|
|
995
998
|
}
|
|
996
999
|
|
|
997
|
-
this.activeTransactions
|
|
1000
|
+
--this.activeTransactions
|
|
998
1001
|
this.totalActiveSegments -= transaction.numSegments
|
|
999
1002
|
this.segmentsClearedInHarvest += transaction.numSegments
|
|
1000
1003
|
}
|
|
@@ -1008,4 +1011,13 @@ Agent.prototype.getTransaction = function getTransaction() {
|
|
|
1008
1011
|
return this.tracer.getTransaction()
|
|
1009
1012
|
}
|
|
1010
1013
|
|
|
1014
|
+
Agent.prototype.recordSupportability = function recordSupportability(name, value) {
|
|
1015
|
+
var metric = this.metrics.getOrCreateMetric(NAMES.SUPPORTABILITY.NODEJS + '/' + name)
|
|
1016
|
+
if (value != null) {
|
|
1017
|
+
metric.recordValue(value)
|
|
1018
|
+
} else {
|
|
1019
|
+
metric.incrementCallCount()
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1011
1023
|
module.exports = Agent
|
package/lib/collector/api.js
CHANGED
|
@@ -42,11 +42,11 @@ var HTTP_LOL_COLLECTOR = 503
|
|
|
42
42
|
function dumpErrors(errors, name) {
|
|
43
43
|
var index = 1
|
|
44
44
|
|
|
45
|
-
errors.forEach(function
|
|
45
|
+
errors.forEach(function forEachError(error) {
|
|
46
46
|
logger.trace(error, "Error %s during %s:", index++, name)
|
|
47
47
|
|
|
48
48
|
if (error.laterErrors) {
|
|
49
|
-
error.laterErrors.forEach(function
|
|
49
|
+
error.laterErrors.forEach(function forEachLaterError(laterError) {
|
|
50
50
|
logger.trace(laterError, "Error %s during %s:", index++, name)
|
|
51
51
|
})
|
|
52
52
|
}
|
|
@@ -115,7 +115,7 @@ CollectorAPI.prototype.connect = function connect(callback) {
|
|
|
115
115
|
var id = setTimeout(function again() {
|
|
116
116
|
api._login(retry)
|
|
117
117
|
}, backoff.interval * TO_MILLIS)
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
if (id.unref) {
|
|
120
120
|
id.unref()
|
|
121
121
|
}
|
|
@@ -129,7 +129,7 @@ CollectorAPI.prototype._login = function _login(callback) {
|
|
|
129
129
|
var agent = this._agent
|
|
130
130
|
|
|
131
131
|
|
|
132
|
-
methods.redirect.invoke(null, function
|
|
132
|
+
methods.redirect.invoke(null, function redirectCb(error, collector, body) {
|
|
133
133
|
if (error) return callback(error, collector, body)
|
|
134
134
|
if (!collector) {
|
|
135
135
|
logger.error(
|
|
@@ -152,18 +152,20 @@ CollectorAPI.prototype._login = function _login(callback) {
|
|
|
152
152
|
)
|
|
153
153
|
|
|
154
154
|
agent.config.host = parts[0]
|
|
155
|
-
|
|
156
|
-
agent.config.port = parts[1]
|
|
157
|
-
}
|
|
155
|
+
agent.config.port = parts[1] || (agent.config.ssl ? 443 : 80)
|
|
158
156
|
}
|
|
159
157
|
}
|
|
160
158
|
|
|
159
|
+
_getFacts()
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
function _getFacts() {
|
|
161
163
|
facts(agent, function getEnvDict(environmentDict) {
|
|
162
164
|
// The collector really likes arrays.
|
|
163
165
|
// In fact, it kind of insists on them.
|
|
164
166
|
var environment = [environmentDict]
|
|
165
167
|
|
|
166
|
-
methods.handshake.invoke(environment, function
|
|
168
|
+
methods.handshake.invoke(environment, function handshakeCb(error, config, body) {
|
|
167
169
|
if (error) return callback(error, config, body)
|
|
168
170
|
if (!config || !config.agent_run_id) {
|
|
169
171
|
return callback(new Error("No agent run ID received from handshake."), config)
|
|
@@ -183,7 +185,7 @@ CollectorAPI.prototype._login = function _login(callback) {
|
|
|
183
185
|
callback(null, config, body)
|
|
184
186
|
})
|
|
185
187
|
})
|
|
186
|
-
}
|
|
188
|
+
}
|
|
187
189
|
}
|
|
188
190
|
|
|
189
191
|
/**
|
package/lib/config.js
CHANGED
|
@@ -12,6 +12,7 @@ var exists = fs.existsSync || path.existsSync
|
|
|
12
12
|
var safeJSON = require('./util/safe-json')
|
|
13
13
|
var stringifySync = safeJSON.stringifySync
|
|
14
14
|
var parse = safeJSON.parse
|
|
15
|
+
var semver = require('semver')
|
|
15
16
|
var os = require('os')
|
|
16
17
|
var logger
|
|
17
18
|
|
|
@@ -727,6 +728,19 @@ Config.prototype.logUnknown = function logUnknown(json, key) {
|
|
|
727
728
|
)
|
|
728
729
|
}
|
|
729
730
|
|
|
731
|
+
/**
|
|
732
|
+
* Return the availability of async_hook for use by the agent.
|
|
733
|
+
*/
|
|
734
|
+
Config.prototype.checkAsyncHookStatus = function checkAsyncHookStatus() {
|
|
735
|
+
return (
|
|
736
|
+
(
|
|
737
|
+
semver.satisfies(process.version, '>=8') ||
|
|
738
|
+
semver.prerelease(process.version)
|
|
739
|
+
) &&
|
|
740
|
+
this.feature_flag.await_support
|
|
741
|
+
)
|
|
742
|
+
}
|
|
743
|
+
|
|
730
744
|
/**
|
|
731
745
|
* Gets the user set host display name. If not provided, it returns the default value.
|
|
732
746
|
*
|
|
@@ -948,13 +962,59 @@ Config.prototype._fromEnvironment = function _fromEnvironment(metadata, data) {
|
|
|
948
962
|
* based on special properties of configuration values should go here as well.
|
|
949
963
|
*/
|
|
950
964
|
Config.prototype._canonicalize = function _canonicalize() {
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
965
|
+
function parseCodes(codes) {
|
|
966
|
+
// range does not support negative values
|
|
967
|
+
function parseRange(range, parsed) {
|
|
968
|
+
var split = range.split('-')
|
|
969
|
+
if (split.length !== 2) {
|
|
970
|
+
logger.warn('Failed to parse range %s', range)
|
|
971
|
+
return parsed
|
|
972
|
+
}
|
|
973
|
+
if (split[0] === '') { // catch negative code. ex. -7
|
|
974
|
+
return parsed.push(parseInt(range, 10))
|
|
975
|
+
}
|
|
976
|
+
var lower = parseInt(split[0], 10)
|
|
977
|
+
var upper = parseInt(split[1], 10)
|
|
978
|
+
if (Number.isNaN(lower) || Number.isNaN(upper)) {
|
|
979
|
+
logger.warn('Range must contain two numbers %s', range)
|
|
980
|
+
return parsed
|
|
981
|
+
}
|
|
982
|
+
if (lower > upper) {
|
|
983
|
+
logger.warn('Range must start with lower bound %s', range)
|
|
984
|
+
} else if (lower < 0 || upper > 1000) {
|
|
985
|
+
logger.warn('Range must be between 0 and 1000 %s', range)
|
|
986
|
+
} else { // success
|
|
987
|
+
for (var i = lower; i <= upper; i++) {
|
|
988
|
+
parsed.push(i)
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
return parsed
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
var parsedCodes = []
|
|
995
|
+
for (var i = 0; i < codes.length; i++) {
|
|
996
|
+
var code = codes[i]
|
|
997
|
+
var parsedCode
|
|
998
|
+
if (typeof code === 'string' && code.indexOf('-') !== -1) {
|
|
999
|
+
parseRange(code, parsedCodes)
|
|
1000
|
+
} else {
|
|
1001
|
+
parsedCode = parseInt(code, 10)
|
|
1002
|
+
if (!Number.isNaN(parsedCode)) {
|
|
1003
|
+
parsedCodes.push(parsedCode)
|
|
1004
|
+
} else {
|
|
1005
|
+
logger.warn('Failed to parse status code %s', code)
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
return parsedCodes
|
|
956
1010
|
}
|
|
957
1011
|
|
|
1012
|
+
var statusCodes = this.error_collector && this.error_collector.ignore_status_codes
|
|
1013
|
+
if (statusCodes) {
|
|
1014
|
+
this.error_collector.ignore_status_codes = parseCodes(statusCodes)
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
|
|
958
1018
|
var logAliases = {
|
|
959
1019
|
'verbose': 'trace',
|
|
960
1020
|
'debugging': 'debug',
|
|
@@ -1020,10 +1080,10 @@ Config.prototype.measureInternal = function measureInternal(suffix, duration) {
|
|
|
1020
1080
|
Config.prototype.validateFlags = function validateFlags() {
|
|
1021
1081
|
Object.keys(this.feature_flag).forEach(function cb_forEach(key) {
|
|
1022
1082
|
if (feature_flag.released.indexOf(key) > -1) {
|
|
1023
|
-
logger.warn('Feature flag
|
|
1083
|
+
logger.warn('Feature flag %s has been released', key)
|
|
1024
1084
|
}
|
|
1025
1085
|
if (feature_flag.unreleased.indexOf(key) > -1) {
|
|
1026
|
-
logger.warn('Feature flag
|
|
1086
|
+
logger.warn('Feature flag %s has been deprecated', key)
|
|
1027
1087
|
}
|
|
1028
1088
|
})
|
|
1029
1089
|
}
|
package/lib/db/tracer.js
CHANGED
|
@@ -56,15 +56,15 @@ QueryTracer.prototype.addQuery = function addQuery(segment, type, query, trace)
|
|
|
56
56
|
|
|
57
57
|
switch (this.config.transaction_tracer.record_sql) {
|
|
58
58
|
case 'raw':
|
|
59
|
-
logger.
|
|
59
|
+
logger.trace('recording raw sql')
|
|
60
60
|
segment.parameters.sql = slowQuery.query
|
|
61
61
|
break
|
|
62
62
|
case 'obfuscated':
|
|
63
|
-
logger.
|
|
63
|
+
logger.trace('recording obfuscated sql')
|
|
64
64
|
segment.parameters.sql_obfuscated = slowQuery.obfuscated
|
|
65
65
|
break
|
|
66
66
|
default:
|
|
67
|
-
logger.
|
|
67
|
+
logger.trace(
|
|
68
68
|
'not collecting slow-query because transaction_tracer.record_sql was set to %s',
|
|
69
69
|
this.config.transaction_tracer.record_sql
|
|
70
70
|
)
|
package/lib/feature_flags.js
CHANGED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var logger = require('../../logger').child({component: 'async_hooks'})
|
|
4
|
+
var promInit = require('../promise')
|
|
5
|
+
var semver = require('semver')
|
|
6
|
+
|
|
7
|
+
module.exports = initialize
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The spec for the native `Promise` class.
|
|
11
|
+
*/
|
|
12
|
+
var STATIC_PROMISE_METHODS = ['accept', 'all', 'defer', 'race', 'reject', 'resolve']
|
|
13
|
+
var NATIVE_PROMISE_SPEC = {
|
|
14
|
+
name: 'global',
|
|
15
|
+
constructor: 'Promise',
|
|
16
|
+
executor: true,
|
|
17
|
+
$proto: {
|
|
18
|
+
then: ['then', 'chain'],
|
|
19
|
+
catch: ['catch']
|
|
20
|
+
},
|
|
21
|
+
$static: {
|
|
22
|
+
$copy: STATIC_PROMISE_METHODS,
|
|
23
|
+
cast: STATIC_PROMISE_METHODS
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function initialize(agent) {
|
|
28
|
+
var enableHooks = agent.config.checkAsyncHookStatus()
|
|
29
|
+
if (enableHooks && tryAsyncHooks(agent)) {
|
|
30
|
+
logger.debug('Using async_hooks.')
|
|
31
|
+
} else if (semver.satisfies(process.version, '>=0.12')) {
|
|
32
|
+
logger.debug('Using promise instrumentation.')
|
|
33
|
+
promInit(agent, global, NATIVE_PROMISE_SPEC)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function tryAsyncHooks(agent) {
|
|
38
|
+
var asyncHooks = null
|
|
39
|
+
try {
|
|
40
|
+
asyncHooks = require('async_hooks')
|
|
41
|
+
} catch (e) {
|
|
42
|
+
logger.info(e, 'Not using async_hooks module.')
|
|
43
|
+
return false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// this map is reused to track the segment that was active when
|
|
47
|
+
// the before callback is called to be replaced in the after callback
|
|
48
|
+
var segmentMap = new Map()
|
|
49
|
+
module.exports._segmentMap = segmentMap
|
|
50
|
+
|
|
51
|
+
asyncHooks.createHook({
|
|
52
|
+
init: function initHook(id, type) {
|
|
53
|
+
var transaction = agent.getTransaction()
|
|
54
|
+
|
|
55
|
+
if (!transaction || type !== 'PROMISE') {
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
segmentMap.set(id, agent.tracer.getSegment())
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
before: function beforeHook(id) {
|
|
63
|
+
var hookSegment = segmentMap.get(id)
|
|
64
|
+
|
|
65
|
+
if (!hookSegment) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
segmentMap.set(id, agent.tracer.getSegment())
|
|
70
|
+
agent.tracer.segment = hookSegment
|
|
71
|
+
},
|
|
72
|
+
after: function afterHook(id) {
|
|
73
|
+
var hookSegment = segmentMap.get(id)
|
|
74
|
+
|
|
75
|
+
// hookSegment is the segment that was active before the promise
|
|
76
|
+
// executed. If the promise is executing before a segment has been
|
|
77
|
+
// restored, hookSegment will be null and should be restored. Thus
|
|
78
|
+
// undefined is the only invalid value here.
|
|
79
|
+
if (hookSegment === undefined) {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
segmentMap.set(id, agent.tracer.getSegment())
|
|
84
|
+
agent.tracer.segment = hookSegment
|
|
85
|
+
},
|
|
86
|
+
destroy: function destHook(id) {
|
|
87
|
+
segmentMap.delete(id)
|
|
88
|
+
}
|
|
89
|
+
}).enable()
|
|
90
|
+
|
|
91
|
+
return true
|
|
92
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
var wrap = require('../../shimmer').wrapMethod
|
|
4
|
+
var isWrapped = require('../../shimmer').isWrapped
|
|
4
5
|
|
|
5
6
|
module.exports = initialize
|
|
6
7
|
|
|
@@ -12,4 +13,38 @@ function initialize(agent, childProcess) {
|
|
|
12
13
|
function wrapMethod(fn, method) {
|
|
13
14
|
return agent.tracer.wrapFunctionLast('child_process.' + method, null, fn)
|
|
14
15
|
}
|
|
16
|
+
|
|
17
|
+
var childProcessProto = childProcess && childProcess.ChildProcess
|
|
18
|
+
// ChildProcess is exposed on Node 4 and higher
|
|
19
|
+
if (childProcessProto) {
|
|
20
|
+
wrapChildProcessCls(childProcess.ChildProcess)
|
|
21
|
+
} else {
|
|
22
|
+
wrap(childProcess, 'childProcess', ['fork', 'spawn'], wrapSpawn)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function wrapSpawn(fn) {
|
|
26
|
+
return function wrapped() {
|
|
27
|
+
var child = fn.apply(this, arguments)
|
|
28
|
+
if (child && child.constructor && child.constructor.prototype) {
|
|
29
|
+
wrapChildProcessCls(child.constructor)
|
|
30
|
+
}
|
|
31
|
+
return child
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function wrapChildProcessCls(childProcessCls) {
|
|
36
|
+
if (!childProcessCls || !childProcessCls.prototype ||
|
|
37
|
+
isWrapped(childProcessCls.prototype.on)) {
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
wrap(
|
|
42
|
+
childProcessCls.prototype,
|
|
43
|
+
'childProcess.ChildProcess.prototype',
|
|
44
|
+
'on',
|
|
45
|
+
function wrapEmit(fn) {
|
|
46
|
+
return agent.tracer.wrapFunctionNoSegment(fn, ['on', 'addListener'])
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
}
|
|
15
50
|
}
|
|
@@ -1,29 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
var asyncHookInstrumentation = require('./async_hooks')
|
|
3
4
|
var events = require('events')
|
|
4
5
|
var wrap = require('../../shimmer').wrapMethod
|
|
5
|
-
var promInit = require('../promise')
|
|
6
|
-
var semver = require('semver')
|
|
7
6
|
|
|
8
7
|
module.exports = initialize
|
|
9
8
|
|
|
10
|
-
/**
|
|
11
|
-
* The spec for the native `Promise` class.
|
|
12
|
-
*/
|
|
13
|
-
var STATIC_PROMISE_METHODS = ['accept', 'all', 'defer', 'race', 'reject', 'resolve']
|
|
14
|
-
var NATIVE_PROMISE_SPEC = {
|
|
15
|
-
name: 'global',
|
|
16
|
-
constructor: 'Promise',
|
|
17
|
-
executor: true,
|
|
18
|
-
$proto: {
|
|
19
|
-
then: ['then', 'chain'],
|
|
20
|
-
catch: ['catch']
|
|
21
|
-
},
|
|
22
|
-
$static: {
|
|
23
|
-
$copy: STATIC_PROMISE_METHODS,
|
|
24
|
-
cast: STATIC_PROMISE_METHODS
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
9
|
|
|
28
10
|
function initialize(agent) {
|
|
29
11
|
// `_fatalException` is an undocumented feature of domains, introduced in
|
|
@@ -63,10 +45,9 @@ function initialize(agent) {
|
|
|
63
45
|
}
|
|
64
46
|
)
|
|
65
47
|
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
48
|
+
// This will initialize the most optimal native-promise instrumentation
|
|
49
|
+
// that we have available.
|
|
50
|
+
asyncHookInstrumentation(agent)
|
|
70
51
|
}
|
|
71
52
|
|
|
72
53
|
function listenerCount(emitter, evnt) {
|
|
@@ -131,7 +131,7 @@ function wrapEmitWithTransaction(agent, emit) {
|
|
|
131
131
|
var start = parseFloat(qtime)
|
|
132
132
|
|
|
133
133
|
if (isNaN(start)) {
|
|
134
|
-
logger.warn('Queue time header parsed as NaN ('
|
|
134
|
+
logger.warn('Queue time header parsed as NaN (%s)', qtime)
|
|
135
135
|
} else {
|
|
136
136
|
// nano seconds
|
|
137
137
|
if (start > 1e18) start = start / 1e6
|
|
@@ -23,6 +23,33 @@ module.exports = function initialize(agent, hapi, moduleName, shim) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
function wrapServer(shim, Server) {
|
|
26
|
+
// wrap server.handler function that registers a new handler type
|
|
27
|
+
// the second argument is expected to be a function that generates the handler function
|
|
28
|
+
shim.wrap(Server.prototype, 'handler', function wrapHandler(shim, original) {
|
|
29
|
+
return function wrappedHandler() {
|
|
30
|
+
var args = shim.argsToArray.apply(shim, arguments)
|
|
31
|
+
|
|
32
|
+
var handlerGenerator = args[1]
|
|
33
|
+
if (typeof handlerGenerator === 'function') {
|
|
34
|
+
args[1] = wrapGenerator(handlerGenerator)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return original.apply(this, args)
|
|
38
|
+
|
|
39
|
+
function wrapGenerator(generator) {
|
|
40
|
+
return function wrappedGenerator() {
|
|
41
|
+
var generatorArgs = shim.argsToArray.apply(shim, arguments)
|
|
42
|
+
var handler = generator.apply(this, generatorArgs)
|
|
43
|
+
if (typeof handler === 'function') {
|
|
44
|
+
var route = generatorArgs[0]
|
|
45
|
+
return wrapRouteHandler(shim, handler, route && route.path)
|
|
46
|
+
}
|
|
47
|
+
return handler
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
26
53
|
shim.wrap(Server.prototype, 'route', function wrapRoute(shim, original) {
|
|
27
54
|
return function wrappedRoute() {
|
|
28
55
|
var args = shim.argsToArray.apply(shim, arguments)
|
|
@@ -35,44 +62,19 @@ function wrapServer(shim, Server) {
|
|
|
35
62
|
var route = args[0]
|
|
36
63
|
// handler function could be on the route object, or on a nested config object
|
|
37
64
|
if (route.config) {
|
|
38
|
-
|
|
65
|
+
_wrapRouteHandler(shim, route.config, route.path)
|
|
39
66
|
} else {
|
|
40
|
-
|
|
67
|
+
_wrapRouteHandler(shim, route)
|
|
41
68
|
}
|
|
42
69
|
|
|
43
70
|
return original.apply(this, args)
|
|
44
71
|
|
|
45
|
-
function
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
shim.recordRender(reply, 'view')
|
|
52
|
-
}
|
|
53
|
-
return original.apply(this, arguments)
|
|
54
|
-
}
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
return shim.recordMiddleware(wrappedHandler, {
|
|
58
|
-
route: path || route.path,
|
|
59
|
-
req: function getReq(shim, fn, fnName, args) {
|
|
60
|
-
var request = args[0]
|
|
61
|
-
if (request && request.raw) {
|
|
62
|
-
return request.raw.req
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
next: function wrapNext(shim, fn, fnName, args, wrap) {
|
|
66
|
-
var reply = args[1]
|
|
67
|
-
if (!shim.isFunction(reply)) return
|
|
68
|
-
wrap(reply, 'response', true)
|
|
69
|
-
},
|
|
70
|
-
params: function getParams(shim, fn, fnName, args, req) {
|
|
71
|
-
var req = args[0]
|
|
72
|
-
if (!req) return
|
|
73
|
-
return req.params
|
|
74
|
-
}
|
|
75
|
-
})
|
|
72
|
+
function _wrapRouteHandler(shim, container, path) {
|
|
73
|
+
if (typeof container.handler !== 'function') {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
shim.wrap(container, 'handler', function wrapHandler(shim, handler) {
|
|
77
|
+
return wrapRouteHandler(shim, handler, path || route.path)
|
|
76
78
|
})
|
|
77
79
|
}
|
|
78
80
|
}
|
|
@@ -109,3 +111,35 @@ function wrapCreateServer(shim, hapi) {
|
|
|
109
111
|
}
|
|
110
112
|
})
|
|
111
113
|
}
|
|
114
|
+
|
|
115
|
+
function wrapRouteHandler(shim, handler, path) {
|
|
116
|
+
var wrappedHandler = shim.wrap(handler, function wrapHandler(shim, original) {
|
|
117
|
+
return function wrapped() {
|
|
118
|
+
var reply = arguments[1]
|
|
119
|
+
if (reply) {
|
|
120
|
+
shim.recordRender(reply, 'view')
|
|
121
|
+
}
|
|
122
|
+
return original.apply(this, arguments)
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
return shim.recordMiddleware(wrappedHandler, {
|
|
127
|
+
route: path,
|
|
128
|
+
req: function getReq(shim, fn, fnName, args) {
|
|
129
|
+
var request = args[0]
|
|
130
|
+
if (request && request.raw) {
|
|
131
|
+
return request.raw.req
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
next: function wrapNext(shim, fn, fnName, args, wrap) {
|
|
135
|
+
var reply = args[1]
|
|
136
|
+
if (!shim.isFunction(reply)) return
|
|
137
|
+
wrap(reply, 'response', true)
|
|
138
|
+
},
|
|
139
|
+
params: function getParams(shim, fn, fnName, args, req) {
|
|
140
|
+
var req = args[0]
|
|
141
|
+
if (!req) return
|
|
142
|
+
return req.params
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
}
|
package/lib/metrics/names.js
CHANGED
|
@@ -150,7 +150,8 @@ var SUPPORTABILITY = {
|
|
|
150
150
|
EVENTS: 'Supportability/Events',
|
|
151
151
|
API: 'Supportability/API',
|
|
152
152
|
UTILIZATION: 'Supportability/utilization',
|
|
153
|
-
DEPENDENCIES: 'Supportability/InstalledDependencies'
|
|
153
|
+
DEPENDENCIES: 'Supportability/InstalledDependencies',
|
|
154
|
+
NODEJS: 'Supportability/Nodejs'
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
var UTILIZATION = {
|
package/lib/shim/shim.js
CHANGED
|
@@ -127,6 +127,7 @@ Shim.prototype.record = record
|
|
|
127
127
|
Shim.prototype.isWrapped = isWrapped
|
|
128
128
|
Shim.prototype.unwrap = unwrap
|
|
129
129
|
Shim.prototype.getSegment = getSegment
|
|
130
|
+
Shim.prototype.getActiveSegment = getActiveSegment
|
|
130
131
|
Shim.prototype.storeSegment = storeSegment
|
|
131
132
|
Shim.prototype.bindCallbackSegment = bindCallbackSegment
|
|
132
133
|
Shim.prototype.applySegment = applySegment
|
|
@@ -875,8 +876,8 @@ function record(nodule, properties, recordNamer) {
|
|
|
875
876
|
shim.logger.trace('Wrapping "%s" with metric recording.', name)
|
|
876
877
|
|
|
877
878
|
return function wrapper() {
|
|
878
|
-
// See if we're in
|
|
879
|
-
var parent = shim.
|
|
879
|
+
// See if we're in an active transaction.
|
|
880
|
+
var parent = shim.getActiveSegment()
|
|
880
881
|
if (!parent) {
|
|
881
882
|
shim.logger.debug('Not recording function %s, not in a transaction.', name)
|
|
882
883
|
return fn.apply(this, arguments)
|
|
@@ -911,6 +912,9 @@ function record(nodule, properties, recordNamer) {
|
|
|
911
912
|
})
|
|
912
913
|
|
|
913
914
|
// Apply the function, and (if it returned a stream) bind that too.
|
|
915
|
+
// If you are looking here Geoff, the reason there is no check for
|
|
916
|
+
// `segment` is because it should be guaranteed by the parent and active
|
|
917
|
+
// transaction check at the beginning of this function. :)
|
|
914
918
|
var ret = _applyRecorderSegment(segment, this, args, segDesc)
|
|
915
919
|
if (ret) {
|
|
916
920
|
if (segDesc.stream) {
|
|
@@ -1141,8 +1145,8 @@ function bindCallbackSegment(args, cbIdx, parentSegment) {
|
|
|
1141
1145
|
}
|
|
1142
1146
|
|
|
1143
1147
|
/**
|
|
1144
|
-
* Retrieves the segment associated with the given object, or the
|
|
1145
|
-
*
|
|
1148
|
+
* Retrieves the segment associated with the given object, or the current
|
|
1149
|
+
* segment if no object is given.
|
|
1146
1150
|
*
|
|
1147
1151
|
* - `getSegment([obj])`
|
|
1148
1152
|
*
|
|
@@ -1151,8 +1155,8 @@ function bindCallbackSegment(args, cbIdx, parentSegment) {
|
|
|
1151
1155
|
* @param {*} [obj] - The object to retrieve a segment from.
|
|
1152
1156
|
*
|
|
1153
1157
|
* @return {?TraceSegment} The trace segment associated with the given object or
|
|
1154
|
-
* the
|
|
1155
|
-
*
|
|
1158
|
+
* the current segment if no object is provided or no segment is associated
|
|
1159
|
+
* with the object.
|
|
1156
1160
|
*/
|
|
1157
1161
|
function getSegment(obj) {
|
|
1158
1162
|
if (obj && obj.__NR_segment) {
|
|
@@ -1161,6 +1165,32 @@ function getSegment(obj) {
|
|
|
1161
1165
|
return this.tracer.getSegment()
|
|
1162
1166
|
}
|
|
1163
1167
|
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* Retrieves the segment associated with the given object, or the currently
|
|
1171
|
+
* active segment if no object is given.
|
|
1172
|
+
*
|
|
1173
|
+
* - `getActiveSegment([obj])`
|
|
1174
|
+
*
|
|
1175
|
+
* An active segment is one whose transaction is still active (e.g. has not
|
|
1176
|
+
* ended yet).
|
|
1177
|
+
*
|
|
1178
|
+
* @memberof Shim.prototype
|
|
1179
|
+
*
|
|
1180
|
+
* @param {*} [obj] - The object to retrieve a segment from.
|
|
1181
|
+
*
|
|
1182
|
+
* @return {?TraceSegment} The trace segment associated with the given object or
|
|
1183
|
+
* the currently active segment if no object is provided or no segment is
|
|
1184
|
+
* associated with the object.
|
|
1185
|
+
*/
|
|
1186
|
+
function getActiveSegment(obj) {
|
|
1187
|
+
var segment = this.getSegment(obj)
|
|
1188
|
+
if (segment && segment.transaction && segment.transaction.isActive()) {
|
|
1189
|
+
return segment
|
|
1190
|
+
}
|
|
1191
|
+
return null
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1164
1194
|
/**
|
|
1165
1195
|
* Associates a segment with the given object.
|
|
1166
1196
|
*
|
|
@@ -652,8 +652,10 @@ function setErrorPredicate(pred) {
|
|
|
652
652
|
*/
|
|
653
653
|
function captureUrlParams(params) {
|
|
654
654
|
var segment = this.getSegment()
|
|
655
|
-
|
|
656
|
-
|
|
655
|
+
if (segment) {
|
|
656
|
+
var config = this.agent.config
|
|
657
|
+
urltils.copyParameters(config, params, segment.parameters)
|
|
658
|
+
}
|
|
657
659
|
}
|
|
658
660
|
|
|
659
661
|
// -------------------------------------------------------------------------- //
|
package/lib/stats/index.js
CHANGED
|
@@ -41,7 +41,7 @@ Stats.prototype.recordValue = function recordValue(totalTime, exclusiveTime) {
|
|
|
41
41
|
this.max = Math.max(totalTime, this.max)
|
|
42
42
|
|
|
43
43
|
this.sumOfSquares += (totalTime * totalTime)
|
|
44
|
-
this.callCount
|
|
44
|
+
++this.callCount
|
|
45
45
|
this.total += totalTime
|
|
46
46
|
this.totalExclusive += exclusiveTime
|
|
47
47
|
}
|
package/lib/system-info.js
CHANGED
|
@@ -142,7 +142,7 @@ module.exports._getProcessorStats = function getProcessorStats(callback) {
|
|
|
142
142
|
callback(parseCpuInfo(data))
|
|
143
143
|
})
|
|
144
144
|
} else {
|
|
145
|
-
logger.debug('Unknown platform:
|
|
145
|
+
logger.debug('Unknown platform: %s; could not retrieve processor info', platform)
|
|
146
146
|
callback(processorStats)
|
|
147
147
|
}
|
|
148
148
|
}
|
|
@@ -162,7 +162,7 @@ module.exports._getMemoryStats = function getMemoryStats(callback) {
|
|
|
162
162
|
callback(parseMemInfo(data))
|
|
163
163
|
})
|
|
164
164
|
} else {
|
|
165
|
-
logger.debug('Unknown platform:
|
|
165
|
+
logger.debug('Unknown platform: %s; could not retrieve memory info', platform)
|
|
166
166
|
callback(null)
|
|
167
167
|
}
|
|
168
168
|
}
|
|
@@ -181,7 +181,7 @@ function getKernelVersion(callback) {
|
|
|
181
181
|
callback(data)
|
|
182
182
|
})
|
|
183
183
|
} else {
|
|
184
|
-
logger.debug('Unknown platform
|
|
184
|
+
logger.debug('Unknown platform: %s; could not read kernel version', platform)
|
|
185
185
|
callback(null)
|
|
186
186
|
}
|
|
187
187
|
}
|
|
@@ -196,7 +196,7 @@ function getSysctlValue(names, callback) {
|
|
|
196
196
|
function respond(err, stdout, stderr) {
|
|
197
197
|
if (returned) return
|
|
198
198
|
if (err) {
|
|
199
|
-
logger.debug('Error when trying to run: sysctl -n
|
|
199
|
+
logger.debug('Error when trying to run: sysctl -n %s: %s', name, err.message)
|
|
200
200
|
callback(null)
|
|
201
201
|
returned = true
|
|
202
202
|
} else if (!stderr) {
|
|
@@ -204,7 +204,7 @@ function getSysctlValue(names, callback) {
|
|
|
204
204
|
returned = true
|
|
205
205
|
}
|
|
206
206
|
if (++ran === names.length && !returned) {
|
|
207
|
-
logger.debug('No sysctl info found for names: '
|
|
207
|
+
logger.debug('No sysctl info found for names: %j', names)
|
|
208
208
|
callback(null)
|
|
209
209
|
}
|
|
210
210
|
}
|
package/lib/transaction/index.js
CHANGED
|
@@ -53,7 +53,8 @@ function Transaction(agent) {
|
|
|
53
53
|
agent.metricNameNormalizer
|
|
54
54
|
)
|
|
55
55
|
|
|
56
|
-
agent.activeTransactions
|
|
56
|
+
++agent.activeTransactions
|
|
57
|
+
++agent.transactionCreatedInHarvest
|
|
57
58
|
|
|
58
59
|
this.numSegments = 0
|
|
59
60
|
|
|
@@ -144,8 +145,9 @@ Transaction.prototype.end = function end(done) {
|
|
|
144
145
|
)
|
|
145
146
|
}
|
|
146
147
|
|
|
147
|
-
|
|
148
|
+
this.agent.recordSupportability('Transactions/Segments', this.numSegments)
|
|
148
149
|
|
|
150
|
+
var transaction = this
|
|
149
151
|
transaction.trace.end()
|
|
150
152
|
process.nextTick(function nextTickedEnd() {
|
|
151
153
|
// recorders must be run before the trace is collected
|
|
@@ -404,6 +406,12 @@ function finalizeNameFromUri(requestURL, statusCode) {
|
|
|
404
406
|
* the current partial name is used.
|
|
405
407
|
*/
|
|
406
408
|
Transaction.prototype.finalizeName = function finalizeName(name) {
|
|
409
|
+
// If no name is given, and this is a web transaction with a url, then
|
|
410
|
+
// finalize the name using the stored url.
|
|
411
|
+
if (name == null && this.type === 'web' && this.url) {
|
|
412
|
+
return this.finalizeNameFromUri(this.url, this.statusCode)
|
|
413
|
+
}
|
|
414
|
+
|
|
407
415
|
this._partialName = name || this._partialName
|
|
408
416
|
|
|
409
417
|
var fullName = TYPE_METRICS[this.type] + '/' + this._partialName
|
|
@@ -100,6 +100,7 @@ function transactionProxy(handler) {
|
|
|
100
100
|
transaction: {id: segment.transaction.id, name: segment.transaction.getName()},
|
|
101
101
|
segment: segment.name
|
|
102
102
|
}, 'Active transaction when creating non-nested transaction')
|
|
103
|
+
tracer.agent.recordSupportability('Transactions/Nested')
|
|
103
104
|
return handler.apply(this, arguments)
|
|
104
105
|
}
|
|
105
106
|
var transaction = new Transaction(tracer.agent)
|
package/lib/util/logger.js
CHANGED
|
@@ -129,12 +129,12 @@ Logger.prototype.child = function child(extra) {
|
|
|
129
129
|
var parent = this
|
|
130
130
|
childLogger.options = parent.options
|
|
131
131
|
|
|
132
|
-
childLogger.write = function write(level, args,
|
|
133
|
-
|
|
132
|
+
childLogger.write = function write(level, args, _extra) {
|
|
133
|
+
_extra = getPropertiesToLog(_extra)
|
|
134
134
|
var selfExtra = util._extend({}, this.extra)
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
return parent.write(level, args,
|
|
136
|
+
_extra = util._extend(selfExtra, _extra)
|
|
137
|
+
return parent.write(level, args, _extra)
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
childLogger.setEnabled = Logger.prototype.setEnabled
|
|
@@ -97,7 +97,7 @@ exports.readProc = readProc
|
|
|
97
97
|
function readProc(path, callback) {
|
|
98
98
|
fs.readFile(path, function readProcFile(err, data) {
|
|
99
99
|
if (err) {
|
|
100
|
-
logger.error('Error when trying to read '
|
|
100
|
+
logger.error(err, 'Error when trying to read %s', path)
|
|
101
101
|
callback(err, null)
|
|
102
102
|
} else {
|
|
103
103
|
callback(null, data.toString())
|
package/newrelic.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
-
|
|
3
2
|
/**
|
|
4
3
|
* New Relic agent configuration.
|
|
5
4
|
*
|
|
6
|
-
* See lib/config.
|
|
5
|
+
* See lib/config.defaults.js in the agent distribution for a more complete
|
|
7
6
|
* description of configuration variables and their potential values.
|
|
8
7
|
*/
|
|
9
8
|
exports.config = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"licenses": [
|
|
6
6
|
{
|
|
@@ -115,6 +115,7 @@
|
|
|
115
115
|
},
|
|
116
116
|
"devDependencies": {
|
|
117
117
|
"async": "^2.1.4",
|
|
118
|
+
"benchmark": "^2.1.4",
|
|
118
119
|
"bluebird": "^3.4.7",
|
|
119
120
|
"eslint": "^2.9.0",
|
|
120
121
|
"jsdoc": "^3.4.0",
|
package/stub_api.js
CHANGED
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
var logger = require('./lib/logger.js')
|
|
4
4
|
var RealAPI = require('./api.js')
|
|
5
5
|
var TransactionHandle = require('./lib/transaction/handle')
|
|
6
|
+
var util = require('util')
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
/* eslint-disable no-eval */
|
|
9
10
|
function stubFunction(name) {
|
|
10
|
-
return eval(
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
return eval(
|
|
12
|
+
"(function () {return function " + name + "() {" +
|
|
13
|
+
"logger.debug('Not calling " + name + " because New Relic is disabled.');" +
|
|
14
|
+
"}}())"
|
|
15
|
+
)
|
|
13
16
|
}
|
|
14
17
|
/* eslint-enable no-eval */
|
|
15
18
|
|
|
@@ -28,8 +31,22 @@ for (var i = 0; i < length; i++) {
|
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
Stub.prototype.createTracer = createTracer
|
|
31
|
-
Stub.prototype.createWebTransaction =
|
|
32
|
-
|
|
34
|
+
Stub.prototype.createWebTransaction = util.deprecate(
|
|
35
|
+
createWebTransaction, [
|
|
36
|
+
'API#createWebTransaction is being deprecated!',
|
|
37
|
+
'Please use API#startWebTransaction for transaction creation',
|
|
38
|
+
'and API#getTransaction for transaction management including',
|
|
39
|
+
'ending transactions.'
|
|
40
|
+
].join(' ')
|
|
41
|
+
)
|
|
42
|
+
Stub.prototype.createBackgroundTransaction = util.deprecate(
|
|
43
|
+
createBackgroundTransaction, [
|
|
44
|
+
'API#createBackgroundTransaction is being deprecated!',
|
|
45
|
+
'Please use API#startBackgroundTransaction for transaction creation',
|
|
46
|
+
'and API#getTransaction for transaction management including',
|
|
47
|
+
'ending transactions.'
|
|
48
|
+
].join(' ')
|
|
49
|
+
)
|
|
33
50
|
Stub.prototype.startWebTransaction = startWebTransaction
|
|
34
51
|
Stub.prototype.startBackgroundTransaction = startBackgroundTransaction
|
|
35
52
|
Stub.prototype.getTransaction = getTransaction
|
|
@@ -89,17 +106,17 @@ function startBackgroundTransaction(name, group, callback) {
|
|
|
89
106
|
// Normally the following call executes callback asynchronously
|
|
90
107
|
function shutdown(options, cb) {
|
|
91
108
|
logger.debug('Not calling shutdown because New Relic is disabled.')
|
|
92
|
-
|
|
109
|
+
|
|
93
110
|
var callback = cb
|
|
94
111
|
if (!callback) {
|
|
95
112
|
if (typeof options === 'function') {
|
|
96
113
|
callback = options
|
|
97
114
|
} else {
|
|
98
|
-
callback =
|
|
115
|
+
callback = function __NR_defaultCb() {}
|
|
99
116
|
}
|
|
100
117
|
}
|
|
101
|
-
|
|
102
|
-
|
|
118
|
+
|
|
119
|
+
setImmediate(callback)
|
|
103
120
|
}
|
|
104
121
|
|
|
105
122
|
module.exports = Stub
|