@depup/elastic-apm-node 4.15.0-depup.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 (154) hide show
  1. package/LICENSE +26 -0
  2. package/NOTICE.md +442 -0
  3. package/README.md +48 -0
  4. package/changes.json +78 -0
  5. package/index.d.ts +398 -0
  6. package/index.js +11 -0
  7. package/lib/InflightEventSet.js +53 -0
  8. package/lib/activation-method.js +119 -0
  9. package/lib/agent.js +941 -0
  10. package/lib/apm-client/apm-client.js +313 -0
  11. package/lib/apm-client/http-apm-client/CHANGELOG.md +271 -0
  12. package/lib/apm-client/http-apm-client/README.md +485 -0
  13. package/lib/apm-client/http-apm-client/central-config.js +41 -0
  14. package/lib/apm-client/http-apm-client/container-info.js +111 -0
  15. package/lib/apm-client/http-apm-client/detect-hostname.js +96 -0
  16. package/lib/apm-client/http-apm-client/index.js +1975 -0
  17. package/lib/apm-client/http-apm-client/logging.js +31 -0
  18. package/lib/apm-client/http-apm-client/ndjson.js +20 -0
  19. package/lib/apm-client/http-apm-client/truncate.js +434 -0
  20. package/lib/apm-client/noop-apm-client.js +73 -0
  21. package/lib/async-hooks-polyfill.js +58 -0
  22. package/lib/cloud-metadata/aws.js +175 -0
  23. package/lib/cloud-metadata/azure.js +123 -0
  24. package/lib/cloud-metadata/callback-coordination.js +159 -0
  25. package/lib/cloud-metadata/gcp.js +133 -0
  26. package/lib/cloud-metadata/index.js +175 -0
  27. package/lib/config/config.js +458 -0
  28. package/lib/config/normalizers.js +701 -0
  29. package/lib/config/schema.js +1007 -0
  30. package/lib/constants.js +35 -0
  31. package/lib/errors.js +303 -0
  32. package/lib/filters/sanitize-field-names.js +69 -0
  33. package/lib/http-request.js +249 -0
  34. package/lib/instrumentation/azure-functions.js +519 -0
  35. package/lib/instrumentation/context.js +56 -0
  36. package/lib/instrumentation/dropped-spans-stats.js +112 -0
  37. package/lib/instrumentation/elasticsearch-shared.js +63 -0
  38. package/lib/instrumentation/express-utils.js +91 -0
  39. package/lib/instrumentation/generic-span.js +322 -0
  40. package/lib/instrumentation/http-shared.js +424 -0
  41. package/lib/instrumentation/ids.js +39 -0
  42. package/lib/instrumentation/index.js +1127 -0
  43. package/lib/instrumentation/modules/@apollo/server.js +30 -0
  44. package/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js +143 -0
  45. package/lib/instrumentation/modules/@aws-sdk/client-s3.js +230 -0
  46. package/lib/instrumentation/modules/@aws-sdk/client-sns.js +197 -0
  47. package/lib/instrumentation/modules/@aws-sdk/client-sqs.js +336 -0
  48. package/lib/instrumentation/modules/@elastic/elasticsearch.js +343 -0
  49. package/lib/instrumentation/modules/@hapi/hapi.js +221 -0
  50. package/lib/instrumentation/modules/@opentelemetry/api.js +86 -0
  51. package/lib/instrumentation/modules/@opentelemetry/sdk-metrics.js +79 -0
  52. package/lib/instrumentation/modules/@redis/client/dist/lib/client/commands-queue.js +178 -0
  53. package/lib/instrumentation/modules/@redis/client/dist/lib/client/index.js +49 -0
  54. package/lib/instrumentation/modules/@smithy/smithy-client.js +198 -0
  55. package/lib/instrumentation/modules/_lambda-handler.js +40 -0
  56. package/lib/instrumentation/modules/apollo-server-core.js +49 -0
  57. package/lib/instrumentation/modules/aws-sdk/dynamodb.js +155 -0
  58. package/lib/instrumentation/modules/aws-sdk/s3.js +184 -0
  59. package/lib/instrumentation/modules/aws-sdk/sns.js +232 -0
  60. package/lib/instrumentation/modules/aws-sdk/sqs.js +361 -0
  61. package/lib/instrumentation/modules/aws-sdk.js +76 -0
  62. package/lib/instrumentation/modules/bluebird.js +93 -0
  63. package/lib/instrumentation/modules/cassandra-driver.js +280 -0
  64. package/lib/instrumentation/modules/elasticsearch.js +191 -0
  65. package/lib/instrumentation/modules/express-graphql.js +66 -0
  66. package/lib/instrumentation/modules/express-queue.js +28 -0
  67. package/lib/instrumentation/modules/express.js +162 -0
  68. package/lib/instrumentation/modules/fastify.js +172 -0
  69. package/lib/instrumentation/modules/finalhandler.js +41 -0
  70. package/lib/instrumentation/modules/generic-pool.js +85 -0
  71. package/lib/instrumentation/modules/graphql.js +256 -0
  72. package/lib/instrumentation/modules/handlebars.js +22 -0
  73. package/lib/instrumentation/modules/http.js +112 -0
  74. package/lib/instrumentation/modules/http2.js +320 -0
  75. package/lib/instrumentation/modules/https.js +68 -0
  76. package/lib/instrumentation/modules/ioredis.js +94 -0
  77. package/lib/instrumentation/modules/jade.js +18 -0
  78. package/lib/instrumentation/modules/kafkajs.js +476 -0
  79. package/lib/instrumentation/modules/knex.js +91 -0
  80. package/lib/instrumentation/modules/koa-router.js +74 -0
  81. package/lib/instrumentation/modules/koa.js +15 -0
  82. package/lib/instrumentation/modules/memcached.js +99 -0
  83. package/lib/instrumentation/modules/mimic-response.js +45 -0
  84. package/lib/instrumentation/modules/mongodb/lib/cmap/connection_pool.js +40 -0
  85. package/lib/instrumentation/modules/mongodb-core.js +206 -0
  86. package/lib/instrumentation/modules/mongodb.js +259 -0
  87. package/lib/instrumentation/modules/mysql.js +200 -0
  88. package/lib/instrumentation/modules/mysql2.js +140 -0
  89. package/lib/instrumentation/modules/pg.js +148 -0
  90. package/lib/instrumentation/modules/pug.js +18 -0
  91. package/lib/instrumentation/modules/redis.js +176 -0
  92. package/lib/instrumentation/modules/restify.js +52 -0
  93. package/lib/instrumentation/modules/tedious.js +159 -0
  94. package/lib/instrumentation/modules/undici.js +270 -0
  95. package/lib/instrumentation/modules/ws.js +59 -0
  96. package/lib/instrumentation/noop-transaction.js +81 -0
  97. package/lib/instrumentation/run-context/AbstractRunContextManager.js +215 -0
  98. package/lib/instrumentation/run-context/AsyncHooksRunContextManager.js +106 -0
  99. package/lib/instrumentation/run-context/AsyncLocalStorageRunContextManager.js +73 -0
  100. package/lib/instrumentation/run-context/BasicRunContextManager.js +82 -0
  101. package/lib/instrumentation/run-context/RunContext.js +151 -0
  102. package/lib/instrumentation/run-context/index.js +23 -0
  103. package/lib/instrumentation/shimmer.js +123 -0
  104. package/lib/instrumentation/span-compression.js +239 -0
  105. package/lib/instrumentation/span.js +621 -0
  106. package/lib/instrumentation/template-shared.js +43 -0
  107. package/lib/instrumentation/timer.js +84 -0
  108. package/lib/instrumentation/transaction.js +571 -0
  109. package/lib/lambda.js +992 -0
  110. package/lib/load-source-map.js +100 -0
  111. package/lib/logging.js +212 -0
  112. package/lib/metrics/index.js +92 -0
  113. package/lib/metrics/platforms/generic/index.js +40 -0
  114. package/lib/metrics/platforms/generic/process-cpu.js +22 -0
  115. package/lib/metrics/platforms/generic/process-top.js +157 -0
  116. package/lib/metrics/platforms/generic/stats.js +34 -0
  117. package/lib/metrics/platforms/generic/system-cpu.js +51 -0
  118. package/lib/metrics/platforms/linux/index.js +19 -0
  119. package/lib/metrics/platforms/linux/stats.js +213 -0
  120. package/lib/metrics/queue.js +90 -0
  121. package/lib/metrics/registry.js +52 -0
  122. package/lib/metrics/reporter.js +119 -0
  123. package/lib/metrics/runtime.js +77 -0
  124. package/lib/middleware/connect.js +16 -0
  125. package/lib/opentelemetry-bridge/OTelBridgeNonRecordingSpan.js +150 -0
  126. package/lib/opentelemetry-bridge/OTelBridgeRunContext.js +124 -0
  127. package/lib/opentelemetry-bridge/OTelContextManager.js +82 -0
  128. package/lib/opentelemetry-bridge/OTelSpan.js +344 -0
  129. package/lib/opentelemetry-bridge/OTelTracer.js +201 -0
  130. package/lib/opentelemetry-bridge/OTelTracerProvider.js +25 -0
  131. package/lib/opentelemetry-bridge/README.md +244 -0
  132. package/lib/opentelemetry-bridge/index.js +15 -0
  133. package/lib/opentelemetry-bridge/oblog.js +23 -0
  134. package/lib/opentelemetry-bridge/opentelemetry-core-mini/README.md +3 -0
  135. package/lib/opentelemetry-bridge/opentelemetry-core-mini/internal/validators.js +52 -0
  136. package/lib/opentelemetry-bridge/opentelemetry-core-mini/trace/TraceState.js +109 -0
  137. package/lib/opentelemetry-bridge/otelutils.js +99 -0
  138. package/lib/opentelemetry-bridge/setup.js +76 -0
  139. package/lib/opentelemetry-metrics/ElasticApmMetricExporter.js +285 -0
  140. package/lib/opentelemetry-metrics/index.js +50 -0
  141. package/lib/parsers.js +225 -0
  142. package/lib/propwrap.js +147 -0
  143. package/lib/stacktraces.js +537 -0
  144. package/lib/symbols.js +15 -0
  145. package/lib/tracecontext/index.js +118 -0
  146. package/lib/tracecontext/traceparent.js +185 -0
  147. package/lib/tracecontext/tracestate.js +388 -0
  148. package/lib/wildcard-matcher.js +52 -0
  149. package/loader.mjs +7 -0
  150. package/package.json +299 -0
  151. package/start.d.ts +8 -0
  152. package/start.js +29 -0
  153. package/types/aws-lambda.d.ts +98 -0
  154. package/types/connect.d.ts +23 -0
@@ -0,0 +1,51 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const os = require('os');
10
+
11
+ function cpuAverage() {
12
+ const times = {
13
+ user: 0,
14
+ nice: 0,
15
+ sys: 0,
16
+ idle: 0,
17
+ irq: 0,
18
+ total: 0,
19
+ };
20
+
21
+ const cpus = os.cpus();
22
+ for (const cpu of cpus) {
23
+ for (const type of Object.keys(cpu.times)) {
24
+ times[type] += cpu.times[type];
25
+ times.total += cpu.times[type];
26
+ }
27
+ }
28
+
29
+ // Average over CPU count
30
+ const averages = {};
31
+ for (const type of Object.keys(times)) {
32
+ averages[type] = times[type] / cpus.length;
33
+ }
34
+
35
+ return averages;
36
+ }
37
+
38
+ function cpuPercent(last, next) {
39
+ const idle = next.idle - last.idle;
40
+ const total = next.total - last.total;
41
+ return 1 - idle / total || 0;
42
+ }
43
+
44
+ let last = cpuAverage();
45
+
46
+ module.exports = function systemCPUUsage() {
47
+ const next = cpuAverage();
48
+ const result = cpuPercent(last, next);
49
+ last = next;
50
+ return result;
51
+ };
@@ -0,0 +1,19 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const Stats = require('./stats');
10
+
11
+ module.exports = function createSystemMetrics(registry) {
12
+ const stats = new Stats();
13
+
14
+ registry.registerCollector(stats);
15
+
16
+ for (const metric of Object.keys(stats.toJSON())) {
17
+ registry.getOrCreateGauge(metric, () => stats.toJSON()[metric]);
18
+ }
19
+ };
@@ -0,0 +1,213 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const fs = require('fs');
10
+
11
+ const afterAll = require('after-all-results');
12
+
13
+ const whitespace = /\s+/;
14
+
15
+ class Stats {
16
+ constructor(opts) {
17
+ opts = opts || {};
18
+
19
+ this.files = {
20
+ processFile: opts.processFile || '/proc/self/stat',
21
+ memoryFile: opts.memoryFile || '/proc/meminfo',
22
+ cpuFile: opts.cpuFile || '/proc/stat',
23
+ };
24
+
25
+ this.previous = {
26
+ cpuTotal: 0,
27
+ cpuUsage: 0,
28
+ memTotal: 0,
29
+ memAvailable: 0,
30
+ utime: 0,
31
+ stime: 0,
32
+ vsize: 0,
33
+ rss: 0,
34
+ };
35
+
36
+ this.stats = {
37
+ 'system.cpu.total.norm.pct': 0,
38
+ 'system.memory.actual.free': 0,
39
+ 'system.memory.total': 0,
40
+ 'system.process.cpu.total.norm.pct': 0,
41
+ 'system.process.cpu.system.norm.pct': 0,
42
+ 'system.process.cpu.user.norm.pct': 0,
43
+ 'system.process.memory.size': 0,
44
+ 'system.process.memory.rss.bytes': 0,
45
+ };
46
+
47
+ this.inProgress = false;
48
+ this.timer = null;
49
+
50
+ // Do initial load
51
+ const files = [
52
+ this.files.processFile,
53
+ this.files.memoryFile,
54
+ this.files.cpuFile,
55
+ ];
56
+
57
+ try {
58
+ const datas = files.map(readFileSync);
59
+ this.previous = this.readStats(datas);
60
+ this.update(datas);
61
+ } catch (err) {}
62
+ }
63
+
64
+ toJSON() {
65
+ return this.stats;
66
+ }
67
+
68
+ collect(cb) {
69
+ if (this.inProgress) {
70
+ if (cb) process.nextTick(cb);
71
+ return;
72
+ }
73
+
74
+ this.inProgress = true;
75
+
76
+ const files = [
77
+ this.files.processFile,
78
+ this.files.memoryFile,
79
+ this.files.cpuFile,
80
+ ];
81
+
82
+ const next = afterAll((err, files) => {
83
+ if (!err) this.update(files);
84
+ if (cb) cb();
85
+ });
86
+
87
+ for (const file of files) {
88
+ fs.readFile(file, next());
89
+ }
90
+ }
91
+
92
+ readStats([processFile, memoryFile, cpuFile]) {
93
+ // CPU data
94
+ //
95
+ // Example of line we're trying to parse:
96
+ // cpu 13978 30 2511 9257 2248 0 102 0 0 0
97
+
98
+ const cpuLine = firstLineOfBufferAsString(cpuFile);
99
+ const cpuTimes = cpuLine.split(whitespace);
100
+
101
+ let cpuTotal = 0;
102
+ for (let i = 1; i < cpuTimes.length; i++) {
103
+ cpuTotal += Number(cpuTimes[i]);
104
+ }
105
+
106
+ // We're off-by-one in relation to the expected index, because we include
107
+ // the `cpu` label at the beginning of the line
108
+ const idle = Number(cpuTimes[4]);
109
+ const iowait = Number(cpuTimes[5]);
110
+ const cpuUsage = cpuTotal - idle - iowait;
111
+
112
+ // Memory data
113
+ let memAvailable = 0;
114
+ let memTotal = 0;
115
+
116
+ let matches = 0;
117
+ for (const line of memoryFile.toString().split('\n')) {
118
+ if (/^MemAvailable:/.test(line)) {
119
+ memAvailable = parseInt(line.split(whitespace)[1], 10) * 1024;
120
+ matches++;
121
+ } else if (/^MemTotal:/.test(line)) {
122
+ memTotal = parseInt(line.split(whitespace)[1], 10) * 1024;
123
+ matches++;
124
+ }
125
+ if (matches === 2) break;
126
+ }
127
+
128
+ // Process data
129
+ //
130
+ // Example of line we're trying to parse:
131
+ //
132
+ // 44 (node /app/node_) R 1 44 44 0 -1 4210688 7948 0 0 0 109 21 0 0 20 0 10 0 133652 954462208 12906 18446744073709551615 4194304 32940036 140735797366336 0 0 0 0 4096 16898 0 0 0 17 0 0 0 0 0 0 35037200 35143856 41115648 140735797369050 140735797369131 140735797369131 140735797370852 0
133
+ //
134
+ // We can't just split on whitespace as the 2nd field might contain
135
+ // whitespace. However, the parentheses will always be there, so we can
136
+ // ignore everything from before the `)` to get rid of the whitespace
137
+ // problem.
138
+ //
139
+ // For details about each field, see:
140
+ // http://man7.org/linux/man-pages/man5/proc.5.html
141
+
142
+ const processLine = firstLineOfBufferAsString(processFile);
143
+ const processData = processLine
144
+ .slice(processLine.lastIndexOf(')'))
145
+ .split(whitespace);
146
+
147
+ // all fields are referenced by their index, but are off by one because
148
+ // we're dropping the first field from the line due to the whitespace
149
+ // problem described above
150
+ const utime = parseInt(processData[12], 10); // position in file: 14
151
+ const stime = parseInt(processData[13], 10); // position in file: 15
152
+ const vsize = parseInt(processData[21], 10); // position in file: 23
153
+
154
+ return {
155
+ cpuUsage,
156
+ cpuTotal,
157
+ memTotal,
158
+ memAvailable,
159
+ utime,
160
+ stime,
161
+ vsize,
162
+ rss: process.memoryUsage().rss, // TODO: Calculate using field 24 (rss) * PAGE_SIZE
163
+ };
164
+ }
165
+
166
+ update(files) {
167
+ const prev = this.previous;
168
+ const next = this.readStats(files);
169
+ const stats = this.stats;
170
+
171
+ const cpuTotal = next.cpuTotal - prev.cpuTotal;
172
+ const cpuUsage = next.cpuUsage - prev.cpuUsage;
173
+ const utime = next.utime - prev.utime;
174
+ const stime = next.stime - prev.stime;
175
+
176
+ stats['system.cpu.total.norm.pct'] = cpuUsage / cpuTotal || 0;
177
+ stats['system.memory.actual.free'] = next.memAvailable;
178
+ stats['system.memory.total'] = next.memTotal;
179
+
180
+ // We use Math.min to guard against an edge case where /proc/self/stat
181
+ // reported more clock ticks than /proc/stat, in which case it looks like
182
+ // the process spent more CPU time than was used by the system. In that
183
+ // case we just assume it was a 100% CPU.
184
+ //
185
+ // This might happen because we don't read the process file at the same
186
+ // time as the system file. In between the two reads, the process will
187
+ // spend some time on the CPU and hence the two reads are not 100% synced
188
+ // up.
189
+ const cpuProcessPercent = Math.min((utime + stime) / cpuTotal || 0, 1);
190
+ const cpuProcessUserPercent = Math.min(utime / cpuTotal || 0, 1);
191
+ const cpuProcessSystemPercent = Math.min(stime / cpuTotal || 0, 1);
192
+
193
+ stats['system.process.cpu.total.norm.pct'] = cpuProcessPercent;
194
+ stats['system.process.cpu.user.norm.pct'] = cpuProcessUserPercent;
195
+ stats['system.process.cpu.system.norm.pct'] = cpuProcessSystemPercent;
196
+ stats['system.process.memory.size'] = next.vsize;
197
+ stats['system.process.memory.rss.bytes'] = next.rss;
198
+
199
+ this.previous = next;
200
+ this.inProgress = false;
201
+ }
202
+ }
203
+
204
+ function firstLineOfBufferAsString(buff) {
205
+ const newline = buff.indexOf('\n');
206
+ return buff.toString('utf8', 0, newline === -1 ? buff.length : newline);
207
+ }
208
+
209
+ function readFileSync(file) {
210
+ return fs.readFileSync(file);
211
+ }
212
+
213
+ module.exports = Stats;
@@ -0,0 +1,90 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+ class QueueMetricsCollector {
9
+ constructor() {
10
+ this.stats = {
11
+ 'queue.latency.min.ms': 0,
12
+ 'queue.latency.max.ms': 0,
13
+ 'queue.latency.avg.ms': 0,
14
+ };
15
+
16
+ this.total = 0;
17
+ this.count = 0;
18
+ this.min = 0;
19
+ this.max = 0;
20
+ }
21
+
22
+ // Updates the data used to generate our stats
23
+ //
24
+ // Unlike the Stats and RuntimeCollector, this function
25
+ // also returns the instantiated collector.
26
+ //
27
+ // @param {number} time A javascript ms timestamp
28
+ updateStats(time) {
29
+ if (this.min === 0 || this.min > time) {
30
+ this.min = time;
31
+ }
32
+ if (this.max < time) {
33
+ this.max = time;
34
+ }
35
+
36
+ this.count++;
37
+ this.total += time;
38
+ }
39
+
40
+ // standard `collect` method for stats collectors
41
+ //
42
+ // called by the MetricReporter object prior to its sending data
43
+ //
44
+ // @param {function} cb callback function
45
+ collect(cb) {
46
+ // update average based on count and total
47
+ this.stats['queue.latency.avg.ms'] = 0;
48
+ if (this.count > 0) {
49
+ this.stats['queue.latency.avg.ms'] = this.total / this.count;
50
+ }
51
+
52
+ this.stats['queue.latency.max.ms'] = this.max;
53
+ this.stats['queue.latency.min.ms'] = this.min;
54
+
55
+ // reset for next run
56
+ this.total = 0;
57
+ this.count = 0;
58
+ this.max = 0;
59
+ this.min = 0;
60
+ if (cb) process.nextTick(cb);
61
+ }
62
+ }
63
+
64
+ // Creates and Registers a Metric Collector
65
+ //
66
+ // Unlike the Stats and RuntimeCollector, this function
67
+ // also returns the instantiated collector.
68
+ //
69
+ // @param {string} queueOrTopicName
70
+ // @param {MetricRegistry} registry
71
+ // @returns QueueMetricsCollector
72
+ function createQueueMetrics(queueOrTopicName, registry) {
73
+ const collector = new QueueMetricsCollector();
74
+ registry.registerCollector(collector);
75
+ for (const metric of Object.keys(collector.stats)) {
76
+ registry.getOrCreateGauge(
77
+ metric,
78
+ function returnCurrentValue() {
79
+ return collector.stats[metric];
80
+ },
81
+ { queue_name: queueOrTopicName },
82
+ );
83
+ }
84
+ return collector;
85
+ }
86
+
87
+ module.exports = {
88
+ createQueueMetrics,
89
+ QueueMetricsCollector,
90
+ };
@@ -0,0 +1,52 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const os = require('os');
10
+
11
+ const { SelfReportingMetricsRegistry } = require('measured-reporting');
12
+ const DimensionAwareMetricsRegistry = require('measured-reporting/lib/registries/DimensionAwareMetricsRegistry');
13
+
14
+ const MetricsReporter = require('./reporter');
15
+ const createRuntimeMetrics = require('./runtime');
16
+ const createSystemMetrics =
17
+ process.platform === 'linux'
18
+ ? require('./platforms/linux')
19
+ : require('./platforms/generic');
20
+
21
+ class MetricsRegistry extends SelfReportingMetricsRegistry {
22
+ constructor(agent, { reporterOptions, registryOptions = {} } = {}) {
23
+ const defaultReporterOptions = {
24
+ defaultDimensions: {
25
+ hostname: agent._conf.hostname || os.hostname(),
26
+ env: agent._conf.environment || '',
27
+ },
28
+ };
29
+
30
+ const options = Object.assign({}, defaultReporterOptions, reporterOptions);
31
+ const reporter = new MetricsReporter(agent, options);
32
+
33
+ registryOptions.registry = new DimensionAwareMetricsRegistry({
34
+ metricLimit: agent._conf.metricsLimit,
35
+ });
36
+
37
+ super(reporter, registryOptions);
38
+ this._agent = agent;
39
+
40
+ this._registry.collectors = [];
41
+ if (reporter.enabled) {
42
+ createSystemMetrics(this);
43
+ createRuntimeMetrics(this);
44
+ }
45
+ }
46
+
47
+ registerCollector(collector) {
48
+ this._registry.collectors.push(collector);
49
+ }
50
+ }
51
+
52
+ module.exports = MetricsRegistry;
@@ -0,0 +1,119 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const afterAll = require('after-all-results');
10
+ const { Reporter } = require('measured-reporting');
11
+ const ObjectIdentityMap = require('object-identity-map');
12
+
13
+ class MetricsReporter extends Reporter {
14
+ constructor(agent, options = {}) {
15
+ super(options);
16
+ this.enabled = options.enabled;
17
+ this._agent = agent;
18
+
19
+ if (!this.enabled) {
20
+ this.shutdown();
21
+ }
22
+ }
23
+
24
+ _reportMetrics(metrics) {
25
+ if (!this.enabled) return;
26
+
27
+ const baseDimensions = {
28
+ timestamp: Date.now() * 1000,
29
+ tags: this._getDimensions(metrics),
30
+ };
31
+
32
+ const next = afterAll(() => {
33
+ const seen = new ObjectIdentityMap();
34
+
35
+ for (const metric of metrics) {
36
+ // Due to limitations in measured-reporting, metrics dropped
37
+ // due to `metricsLimit` leave empty slots in the list.
38
+ if (!metric) continue;
39
+ if (this._agent._isMetricNameDisabled(metric.name)) {
40
+ continue;
41
+ }
42
+
43
+ if (this.isStaleMetric(metric)) {
44
+ this.removeMetricFromRegistry(metric, this._registry);
45
+ continue;
46
+ }
47
+
48
+ const data = seen.ensure(metric.dimensions, () => {
49
+ const metricData = unflattenBreakdown(metric.dimensions);
50
+ const merged = Object.assign(
51
+ { samples: {} },
52
+ baseDimensions,
53
+ metricData,
54
+ );
55
+ Object.assign(merged.tags, baseDimensions.tags, metricData.tags);
56
+ return merged;
57
+ });
58
+
59
+ data.samples[metric.name] = {
60
+ value: metric.metricImpl.toJSON(),
61
+ };
62
+
63
+ if (metric.metricImpl.constructor.name === 'Counter') {
64
+ metric.metricImpl.reset();
65
+ }
66
+ }
67
+
68
+ if (this._agent._apmClient) {
69
+ for (const metric of seen.values()) {
70
+ this._agent._apmClient.sendMetricSet(metric);
71
+ }
72
+ }
73
+ });
74
+
75
+ for (const collector of this._registry.collectors) {
76
+ collector.collect(next());
77
+ }
78
+ }
79
+
80
+ isStaleMetric(metric) {
81
+ // if a metric is a counting metric and that count is
82
+ // zero, then the metric is considered stale
83
+ if (metric.metricImpl && metric.metricImpl._count === 0) {
84
+ return true;
85
+ }
86
+ return false;
87
+ }
88
+
89
+ removeMetricFromRegistry(metric) {
90
+ if (!this._registry || !this._registry._metrics) {
91
+ return;
92
+ }
93
+ const key = this._registry._generateStorageKey(
94
+ metric.name,
95
+ metric.dimensions,
96
+ );
97
+ return this._registry._metrics.delete(key);
98
+ }
99
+ }
100
+
101
+ module.exports = MetricsReporter;
102
+
103
+ function unflattenBreakdown(source) {
104
+ const target = {
105
+ tags: {},
106
+ };
107
+
108
+ for (const [key, value] of Object.entries(source)) {
109
+ if (key.includes('::')) {
110
+ const [parent, child] = key.split('::');
111
+ if (!target[parent]) target[parent] = {};
112
+ target[parent][child] = value;
113
+ } else {
114
+ target.tags[key] = value;
115
+ }
116
+ }
117
+
118
+ return target;
119
+ }
@@ -0,0 +1,77 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const eventLoopMonitor = require('monitor-event-loop-delay');
10
+
11
+ const activeHandles =
12
+ typeof process._getActiveHandles === 'function'
13
+ ? process._getActiveHandles.bind(process)
14
+ : () => [];
15
+
16
+ const activeRequests =
17
+ typeof process._getActiveRequests === 'function'
18
+ ? process._getActiveRequests.bind(process)
19
+ : () => [];
20
+
21
+ const eventLoopMonitorResolution = 10;
22
+
23
+ class RuntimeCollector {
24
+ constructor() {
25
+ this.stats = {
26
+ 'nodejs.handles.active': 0,
27
+ 'nodejs.requests.active': 0,
28
+ 'nodejs.eventloop.delay.avg.ms': 0,
29
+ 'nodejs.memory.heap.allocated.bytes': 0,
30
+ 'nodejs.memory.heap.used.bytes': 0,
31
+ 'nodejs.memory.external.bytes': 0,
32
+ 'nodejs.memory.arrayBuffers.bytes': 0,
33
+ };
34
+
35
+ const monitor = eventLoopMonitor({
36
+ resolution: eventLoopMonitorResolution,
37
+ });
38
+ monitor.enable();
39
+
40
+ this.loopMonitor = monitor;
41
+ this.collect();
42
+ }
43
+
44
+ collect(cb) {
45
+ // Handles and Requests
46
+ this.stats['nodejs.handles.active'] = activeHandles().length;
47
+ this.stats['nodejs.requests.active'] = activeRequests().length;
48
+
49
+ // Event loop
50
+ const loopDelay = Math.max(
51
+ 0,
52
+ (this.loopMonitor.mean || 0) / 1e6 - eventLoopMonitorResolution,
53
+ );
54
+ this.stats['nodejs.eventloop.delay.avg.ms'] = loopDelay;
55
+ this.loopMonitor.reset();
56
+
57
+ // Memory / Heap
58
+ const memoryUsage = process.memoryUsage();
59
+ this.stats['nodejs.memory.heap.allocated.bytes'] = memoryUsage.heapTotal;
60
+ this.stats['nodejs.memory.heap.used.bytes'] = memoryUsage.heapUsed;
61
+
62
+ this.stats['nodejs.memory.external.bytes'] = memoryUsage.external;
63
+ this.stats['nodejs.memory.arrayBuffers.bytes'] =
64
+ memoryUsage.arrayBuffers || 0; // Only available in NodeJS +13.0
65
+
66
+ if (cb) process.nextTick(cb);
67
+ }
68
+ }
69
+
70
+ module.exports = function createRuntimeMetrics(registry) {
71
+ const collector = new RuntimeCollector();
72
+ registry.registerCollector(collector);
73
+
74
+ for (const metric of Object.keys(collector.stats)) {
75
+ registry.getOrCreateGauge(metric, () => collector.stats[metric]);
76
+ }
77
+ };
@@ -0,0 +1,16 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ module.exports = function connectMiddleware() {
10
+ var agent = this;
11
+ return function (err, req, res, next) {
12
+ agent.captureError(err, { request: req }, function elasticAPMMiddleware() {
13
+ next(err);
14
+ });
15
+ };
16
+ };