dd-trace 2.4.1 → 2.6.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.
Files changed (57) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/ci/init.js +6 -0
  3. package/ci/jest/env.js +16 -3
  4. package/ext/exporters.d.ts +2 -1
  5. package/ext/exporters.js +2 -1
  6. package/index.d.ts +17 -1
  7. package/package.json +5 -4
  8. package/packages/datadog-instrumentations/index.js +1 -0
  9. package/packages/datadog-instrumentations/src/amqplib.js +1 -1
  10. package/packages/datadog-instrumentations/src/cypress.js +8 -0
  11. package/packages/datadog-instrumentations/src/http/client.js +10 -10
  12. package/packages/datadog-instrumentations/src/jest.js +170 -0
  13. package/packages/datadog-plugin-aws-sdk/src/helpers.js +4 -4
  14. package/packages/datadog-plugin-aws-sdk/src/index.js +1 -1
  15. package/packages/datadog-plugin-cucumber/src/index.js +16 -16
  16. package/packages/datadog-plugin-cypress/src/index.js +10 -5
  17. package/packages/datadog-plugin-cypress/src/plugin.js +18 -17
  18. package/packages/datadog-plugin-elasticsearch/src/index.js +4 -2
  19. package/packages/datadog-plugin-fs/src/index.js +2 -0
  20. package/packages/datadog-plugin-http/src/client.js +4 -1
  21. package/packages/datadog-plugin-http/src/server.js +7 -10
  22. package/packages/datadog-plugin-jest/src/index.js +101 -3
  23. package/packages/datadog-plugin-jest/src/util.js +1 -29
  24. package/packages/datadog-plugin-mocha/src/index.js +14 -15
  25. package/packages/dd-trace/lib/version.js +1 -1
  26. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +29 -12
  27. package/packages/dd-trace/src/appsec/index.js +7 -3
  28. package/packages/dd-trace/src/appsec/recommended.json +119 -210
  29. package/packages/dd-trace/src/appsec/reporter.js +29 -3
  30. package/packages/dd-trace/src/appsec/rule_manager.js +2 -2
  31. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +32 -0
  32. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +51 -0
  33. package/packages/dd-trace/src/config.js +33 -4
  34. package/packages/dd-trace/src/encode/0.4.js +0 -1
  35. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +193 -0
  36. package/packages/dd-trace/src/encode/tags-processors.js +116 -0
  37. package/packages/dd-trace/src/exporter.js +3 -0
  38. package/packages/dd-trace/src/exporters/agent/index.js +1 -1
  39. package/packages/dd-trace/src/exporters/agent/writer.js +7 -32
  40. package/packages/dd-trace/src/exporters/{agent → common}/docker.js +0 -0
  41. package/packages/dd-trace/src/exporters/common/request.js +83 -0
  42. package/packages/dd-trace/src/exporters/common/writer.js +36 -0
  43. package/packages/dd-trace/src/exporters/{agent/scheduler.js → scheduler.js} +0 -0
  44. package/packages/dd-trace/src/format.js +9 -5
  45. package/packages/dd-trace/src/instrumenter.js +3 -0
  46. package/packages/dd-trace/src/pkg.js +11 -6
  47. package/packages/dd-trace/src/plugins/util/test.js +79 -1
  48. package/packages/dd-trace/src/plugins/util/web.js +11 -10
  49. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
  50. package/packages/dd-trace/src/profiling/profilers/cpu.js +1 -1
  51. package/packages/dd-trace/src/proxy.js +2 -0
  52. package/packages/dd-trace/src/span_processor.js +4 -1
  53. package/packages/dd-trace/src/telemetry.js +187 -0
  54. package/scripts/install_plugin_modules.js +1 -0
  55. package/packages/datadog-plugin-jest/src/jest-environment.js +0 -272
  56. package/packages/datadog-plugin-jest/src/jest-jasmine2.js +0 -185
  57. package/packages/dd-trace/src/exporters/agent/request.js +0 -86
@@ -0,0 +1,83 @@
1
+ 'use strict'
2
+
3
+ const http = require('http')
4
+ const https = require('https')
5
+ const log = require('../../log')
6
+ const docker = require('./docker')
7
+ const { storage } = require('../../../../datadog-core')
8
+
9
+ const httpAgent = new http.Agent({ keepAlive: true })
10
+ const httpsAgent = new https.Agent({ keepAlive: true })
11
+ const containerId = docker.id()
12
+
13
+ function request (data, options, keepAlive, callback) {
14
+ if (!options.headers) {
15
+ options.headers = {}
16
+ }
17
+ const isSecure = options.protocol === 'https:'
18
+ const client = isSecure ? https : http
19
+ const dataArray = [].concat(data)
20
+ options.headers['Content-Length'] = byteLength(dataArray)
21
+
22
+ if (containerId) {
23
+ options.headers['Datadog-Container-ID'] = containerId
24
+ }
25
+
26
+ if (keepAlive) {
27
+ options.agent = isSecure ? httpsAgent : httpAgent
28
+ }
29
+
30
+ const firstRequest = retriableRequest(options, client, callback)
31
+ dataArray.forEach(buffer => firstRequest.write(buffer))
32
+
33
+ // The first request will be retried
34
+ const firstRequestErrorHandler = () => {
35
+ log.debug('Retrying request to the intake')
36
+ const retriedReq = retriableRequest(options, client, callback)
37
+ dataArray.forEach(buffer => retriedReq.write(buffer))
38
+ // The retried request will fail normally
39
+ retriedReq.on('error', e => callback(new Error(`Network error trying to reach the intake: ${e.message}`)))
40
+ retriedReq.end()
41
+ }
42
+
43
+ firstRequest.on('error', firstRequestErrorHandler)
44
+ firstRequest.end()
45
+
46
+ return firstRequest
47
+ }
48
+
49
+ function retriableRequest (options, client, callback) {
50
+ const store = storage.getStore()
51
+
52
+ storage.enterWith({ noop: true })
53
+
54
+ const timeout = options.timeout || 15000
55
+
56
+ const request = client.request(options, res => {
57
+ let responseData = ''
58
+
59
+ res.setTimeout(timeout)
60
+
61
+ res.on('data', chunk => { responseData += chunk })
62
+ res.on('end', () => {
63
+ if (res.statusCode >= 200 && res.statusCode <= 299) {
64
+ callback(null, responseData, res.statusCode)
65
+ } else {
66
+ const error = new Error(`Error from the endpoint: ${res.statusCode} ${http.STATUS_CODES[res.statusCode]}`)
67
+ error.status = res.statusCode
68
+
69
+ callback(error, null, res.statusCode)
70
+ }
71
+ })
72
+ })
73
+ request.setTimeout(timeout, request.abort)
74
+ storage.enterWith(store)
75
+
76
+ return request
77
+ }
78
+
79
+ function byteLength (data) {
80
+ return data.length > 0 ? data.reduce((prev, next) => prev + next.length, 0) : 0
81
+ }
82
+
83
+ module.exports = request
@@ -0,0 +1,36 @@
1
+ 'use strict'
2
+ const log = require('../../log')
3
+
4
+ class Writer {
5
+ constructor ({ url }) {
6
+ this._url = url
7
+ }
8
+
9
+ flush (done = () => {}) {
10
+ const count = this._encoder.count()
11
+
12
+ if (count > 0) {
13
+ const payload = this._encoder.makePayload()
14
+
15
+ this._sendPayload(payload, count, done)
16
+ } else {
17
+ done()
18
+ }
19
+ }
20
+
21
+ append (spans) {
22
+ log.debug(() => `Encoding trace: ${JSON.stringify(spans)}`)
23
+
24
+ this._encode(spans)
25
+ }
26
+
27
+ _encode (trace) {
28
+ this._encoder.encode(trace)
29
+ }
30
+
31
+ setUrl (url) {
32
+ this._url = url
33
+ }
34
+ }
35
+
36
+ module.exports = Writer
@@ -22,7 +22,6 @@ const map = {
22
22
  function format (span) {
23
23
  const formatted = formatSpan(span)
24
24
 
25
- extractError(formatted, span)
26
25
  extractRootTags(formatted, span)
27
26
  extractChunkTags(formatted, span)
28
27
  extractTags(formatted, span)
@@ -74,8 +73,8 @@ function extractTags (trace, span) {
74
73
  addTag({}, trace.metrics, tag, tags[tag] === undefined || tags[tag] ? 1 : 0)
75
74
  break
76
75
  case 'error':
77
- if (tags[tag] && (context._name !== 'fs.operation')) {
78
- trace.error = 1
76
+ if (context._name !== 'fs.operation') {
77
+ extractError(trace, tags[tag])
79
78
  }
80
79
  break
81
80
  case 'error.type':
@@ -84,6 +83,8 @@ function extractTags (trace, span) {
84
83
  // HACK: remove when implemented in the backend
85
84
  if (context._name !== 'fs.operation') {
86
85
  trace.error = 1
86
+ } else {
87
+ break
87
88
  }
88
89
  default: // eslint-disable-line no-fallthrough
89
90
  addTag(trace.meta, trace.metrics, tag, tags[tag])
@@ -122,8 +123,11 @@ function extractChunkTags (trace, span) {
122
123
  }
123
124
  }
124
125
 
125
- function extractError (trace, span) {
126
- const error = span.context()._tags['error']
126
+ function extractError (trace, error) {
127
+ if (!error) return
128
+
129
+ trace.error = 1
130
+
127
131
  if (isError(error)) {
128
132
  addTag(trace.meta, trace.metrics, 'error.msg', error.message)
129
133
  addTag(trace.meta, trace.metrics, 'error.type', error.name)
@@ -7,6 +7,7 @@ const Loader = require('./loader')
7
7
  const { isTrue } = require('./util')
8
8
  const plugins = require('./plugins')
9
9
  const Plugin = require('./plugins/plugin')
10
+ const telemetry = require('./telemetry')
10
11
 
11
12
  const disabledPlugins = process.env.DD_TRACE_DISABLED_PLUGINS
12
13
 
@@ -54,6 +55,7 @@ class Instrumenter {
54
55
 
55
56
  try {
56
57
  this._set(plugin, { name, config })
58
+ telemetry.updateIntegrations()
57
59
  } catch (e) {
58
60
  log.debug(`Could not find a plugin named "${name}".`)
59
61
  }
@@ -154,6 +156,7 @@ class Instrumenter {
154
156
 
155
157
  if (!instrumented) {
156
158
  this._instrumented.set(instrumentation, instrumented = new Set())
159
+ telemetry.updateIntegrations()
157
160
  }
158
161
 
159
162
  if (!instrumented.has(this._defaultExport(moduleExports))) {
@@ -11,7 +11,14 @@ function findRoot () {
11
11
 
12
12
  function findPkg () {
13
13
  const cwd = findRoot()
14
- const filePath = findUp('package.json', cwd)
14
+ const directory = path.resolve(cwd)
15
+ const res = path.parse(directory)
16
+
17
+ if (!res) return {}
18
+
19
+ const { root } = res
20
+
21
+ const filePath = findUp('package.json', root, directory)
15
22
 
16
23
  try {
17
24
  return JSON.parse(fs.readFileSync(filePath, 'utf8'))
@@ -20,18 +27,16 @@ function findPkg () {
20
27
  }
21
28
  }
22
29
 
23
- function findUp (name, cwd) {
24
- let directory = path.resolve(cwd)
25
- const { root } = path.parse(directory)
26
-
30
+ function findUp (name, root, directory) {
27
31
  while (true) {
28
32
  const current = path.resolve(directory, name)
29
33
 
30
34
  if (fs.existsSync(current)) return current
35
+
31
36
  if (directory === root) return
32
37
 
33
38
  directory = path.dirname(directory)
34
39
  }
35
40
  }
36
41
 
37
- module.exports = findPkg()
42
+ module.exports = Object.assign(findPkg(), { findRoot, findUp })
@@ -1,4 +1,7 @@
1
1
  const path = require('path')
2
+ const fs = require('fs')
3
+
4
+ const ignore = require('ignore')
2
5
 
3
6
  const { getGitMetadata } = require('./git')
4
7
  const { getUserProviderGitMetadata } = require('./user-provided-git')
@@ -16,6 +19,10 @@ const {
16
19
  } = require('./tags')
17
20
  const id = require('../../id')
18
21
 
22
+ const { SPAN_TYPE, RESOURCE_NAME, SAMPLING_PRIORITY } = require('../../../../../ext/tags')
23
+ const { SAMPLING_RULE_DECISION } = require('../../constants')
24
+ const { AUTO_KEEP } = require('../../../../../ext/priority')
25
+
19
26
  const TEST_FRAMEWORK = 'test.framework'
20
27
  const TEST_FRAMEWORK_VERSION = 'test.framework_version'
21
28
  const TEST_TYPE = 'test.type'
@@ -25,6 +32,7 @@ const TEST_STATUS = 'test.status'
25
32
  const TEST_PARAMETERS = 'test.parameters'
26
33
  const TEST_SKIP_REASON = 'test.skip_reason'
27
34
  const TEST_IS_RUM_ACTIVE = 'test.is_rum_active'
35
+ const TEST_CODE_OWNERS = 'test.codeowners'
28
36
 
29
37
  const ERROR_TYPE = 'error.type'
30
38
  const ERROR_MESSAGE = 'error.msg'
@@ -35,6 +43,7 @@ const CI_APP_ORIGIN = 'ciapp-test'
35
43
  const JEST_TEST_RUNNER = 'test.jest.test_runner'
36
44
 
37
45
  module.exports = {
46
+ TEST_CODE_OWNERS,
38
47
  TEST_FRAMEWORK,
39
48
  TEST_FRAMEWORK_VERSION,
40
49
  JEST_TEST_RUNNER,
@@ -53,7 +62,10 @@ module.exports = {
53
62
  getTestParametersString,
54
63
  finishAllTraceSpans,
55
64
  getTestParentSpan,
56
- getTestSuitePath
65
+ getTestSuitePath,
66
+ getCodeOwnersFileEntries,
67
+ getCodeOwnersForFilename,
68
+ getTestCommonTags
57
69
  }
58
70
 
59
71
  function getTestEnvironmentMetadata (testFramework, config) {
@@ -127,6 +139,20 @@ function getTestParentSpan (tracer) {
127
139
  'x-datadog-parent-id': '0000000000000000'
128
140
  })
129
141
  }
142
+
143
+ function getTestCommonTags (name, suite, version) {
144
+ return {
145
+ [SPAN_TYPE]: 'test',
146
+ [TEST_TYPE]: 'test',
147
+ [SAMPLING_RULE_DECISION]: 1,
148
+ [SAMPLING_PRIORITY]: AUTO_KEEP,
149
+ [TEST_NAME]: name,
150
+ [TEST_SUITE]: suite,
151
+ [RESOURCE_NAME]: `${suite}.${name}`,
152
+ [TEST_FRAMEWORK_VERSION]: version
153
+ }
154
+ }
155
+
130
156
  /**
131
157
  * We want to make sure that test suites are reported the same way for
132
158
  * every OS, so we replace `path.sep` by `/`
@@ -140,3 +166,55 @@ function getTestSuitePath (testSuiteAbsolutePath, sourceRoot) {
140
166
 
141
167
  return testSuitePath.replace(path.sep, '/')
142
168
  }
169
+
170
+ const POSSIBLE_CODEOWNERS_LOCATIONS = [
171
+ 'CODEOWNERS',
172
+ '.github/CODEOWNERS',
173
+ 'docs/CODEOWNERS',
174
+ '.gitlab/CODEOWNERS'
175
+ ]
176
+
177
+ function getCodeOwnersFileEntries (rootDir = process.cwd()) {
178
+ let codeOwnersContent
179
+
180
+ POSSIBLE_CODEOWNERS_LOCATIONS.forEach(location => {
181
+ try {
182
+ codeOwnersContent = fs.readFileSync(`${rootDir}/${location}`).toString()
183
+ } catch (e) {
184
+ // retry with next path
185
+ }
186
+ })
187
+ if (!codeOwnersContent) {
188
+ return null
189
+ }
190
+
191
+ const entries = []
192
+ const lines = codeOwnersContent.split('\n')
193
+
194
+ for (const line of lines) {
195
+ const [content] = line.split('#')
196
+ const trimmed = content.trim()
197
+ if (trimmed === '') continue
198
+ const [pattern, ...owners] = trimmed.split(/\s+/)
199
+ entries.push({ pattern, owners })
200
+ }
201
+ // Reverse because rules defined last take precedence
202
+ return entries.reverse()
203
+ }
204
+
205
+ function getCodeOwnersForFilename (filename, entries) {
206
+ if (!entries) {
207
+ return null
208
+ }
209
+ for (const entry of entries) {
210
+ try {
211
+ const isResponsible = ignore().add(entry.pattern).ignores(filename)
212
+ if (isResponsible) {
213
+ return JSON.stringify(entry.owners)
214
+ }
215
+ } catch (e) {
216
+ return null
217
+ }
218
+ }
219
+ return null
220
+ }
@@ -69,6 +69,17 @@ const web = {
69
69
  context.span = span
70
70
  context.res = res
71
71
 
72
+ if (!config.filter(req.url)) {
73
+ span.setTag(MANUAL_DROP, true)
74
+ span.context()._trace.isRecording = false
75
+ }
76
+
77
+ if (config.service) {
78
+ span.setTag(SERVICE_NAME, config.service)
79
+ }
80
+
81
+ analyticsSampler.sample(span, config.measured, true)
82
+
72
83
  return span
73
84
  },
74
85
  wrap (req) {
@@ -83,16 +94,6 @@ const web = {
83
94
  instrument (tracer, config, req, res, name, callback) {
84
95
  const span = this.startSpan(tracer, config, req, res, name)
85
96
 
86
- if (!config.filter(req.url)) {
87
- span.setTag(MANUAL_DROP, true)
88
- }
89
-
90
- if (config.service) {
91
- span.setTag(SERVICE_NAME, config.service)
92
- }
93
-
94
- analyticsSampler.sample(span, config.measured, true)
95
-
96
97
  this.wrap(req)
97
98
 
98
99
  return callback && tracer.scope().activate(span, () => callback(span))
@@ -5,7 +5,7 @@ const { request } = require('http')
5
5
  const FormData = require('form-data')
6
6
 
7
7
  // TODO: avoid using dd-trace internals. Make this a separate module?
8
- const docker = require('../../exporters/agent/docker')
8
+ const docker = require('../../exporters/common/docker')
9
9
  const version = require('../../../lib/version')
10
10
 
11
11
  const containerId = docker.id()
@@ -3,7 +3,7 @@
3
3
  class NativeCpuProfiler {
4
4
  constructor (options = {}) {
5
5
  this.type = 'wall'
6
- this._samplingInterval = options.samplingInterval || 10 * 1000
6
+ this._samplingInterval = options.samplingInterval || 1e6 / 99 // 99hz
7
7
  this._mapper = undefined
8
8
  this._pprof = undefined
9
9
  }
@@ -10,6 +10,7 @@ const metrics = require('./metrics')
10
10
  const log = require('./log')
11
11
  const { isFalse } = require('./util')
12
12
  const { setStartupLogInstrumenter } = require('./startup-log')
13
+ const telemetry = require('./telemetry')
13
14
 
14
15
  const noop = new NoopTracer()
15
16
 
@@ -63,6 +64,7 @@ class Tracer extends BaseTracer {
63
64
  this._instrumenter.enable(config)
64
65
  this._pluginManager.configure(config)
65
66
  setStartupLogInstrumenter(this._instrumenter)
67
+ telemetry.start(config, this._instrumenter, this._pluginManager)
66
68
  }
67
69
  } catch (e) {
68
70
  log.error(e)
@@ -32,7 +32,10 @@ class SpanProcessor {
32
32
  }
33
33
  }
34
34
 
35
- this._exporter.export(formatted)
35
+ if (formatted.length !== 0 && trace.isRecording !== false) {
36
+ this._exporter.export(formatted)
37
+ }
38
+
36
39
  this._erase(trace, active)
37
40
  }
38
41
  }
@@ -0,0 +1,187 @@
1
+ 'use strict'
2
+
3
+ const tracerVersion = require('../lib/version')
4
+ const pkg = require('./pkg')
5
+ const containerId = require('./exporters/common/docker').id()
6
+ const requirePackageJson = require('./require-package-json')
7
+ const path = require('path')
8
+ const os = require('os')
9
+ const request = require('./exporters/common/request')
10
+
11
+ let config
12
+ let instrumenter
13
+ let pluginManager
14
+
15
+ let seqId = 0
16
+ let application
17
+ let host
18
+ let interval
19
+ const sentIntegrations = new Set()
20
+
21
+ function getIntegrations () {
22
+ const newIntegrations = []
23
+ for (const plugin of instrumenter._instrumented.keys()) {
24
+ if (sentIntegrations.has(plugin.name)) {
25
+ continue
26
+ }
27
+ newIntegrations.push({
28
+ name: plugin.name,
29
+ enabled: true,
30
+ auto_enabled: true
31
+ })
32
+ sentIntegrations.add(plugin.name)
33
+ }
34
+ for (const pluginName in pluginManager._pluginsByName) {
35
+ if (sentIntegrations.has(pluginName)) {
36
+ continue
37
+ }
38
+ newIntegrations.push({
39
+ name: pluginName,
40
+ enabled: pluginManager._pluginsByName[pluginName]._enabled,
41
+ auto_enabled: true
42
+ })
43
+ sentIntegrations.add(pluginName)
44
+ }
45
+ return newIntegrations
46
+ }
47
+
48
+ function getDependencies () {
49
+ const deps = []
50
+ const { dependencies } = pkg
51
+ if (!dependencies) {
52
+ return deps
53
+ }
54
+ const rootDir = pkg.findRoot()
55
+ for (const [name, version] of Object.entries(dependencies)) {
56
+ const dep = { name }
57
+ try {
58
+ dep.version = requirePackageJson(
59
+ path.join(rootDir, 'node_modules', name.replace('/', path.sep))
60
+ ).version
61
+ } catch (e) {
62
+ dep.version = version
63
+ }
64
+ deps.push(dep)
65
+ }
66
+ return deps
67
+ }
68
+
69
+ function flatten (input, result = [], prefix = [], traversedObjects = null) {
70
+ traversedObjects = traversedObjects || new WeakSet()
71
+ if (traversedObjects.has(input)) {
72
+ return
73
+ }
74
+ traversedObjects.add(input)
75
+ for (const [key, value] of Object.entries(input)) {
76
+ if (typeof value === 'object' && value !== null) {
77
+ flatten(value, result, [...prefix, key], traversedObjects)
78
+ } else {
79
+ result.push({ name: [...prefix, key].join('.'), value })
80
+ }
81
+ }
82
+ return result
83
+ }
84
+
85
+ function appStarted () {
86
+ return {
87
+ integrations: getIntegrations(),
88
+ dependencies: getDependencies(),
89
+ configuration: flatten(config),
90
+ additional_payload: []
91
+ }
92
+ }
93
+
94
+ function onBeforeExit () {
95
+ process.removeListener('beforeExit', onBeforeExit)
96
+ sendData('app-closing')
97
+ }
98
+
99
+ function createAppObject () {
100
+ return {
101
+ service_name: config.service,
102
+ env: config.env,
103
+ service_version: config.version,
104
+ tracer_version: tracerVersion,
105
+ language_name: 'nodejs',
106
+ language_version: process.versions.node
107
+ }
108
+ }
109
+
110
+ function createHostObject () {
111
+ return {
112
+ hostname: os.hostname(), // TODO is this enough?
113
+ container_id: containerId
114
+ }
115
+ }
116
+
117
+ function sendData (reqType, payload = {}) {
118
+ const {
119
+ hostname,
120
+ port
121
+ } = config
122
+ const options = {
123
+ hostname,
124
+ port,
125
+ method: 'POST',
126
+ path: '/telemetry/proxy/api/v2/apmtelemetry',
127
+ headers: {
128
+ 'content-type': 'application/json',
129
+ 'dd-telemetry-api-version': 'v1',
130
+ 'dd-telemetry-request-type': reqType
131
+ }
132
+ }
133
+ const data = JSON.stringify({
134
+ api_version: 'v1',
135
+ request_type: reqType,
136
+ tracer_time: Math.floor(Date.now() / 1000),
137
+ runtime_id: config.tags['runtime-id'],
138
+ seq_id: ++seqId,
139
+ payload,
140
+ application,
141
+ host
142
+ })
143
+
144
+ request(data, options, true, () => {
145
+ // ignore errors
146
+ })
147
+ }
148
+
149
+ function start (aConfig, theInstrumenter, thePluginManager) {
150
+ if (!aConfig.telemetryEnabled) {
151
+ return
152
+ }
153
+ config = aConfig
154
+ instrumenter = theInstrumenter
155
+ pluginManager = thePluginManager
156
+ application = createAppObject()
157
+ host = createHostObject()
158
+ sendData('app-started', appStarted())
159
+ interval = setInterval(() => sendData('app-heartbeat'), 60000)
160
+ interval.unref()
161
+ process.on('beforeExit', onBeforeExit)
162
+ }
163
+
164
+ function stop () {
165
+ if (!config) {
166
+ return
167
+ }
168
+ clearInterval(interval)
169
+ process.removeListener('beforeExit', onBeforeExit)
170
+ }
171
+
172
+ function updateIntegrations () {
173
+ if (!config || !config.telemetryEnabled) {
174
+ return
175
+ }
176
+ const integrations = getIntegrations()
177
+ if (integrations.length === 0) {
178
+ return
179
+ }
180
+ sendData('app-integrations-change', { integrations })
181
+ }
182
+
183
+ module.exports = {
184
+ start,
185
+ stop,
186
+ updateIntegrations
187
+ }
@@ -186,6 +186,7 @@ const requirePackageJson = require('${requirePackageJsonPath}')
186
186
 
187
187
  module.exports = {
188
188
  get (id) { return require(id || '${name}') },
189
+ getPath (id) { return require.resolve(id || '${name}' ) },
189
190
  version () { return requirePackageJson('${name}', module).version }
190
191
  }
191
192
  `