@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.
- package/LICENSE +26 -0
- package/NOTICE.md +442 -0
- package/README.md +48 -0
- package/changes.json +78 -0
- package/index.d.ts +398 -0
- package/index.js +11 -0
- package/lib/InflightEventSet.js +53 -0
- package/lib/activation-method.js +119 -0
- package/lib/agent.js +941 -0
- package/lib/apm-client/apm-client.js +313 -0
- package/lib/apm-client/http-apm-client/CHANGELOG.md +271 -0
- package/lib/apm-client/http-apm-client/README.md +485 -0
- package/lib/apm-client/http-apm-client/central-config.js +41 -0
- package/lib/apm-client/http-apm-client/container-info.js +111 -0
- package/lib/apm-client/http-apm-client/detect-hostname.js +96 -0
- package/lib/apm-client/http-apm-client/index.js +1975 -0
- package/lib/apm-client/http-apm-client/logging.js +31 -0
- package/lib/apm-client/http-apm-client/ndjson.js +20 -0
- package/lib/apm-client/http-apm-client/truncate.js +434 -0
- package/lib/apm-client/noop-apm-client.js +73 -0
- package/lib/async-hooks-polyfill.js +58 -0
- package/lib/cloud-metadata/aws.js +175 -0
- package/lib/cloud-metadata/azure.js +123 -0
- package/lib/cloud-metadata/callback-coordination.js +159 -0
- package/lib/cloud-metadata/gcp.js +133 -0
- package/lib/cloud-metadata/index.js +175 -0
- package/lib/config/config.js +458 -0
- package/lib/config/normalizers.js +701 -0
- package/lib/config/schema.js +1007 -0
- package/lib/constants.js +35 -0
- package/lib/errors.js +303 -0
- package/lib/filters/sanitize-field-names.js +69 -0
- package/lib/http-request.js +249 -0
- package/lib/instrumentation/azure-functions.js +519 -0
- package/lib/instrumentation/context.js +56 -0
- package/lib/instrumentation/dropped-spans-stats.js +112 -0
- package/lib/instrumentation/elasticsearch-shared.js +63 -0
- package/lib/instrumentation/express-utils.js +91 -0
- package/lib/instrumentation/generic-span.js +322 -0
- package/lib/instrumentation/http-shared.js +424 -0
- package/lib/instrumentation/ids.js +39 -0
- package/lib/instrumentation/index.js +1127 -0
- package/lib/instrumentation/modules/@apollo/server.js +30 -0
- package/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js +143 -0
- package/lib/instrumentation/modules/@aws-sdk/client-s3.js +230 -0
- package/lib/instrumentation/modules/@aws-sdk/client-sns.js +197 -0
- package/lib/instrumentation/modules/@aws-sdk/client-sqs.js +336 -0
- package/lib/instrumentation/modules/@elastic/elasticsearch.js +343 -0
- package/lib/instrumentation/modules/@hapi/hapi.js +221 -0
- package/lib/instrumentation/modules/@opentelemetry/api.js +86 -0
- package/lib/instrumentation/modules/@opentelemetry/sdk-metrics.js +79 -0
- package/lib/instrumentation/modules/@redis/client/dist/lib/client/commands-queue.js +178 -0
- package/lib/instrumentation/modules/@redis/client/dist/lib/client/index.js +49 -0
- package/lib/instrumentation/modules/@smithy/smithy-client.js +198 -0
- package/lib/instrumentation/modules/_lambda-handler.js +40 -0
- package/lib/instrumentation/modules/apollo-server-core.js +49 -0
- package/lib/instrumentation/modules/aws-sdk/dynamodb.js +155 -0
- package/lib/instrumentation/modules/aws-sdk/s3.js +184 -0
- package/lib/instrumentation/modules/aws-sdk/sns.js +232 -0
- package/lib/instrumentation/modules/aws-sdk/sqs.js +361 -0
- package/lib/instrumentation/modules/aws-sdk.js +76 -0
- package/lib/instrumentation/modules/bluebird.js +93 -0
- package/lib/instrumentation/modules/cassandra-driver.js +280 -0
- package/lib/instrumentation/modules/elasticsearch.js +191 -0
- package/lib/instrumentation/modules/express-graphql.js +66 -0
- package/lib/instrumentation/modules/express-queue.js +28 -0
- package/lib/instrumentation/modules/express.js +162 -0
- package/lib/instrumentation/modules/fastify.js +172 -0
- package/lib/instrumentation/modules/finalhandler.js +41 -0
- package/lib/instrumentation/modules/generic-pool.js +85 -0
- package/lib/instrumentation/modules/graphql.js +256 -0
- package/lib/instrumentation/modules/handlebars.js +22 -0
- package/lib/instrumentation/modules/http.js +112 -0
- package/lib/instrumentation/modules/http2.js +320 -0
- package/lib/instrumentation/modules/https.js +68 -0
- package/lib/instrumentation/modules/ioredis.js +94 -0
- package/lib/instrumentation/modules/jade.js +18 -0
- package/lib/instrumentation/modules/kafkajs.js +476 -0
- package/lib/instrumentation/modules/knex.js +91 -0
- package/lib/instrumentation/modules/koa-router.js +74 -0
- package/lib/instrumentation/modules/koa.js +15 -0
- package/lib/instrumentation/modules/memcached.js +99 -0
- package/lib/instrumentation/modules/mimic-response.js +45 -0
- package/lib/instrumentation/modules/mongodb/lib/cmap/connection_pool.js +40 -0
- package/lib/instrumentation/modules/mongodb-core.js +206 -0
- package/lib/instrumentation/modules/mongodb.js +259 -0
- package/lib/instrumentation/modules/mysql.js +200 -0
- package/lib/instrumentation/modules/mysql2.js +140 -0
- package/lib/instrumentation/modules/pg.js +148 -0
- package/lib/instrumentation/modules/pug.js +18 -0
- package/lib/instrumentation/modules/redis.js +176 -0
- package/lib/instrumentation/modules/restify.js +52 -0
- package/lib/instrumentation/modules/tedious.js +159 -0
- package/lib/instrumentation/modules/undici.js +270 -0
- package/lib/instrumentation/modules/ws.js +59 -0
- package/lib/instrumentation/noop-transaction.js +81 -0
- package/lib/instrumentation/run-context/AbstractRunContextManager.js +215 -0
- package/lib/instrumentation/run-context/AsyncHooksRunContextManager.js +106 -0
- package/lib/instrumentation/run-context/AsyncLocalStorageRunContextManager.js +73 -0
- package/lib/instrumentation/run-context/BasicRunContextManager.js +82 -0
- package/lib/instrumentation/run-context/RunContext.js +151 -0
- package/lib/instrumentation/run-context/index.js +23 -0
- package/lib/instrumentation/shimmer.js +123 -0
- package/lib/instrumentation/span-compression.js +239 -0
- package/lib/instrumentation/span.js +621 -0
- package/lib/instrumentation/template-shared.js +43 -0
- package/lib/instrumentation/timer.js +84 -0
- package/lib/instrumentation/transaction.js +571 -0
- package/lib/lambda.js +992 -0
- package/lib/load-source-map.js +100 -0
- package/lib/logging.js +212 -0
- package/lib/metrics/index.js +92 -0
- package/lib/metrics/platforms/generic/index.js +40 -0
- package/lib/metrics/platforms/generic/process-cpu.js +22 -0
- package/lib/metrics/platforms/generic/process-top.js +157 -0
- package/lib/metrics/platforms/generic/stats.js +34 -0
- package/lib/metrics/platforms/generic/system-cpu.js +51 -0
- package/lib/metrics/platforms/linux/index.js +19 -0
- package/lib/metrics/platforms/linux/stats.js +213 -0
- package/lib/metrics/queue.js +90 -0
- package/lib/metrics/registry.js +52 -0
- package/lib/metrics/reporter.js +119 -0
- package/lib/metrics/runtime.js +77 -0
- package/lib/middleware/connect.js +16 -0
- package/lib/opentelemetry-bridge/OTelBridgeNonRecordingSpan.js +150 -0
- package/lib/opentelemetry-bridge/OTelBridgeRunContext.js +124 -0
- package/lib/opentelemetry-bridge/OTelContextManager.js +82 -0
- package/lib/opentelemetry-bridge/OTelSpan.js +344 -0
- package/lib/opentelemetry-bridge/OTelTracer.js +201 -0
- package/lib/opentelemetry-bridge/OTelTracerProvider.js +25 -0
- package/lib/opentelemetry-bridge/README.md +244 -0
- package/lib/opentelemetry-bridge/index.js +15 -0
- package/lib/opentelemetry-bridge/oblog.js +23 -0
- package/lib/opentelemetry-bridge/opentelemetry-core-mini/README.md +3 -0
- package/lib/opentelemetry-bridge/opentelemetry-core-mini/internal/validators.js +52 -0
- package/lib/opentelemetry-bridge/opentelemetry-core-mini/trace/TraceState.js +109 -0
- package/lib/opentelemetry-bridge/otelutils.js +99 -0
- package/lib/opentelemetry-bridge/setup.js +76 -0
- package/lib/opentelemetry-metrics/ElasticApmMetricExporter.js +285 -0
- package/lib/opentelemetry-metrics/index.js +50 -0
- package/lib/parsers.js +225 -0
- package/lib/propwrap.js +147 -0
- package/lib/stacktraces.js +537 -0
- package/lib/symbols.js +15 -0
- package/lib/tracecontext/index.js +118 -0
- package/lib/tracecontext/traceparent.js +185 -0
- package/lib/tracecontext/tracestate.js +388 -0
- package/lib/wildcard-matcher.js +52 -0
- package/loader.mjs +7 -0
- package/package.json +299 -0
- package/start.d.ts +8 -0
- package/start.js +29 -0
- package/types/aws-lambda.d.ts +98 -0
- 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
|
+
};
|