@redthreadlabs/tracelog 1.4.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/README.md +126 -0
- package/index.d.ts +464 -0
- package/index.js +11 -0
- package/lib/InflightEventSet.js +53 -0
- package/lib/activation-method.js +97 -0
- package/lib/agent.js +1226 -0
- package/lib/apm-client/apm-client.js +107 -0
- package/lib/apm-client/channel-writer.js +334 -0
- package/lib/apm-client/jsonl-file-client.js +241 -0
- package/lib/apm-client/ndjson.js +20 -0
- package/lib/apm-client/noop-apm-client.js +79 -0
- package/lib/apm-client/s3-uploader.js +308 -0
- package/lib/apm-client/truncate.js +507 -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 +431 -0
- package/lib/config/normalizers.js +649 -0
- package/lib/config/schema.js +946 -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/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 +1078 -0
- package/lib/instrumentation/modules/@apollo/server.js +39 -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/@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/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 +200 -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 +179 -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 +33 -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 +29 -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 +100 -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 +29 -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/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/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 +115 -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 +98 -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,200 @@
|
|
|
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 EventEmitter = require('events');
|
|
10
|
+
|
|
11
|
+
var semver = require('semver');
|
|
12
|
+
var sqlSummary = require('sql-summary');
|
|
13
|
+
|
|
14
|
+
var shimmer = require('../shimmer');
|
|
15
|
+
var symbols = require('../../symbols');
|
|
16
|
+
var { getDBDestination } = require('../context');
|
|
17
|
+
|
|
18
|
+
module.exports = function (mysql, agent, { version, enabled }) {
|
|
19
|
+
if (!enabled) {
|
|
20
|
+
return mysql;
|
|
21
|
+
}
|
|
22
|
+
if (!semver.satisfies(version, '^2.0.0')) {
|
|
23
|
+
agent.logger.debug('mysql version %s not supported - aborting...', version);
|
|
24
|
+
return mysql;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
agent.logger.debug('shimming mysql.createPool');
|
|
28
|
+
shimmer.wrap(mysql, 'createPool', wrapCreatePool);
|
|
29
|
+
|
|
30
|
+
agent.logger.debug('shimming mysql.createPoolCluster');
|
|
31
|
+
shimmer.wrap(mysql, 'createPoolCluster', wrapCreatePoolCluster);
|
|
32
|
+
|
|
33
|
+
agent.logger.debug('shimming mysql.createConnection');
|
|
34
|
+
shimmer.wrap(mysql, 'createConnection', wrapCreateConnection);
|
|
35
|
+
|
|
36
|
+
return mysql;
|
|
37
|
+
|
|
38
|
+
function wrapCreateConnection(original) {
|
|
39
|
+
return function wrappedCreateConnection() {
|
|
40
|
+
var connection = original.apply(this, arguments);
|
|
41
|
+
|
|
42
|
+
wrapQueryable(connection, 'connection', agent);
|
|
43
|
+
|
|
44
|
+
return connection;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function wrapCreatePool(original) {
|
|
49
|
+
return function wrappedCreatePool() {
|
|
50
|
+
var pool = original.apply(this, arguments);
|
|
51
|
+
|
|
52
|
+
agent.logger.debug('shimming mysql pool.getConnection');
|
|
53
|
+
shimmer.wrap(pool, 'getConnection', wrapGetConnection);
|
|
54
|
+
|
|
55
|
+
return pool;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function wrapCreatePoolCluster(original) {
|
|
60
|
+
return function wrappedCreatePoolCluster() {
|
|
61
|
+
var cluster = original.apply(this, arguments);
|
|
62
|
+
|
|
63
|
+
agent.logger.debug('shimming mysql cluster.of');
|
|
64
|
+
shimmer.wrap(cluster, 'of', function wrapOf(original) {
|
|
65
|
+
return function wrappedOf() {
|
|
66
|
+
var ofCluster = original.apply(this, arguments);
|
|
67
|
+
|
|
68
|
+
agent.logger.debug('shimming mysql cluster of.getConnection');
|
|
69
|
+
shimmer.wrap(ofCluster, 'getConnection', wrapGetConnection);
|
|
70
|
+
|
|
71
|
+
return ofCluster;
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return cluster;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function wrapGetConnection(original) {
|
|
80
|
+
return function wrappedGetConnection() {
|
|
81
|
+
var cb = arguments[0];
|
|
82
|
+
|
|
83
|
+
if (typeof cb === 'function') {
|
|
84
|
+
arguments[0] = agent._instrumentation.bindFunction(
|
|
85
|
+
function wrapedCallback(_err, connection) {
|
|
86
|
+
if (connection)
|
|
87
|
+
wrapQueryable(connection, 'getConnection() > connection', agent);
|
|
88
|
+
return cb.apply(this, arguments);
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return original.apply(this, arguments);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
function wrapQueryable(connection, objType, agent) {
|
|
99
|
+
const ins = agent._instrumentation;
|
|
100
|
+
|
|
101
|
+
agent.logger.debug('shimming mysql %s.query', objType);
|
|
102
|
+
shimmer.wrap(connection, 'query', wrapQuery);
|
|
103
|
+
|
|
104
|
+
let host, port, user, database;
|
|
105
|
+
if (typeof connection.config === 'object') {
|
|
106
|
+
({ host, port, user, database } = connection.config);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function wrapQuery(original) {
|
|
110
|
+
return function wrappedQuery(sql, values, cb) {
|
|
111
|
+
agent.logger.debug('intercepted call to mysql %s.query', objType);
|
|
112
|
+
|
|
113
|
+
var span = ins.createSpan(null, 'db', 'mysql', 'query', {
|
|
114
|
+
exitSpan: true,
|
|
115
|
+
});
|
|
116
|
+
if (!span) {
|
|
117
|
+
return original.apply(this, arguments);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
var hasCallback = false;
|
|
121
|
+
var sqlStr;
|
|
122
|
+
|
|
123
|
+
if (this[symbols.knexStackObj]) {
|
|
124
|
+
span.customStackTrace(this[symbols.knexStackObj]);
|
|
125
|
+
this[symbols.knexStackObj] = null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const wrapCallback = function (origCallback) {
|
|
129
|
+
hasCallback = true;
|
|
130
|
+
return ins.bindFunction(function wrappedCallback(_err) {
|
|
131
|
+
span.end();
|
|
132
|
+
return origCallback.apply(this, arguments);
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
switch (typeof sql) {
|
|
137
|
+
case 'string':
|
|
138
|
+
sqlStr = sql;
|
|
139
|
+
break;
|
|
140
|
+
case 'object':
|
|
141
|
+
if (typeof sql._callback === 'function') {
|
|
142
|
+
sql._callback = wrapCallback(sql._callback);
|
|
143
|
+
}
|
|
144
|
+
sqlStr = sql.sql;
|
|
145
|
+
break;
|
|
146
|
+
case 'function':
|
|
147
|
+
arguments[0] = wrapCallback(sql);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (sqlStr) {
|
|
152
|
+
agent.logger.debug({ sql: sqlStr }, 'extracted sql from mysql query');
|
|
153
|
+
span.setDbContext({
|
|
154
|
+
statement: sqlStr,
|
|
155
|
+
type: 'sql',
|
|
156
|
+
user,
|
|
157
|
+
instance: database,
|
|
158
|
+
});
|
|
159
|
+
span.name = sqlSummary(sqlStr);
|
|
160
|
+
}
|
|
161
|
+
span._setDestinationContext(getDBDestination(host, port));
|
|
162
|
+
|
|
163
|
+
if (typeof values === 'function') {
|
|
164
|
+
arguments[1] = wrapCallback(values);
|
|
165
|
+
} else if (typeof cb === 'function') {
|
|
166
|
+
arguments[2] = wrapCallback(cb);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const spanRunContext = ins.currRunContext().enterSpan(span);
|
|
170
|
+
const result = ins.withRunContext(
|
|
171
|
+
spanRunContext,
|
|
172
|
+
original,
|
|
173
|
+
this,
|
|
174
|
+
...arguments,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (!hasCallback && result instanceof EventEmitter) {
|
|
178
|
+
// Wrap `result.emit` instead of `result.once('error', ...)` to avoid
|
|
179
|
+
// changing app behaviour by possibly setting the only 'error' handler.
|
|
180
|
+
shimmer.wrap(result, 'emit', function (origEmit) {
|
|
181
|
+
return function wrappedEmit(event, data) {
|
|
182
|
+
// The 'mysql' module emits 'end' even after an 'error' event.
|
|
183
|
+
switch (event) {
|
|
184
|
+
case 'error':
|
|
185
|
+
break;
|
|
186
|
+
case 'end':
|
|
187
|
+
span.end();
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
return origEmit.apply(this, arguments);
|
|
191
|
+
};
|
|
192
|
+
});
|
|
193
|
+
// Ensure event handlers execute in the caller run context.
|
|
194
|
+
ins.bindEmitter(result);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return result;
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
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
|
+
var semver = require('semver');
|
|
10
|
+
var sqlSummary = require('sql-summary');
|
|
11
|
+
|
|
12
|
+
var shimmer = require('../shimmer');
|
|
13
|
+
var symbols = require('../../symbols');
|
|
14
|
+
var { getDBDestination } = require('../context');
|
|
15
|
+
|
|
16
|
+
module.exports = function (mysql2, agent, { version, enabled }) {
|
|
17
|
+
if (!enabled) {
|
|
18
|
+
return mysql2;
|
|
19
|
+
}
|
|
20
|
+
if (!semver.satisfies(version, '>=1 <4')) {
|
|
21
|
+
agent.logger.debug(
|
|
22
|
+
'mysql2 version %s not supported - aborting...',
|
|
23
|
+
version,
|
|
24
|
+
);
|
|
25
|
+
return mysql2;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var ins = agent._instrumentation;
|
|
29
|
+
|
|
30
|
+
// mysql2@3.11.5 added BaseConnection class which is extended by Connection
|
|
31
|
+
// but is not in the public API so we need to extract it via prototype chain
|
|
32
|
+
// ref: https://github.com/sidorares/node-mysql2/pull/3081
|
|
33
|
+
const baseClass = Object.getPrototypeOf(mysql2.Connection);
|
|
34
|
+
const baseProto = baseClass.prototype;
|
|
35
|
+
const hasQuery = typeof baseProto?.query === 'function';
|
|
36
|
+
const hasExec = typeof baseProto?.execute === 'function';
|
|
37
|
+
const shouldPatchBase = hasQuery && hasExec;
|
|
38
|
+
|
|
39
|
+
if (shouldPatchBase) {
|
|
40
|
+
shimmer.wrap(baseProto, 'query', wrapQuery);
|
|
41
|
+
shimmer.wrap(baseProto, 'execute', wrapQuery);
|
|
42
|
+
} else {
|
|
43
|
+
shimmer.wrap(mysql2.Connection.prototype, 'query', wrapQuery);
|
|
44
|
+
shimmer.wrap(mysql2.Connection.prototype, 'execute', wrapQuery);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return mysql2;
|
|
48
|
+
|
|
49
|
+
function wrapQuery(original) {
|
|
50
|
+
return function wrappedQuery(sql, values, cb) {
|
|
51
|
+
agent.logger.debug('intercepted call to mysql2.%s', original.name);
|
|
52
|
+
|
|
53
|
+
var span = ins.createSpan(null, 'db', 'mysql', 'query', {
|
|
54
|
+
exitSpan: true,
|
|
55
|
+
});
|
|
56
|
+
if (!span) {
|
|
57
|
+
return original.apply(this, arguments);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (this[symbols.knexStackObj]) {
|
|
61
|
+
span.customStackTrace(this[symbols.knexStackObj]);
|
|
62
|
+
this[symbols.knexStackObj] = null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let hasCallback = false;
|
|
66
|
+
const wrapCallback = function (origCallback) {
|
|
67
|
+
hasCallback = true;
|
|
68
|
+
return ins.bindFunction(function wrappedCallback(_err) {
|
|
69
|
+
span.end();
|
|
70
|
+
return origCallback.apply(this, arguments);
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
let host, port, user, database;
|
|
75
|
+
if (typeof this.config === 'object') {
|
|
76
|
+
({ host, port, user, database } = this.config);
|
|
77
|
+
}
|
|
78
|
+
span._setDestinationContext(getDBDestination(host, port));
|
|
79
|
+
|
|
80
|
+
let sqlStr;
|
|
81
|
+
switch (typeof sql) {
|
|
82
|
+
case 'string':
|
|
83
|
+
sqlStr = sql;
|
|
84
|
+
break;
|
|
85
|
+
case 'object':
|
|
86
|
+
// `mysql2.{query,execute}` support the only arg being an instance
|
|
87
|
+
// of the internal mysql2 `Command` object.
|
|
88
|
+
if (typeof sql.onResult === 'function') {
|
|
89
|
+
sql.onResult = wrapCallback(sql.onResult);
|
|
90
|
+
}
|
|
91
|
+
sqlStr = sql.sql;
|
|
92
|
+
break;
|
|
93
|
+
case 'function':
|
|
94
|
+
arguments[0] = wrapCallback(sql);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
if (sqlStr) {
|
|
98
|
+
span.setDbContext({
|
|
99
|
+
type: 'sql',
|
|
100
|
+
instance: database,
|
|
101
|
+
user,
|
|
102
|
+
statement: sqlStr,
|
|
103
|
+
});
|
|
104
|
+
span.name = sqlSummary(sqlStr);
|
|
105
|
+
} else {
|
|
106
|
+
span.setDbContext({ type: 'sql', instance: database, user });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (typeof values === 'function') {
|
|
110
|
+
arguments[1] = wrapCallback(values);
|
|
111
|
+
} else if (typeof cb === 'function') {
|
|
112
|
+
arguments[2] = wrapCallback(cb);
|
|
113
|
+
}
|
|
114
|
+
const spanRunContext = ins.currRunContext().enterSpan(span);
|
|
115
|
+
const result = ins.withRunContext(
|
|
116
|
+
spanRunContext,
|
|
117
|
+
original,
|
|
118
|
+
this,
|
|
119
|
+
...arguments,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if (result && !hasCallback) {
|
|
123
|
+
ins.bindEmitter(result);
|
|
124
|
+
shimmer.wrap(result, 'emit', function (origEmit) {
|
|
125
|
+
return function (event) {
|
|
126
|
+
switch (event) {
|
|
127
|
+
case 'error':
|
|
128
|
+
case 'close':
|
|
129
|
+
case 'end':
|
|
130
|
+
span.end();
|
|
131
|
+
}
|
|
132
|
+
return origEmit.apply(this, arguments);
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return result;
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
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 EventEmitter = require('events');
|
|
10
|
+
|
|
11
|
+
var semver = require('semver');
|
|
12
|
+
var sqlSummary = require('sql-summary');
|
|
13
|
+
|
|
14
|
+
var shimmer = require('../shimmer');
|
|
15
|
+
var symbols = require('../../symbols');
|
|
16
|
+
var { getDBDestination } = require('../context');
|
|
17
|
+
|
|
18
|
+
module.exports = function (pg, agent, { version, enabled }) {
|
|
19
|
+
if (!enabled) {
|
|
20
|
+
return pg;
|
|
21
|
+
}
|
|
22
|
+
if (!semver.satisfies(version, '>=4.0.0 <9.0.0')) {
|
|
23
|
+
agent.logger.debug('pg version %s not supported - aborting...', version);
|
|
24
|
+
return pg;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
patchClient(pg.Client, 'pg.Client', agent);
|
|
28
|
+
|
|
29
|
+
// Trying to access the pg.native getter will trigger and log the warning
|
|
30
|
+
// "Cannot find module 'pg-native'" to STDERR if the module isn't installed.
|
|
31
|
+
// Overwriting the getter we can lazily patch the native client only if the
|
|
32
|
+
// user is acually requesting it.
|
|
33
|
+
var getter = pg.__lookupGetter__('native');
|
|
34
|
+
if (getter) {
|
|
35
|
+
delete pg.native;
|
|
36
|
+
// To be as true to the original pg module as possible, we use
|
|
37
|
+
// __defineGetter__ instead of Object.defineProperty.
|
|
38
|
+
pg.__defineGetter__('native', function () {
|
|
39
|
+
var native = getter();
|
|
40
|
+
if (native && native.Client) {
|
|
41
|
+
patchClient(native.Client, 'pg.native.Client', agent);
|
|
42
|
+
}
|
|
43
|
+
return native;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return pg;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
function patchClient(Client, klass, agent) {
|
|
51
|
+
agent.logger.debug('shimming %s.prototype.query', klass);
|
|
52
|
+
shimmer.wrap(Client.prototype, 'query', wrapQuery);
|
|
53
|
+
|
|
54
|
+
function wrapQuery(orig, name) {
|
|
55
|
+
return function wrappedFunction(sql) {
|
|
56
|
+
agent.logger.debug('intercepted call to %s.prototype.%s', klass, name);
|
|
57
|
+
const ins = agent._instrumentation;
|
|
58
|
+
const span = ins.createSpan('SQL', 'db', 'postgresql', 'query', {
|
|
59
|
+
exitSpan: true,
|
|
60
|
+
});
|
|
61
|
+
if (!span) {
|
|
62
|
+
return orig.apply(this, arguments);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Get connection parameters from Client.
|
|
66
|
+
let host, port, database, user;
|
|
67
|
+
if (typeof this.connectionParameters === 'object') {
|
|
68
|
+
({ host, port, database, user } = this.connectionParameters);
|
|
69
|
+
}
|
|
70
|
+
span._setDestinationContext(getDBDestination(host, port));
|
|
71
|
+
|
|
72
|
+
const dbContext = { type: 'sql' };
|
|
73
|
+
let sqlText = sql;
|
|
74
|
+
if (sql && typeof sql.text === 'string') {
|
|
75
|
+
sqlText = sql.text;
|
|
76
|
+
}
|
|
77
|
+
if (typeof sqlText === 'string') {
|
|
78
|
+
span.name = sqlSummary(sqlText);
|
|
79
|
+
dbContext.statement = sqlText;
|
|
80
|
+
} else {
|
|
81
|
+
agent.logger.debug(
|
|
82
|
+
'unable to parse sql form pg module (type: %s)',
|
|
83
|
+
typeof sqlText,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
if (database) {
|
|
87
|
+
dbContext.instance = database;
|
|
88
|
+
}
|
|
89
|
+
if (user) {
|
|
90
|
+
dbContext.user = user;
|
|
91
|
+
}
|
|
92
|
+
span.setDbContext(dbContext);
|
|
93
|
+
|
|
94
|
+
if (this[symbols.knexStackObj]) {
|
|
95
|
+
span.customStackTrace(this[symbols.knexStackObj]);
|
|
96
|
+
this[symbols.knexStackObj] = null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let index = arguments.length - 1;
|
|
100
|
+
let cb = arguments[index];
|
|
101
|
+
if (Array.isArray(cb)) {
|
|
102
|
+
index = cb.length - 1;
|
|
103
|
+
cb = cb[index];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const spanRunContext = ins.currRunContext().enterSpan(span);
|
|
107
|
+
const onQueryEnd = ins.bindFunctionToRunContext(
|
|
108
|
+
spanRunContext,
|
|
109
|
+
(_err) => {
|
|
110
|
+
agent.logger.debug('intercepted end of %s.prototype.%s', klass, name);
|
|
111
|
+
span.end();
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
if (typeof cb === 'function') {
|
|
116
|
+
arguments[index] = ins.bindFunction((err, res) => {
|
|
117
|
+
onQueryEnd(err);
|
|
118
|
+
return cb(err, res);
|
|
119
|
+
});
|
|
120
|
+
return orig.apply(this, arguments);
|
|
121
|
+
} else {
|
|
122
|
+
var queryOrPromise = orig.apply(this, arguments);
|
|
123
|
+
|
|
124
|
+
// It is important to prefer `.on` to `.then` for pg <7 >=6.3.0, because
|
|
125
|
+
// `query.then` is broken in those versions. See
|
|
126
|
+
// https://github.com/brianc/node-postgres/commit/b5b49eb895727e01290e90d08292c0d61ab86322#r23267714
|
|
127
|
+
if (typeof queryOrPromise.on === 'function') {
|
|
128
|
+
queryOrPromise.on('end', onQueryEnd);
|
|
129
|
+
queryOrPromise.on('error', onQueryEnd);
|
|
130
|
+
if (queryOrPromise instanceof EventEmitter) {
|
|
131
|
+
ins.bindEmitter(queryOrPromise);
|
|
132
|
+
}
|
|
133
|
+
} else if (typeof queryOrPromise.then === 'function') {
|
|
134
|
+
queryOrPromise.then(() => {
|
|
135
|
+
onQueryEnd();
|
|
136
|
+
}, onQueryEnd);
|
|
137
|
+
} else {
|
|
138
|
+
agent.logger.debug(
|
|
139
|
+
'ERROR: unknown pg query type: %s',
|
|
140
|
+
typeof queryOrPromise,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return queryOrPromise;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
var semver = require('semver');
|
|
10
|
+
|
|
11
|
+
var shimmer = require('../shimmer');
|
|
12
|
+
var templateShared = require('../template-shared');
|
|
13
|
+
|
|
14
|
+
module.exports = function (pug, agent, { version, enabled }) {
|
|
15
|
+
if (!enabled) return pug;
|
|
16
|
+
|
|
17
|
+
if (!semver.satisfies(version, '>=0.1.0 <4')) {
|
|
18
|
+
agent.logger.debug(
|
|
19
|
+
'cannot instrument pug version %s, skipping pug instrumentation',
|
|
20
|
+
version,
|
|
21
|
+
);
|
|
22
|
+
return pug;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
agent.logger.debug('shimming pug.compile');
|
|
26
|
+
shimmer.wrap(pug, 'compile', templateShared.wrapCompile(agent, 'pug'));
|
|
27
|
+
|
|
28
|
+
return pug;
|
|
29
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
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
|
+
var semver = require('semver');
|
|
10
|
+
|
|
11
|
+
const constants = require('../../constants');
|
|
12
|
+
var shimmer = require('../shimmer');
|
|
13
|
+
var { getDBDestination } = require('../context');
|
|
14
|
+
|
|
15
|
+
const isWrappedRedisCbSym = Symbol('ElasticAPMIsWrappedRedisCb');
|
|
16
|
+
|
|
17
|
+
const TYPE = 'db';
|
|
18
|
+
const SUBTYPE = 'redis';
|
|
19
|
+
const ACTION = 'query';
|
|
20
|
+
|
|
21
|
+
module.exports = function (redis, agent, { version, enabled }) {
|
|
22
|
+
if (!enabled) {
|
|
23
|
+
return redis;
|
|
24
|
+
}
|
|
25
|
+
if (!semver.satisfies(version, '>=2.0.0 <4.0.0')) {
|
|
26
|
+
// Explicitly do not log.debug here, because the message is misleading for
|
|
27
|
+
// redis@4 and later that is being handled by @redis/client instrumentation.
|
|
28
|
+
return redis;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const ins = agent._instrumentation;
|
|
32
|
+
|
|
33
|
+
// The undocumented field on a RedisClient instance on which connection
|
|
34
|
+
// options are stored has changed a few times.
|
|
35
|
+
//
|
|
36
|
+
// - >=2.4.0: `client.connection_options.{host,port}`, commit eae5596a
|
|
37
|
+
// - >=2.3.0, <2.4.0: `client.connection_option.{host,port}`, commit d454e402
|
|
38
|
+
// - >=0.12.0, <2.3.0: `client.connectionOption.{host,port}`, commit 064260d1
|
|
39
|
+
// - <0.12.0: *maybe* `client.{host,port}`
|
|
40
|
+
const connOptsFromRedisClient = (rc) =>
|
|
41
|
+
rc.connection_options || rc.connection_option || rc.connectionOption || {};
|
|
42
|
+
|
|
43
|
+
var proto = redis.RedisClient && redis.RedisClient.prototype;
|
|
44
|
+
if (semver.satisfies(version, '>2.5.3')) {
|
|
45
|
+
agent.logger.debug(
|
|
46
|
+
'shimming redis.RedisClient.prototype.internal_send_command',
|
|
47
|
+
);
|
|
48
|
+
shimmer.wrap(proto, 'internal_send_command', wrapInternalSendCommand);
|
|
49
|
+
} else {
|
|
50
|
+
agent.logger.debug('shimming redis.RedisClient.prototype.send_command');
|
|
51
|
+
shimmer.wrap(proto, 'send_command', wrapSendCommand);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return redis;
|
|
55
|
+
|
|
56
|
+
function makeWrappedCallback(spanRunContext, span, origCb) {
|
|
57
|
+
const wrappedCallback = ins.bindFunctionToRunContext(
|
|
58
|
+
spanRunContext,
|
|
59
|
+
function (err, _reply) {
|
|
60
|
+
if (err) {
|
|
61
|
+
span._setOutcomeFromErrorCapture(constants.OUTCOME_FAILURE);
|
|
62
|
+
agent.captureError(err, { skipOutcome: true });
|
|
63
|
+
}
|
|
64
|
+
span.end();
|
|
65
|
+
if (origCb) {
|
|
66
|
+
return origCb.apply(this, arguments);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
wrappedCallback[isWrappedRedisCbSym] = true;
|
|
71
|
+
return wrappedCallback;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function wrapInternalSendCommand(original) {
|
|
75
|
+
return function wrappedInternalSendCommand(commandObj) {
|
|
76
|
+
if (!commandObj || typeof commandObj.command !== 'string') {
|
|
77
|
+
// Unexpected usage. Skip instrumenting this call.
|
|
78
|
+
return original.apply(this, arguments);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (commandObj.callback && commandObj.callback[isWrappedRedisCbSym]) {
|
|
82
|
+
// Avoid re-wrapping internal_send_command called *again* for commands
|
|
83
|
+
// queued before the client was "ready".
|
|
84
|
+
return original.apply(this, arguments);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const command = commandObj.command;
|
|
88
|
+
agent.logger.debug(
|
|
89
|
+
{ command },
|
|
90
|
+
'intercepted call to RedisClient.prototype.internal_send_command',
|
|
91
|
+
);
|
|
92
|
+
const span = ins.createSpan(
|
|
93
|
+
command.toUpperCase(),
|
|
94
|
+
TYPE,
|
|
95
|
+
SUBTYPE,
|
|
96
|
+
ACTION,
|
|
97
|
+
{ exitSpan: true },
|
|
98
|
+
);
|
|
99
|
+
if (!span) {
|
|
100
|
+
return original.apply(this, arguments);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const connOpts = connOptsFromRedisClient(this);
|
|
104
|
+
span._setDestinationContext(
|
|
105
|
+
getDBDestination(connOpts.host, connOpts.port),
|
|
106
|
+
);
|
|
107
|
+
span.setDbContext({ type: 'redis' });
|
|
108
|
+
|
|
109
|
+
const spanRunContext = ins.currRunContext().enterSpan(span);
|
|
110
|
+
commandObj.callback = makeWrappedCallback(
|
|
111
|
+
spanRunContext,
|
|
112
|
+
span,
|
|
113
|
+
commandObj.callback,
|
|
114
|
+
);
|
|
115
|
+
return ins.withRunContext(spanRunContext, original, this, ...arguments);
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function wrapSendCommand(original) {
|
|
120
|
+
return function wrappedSendCommand(command, args, cb) {
|
|
121
|
+
if (typeof command !== 'string') {
|
|
122
|
+
// Unexpected usage. Skip instrumenting this call.
|
|
123
|
+
return original.apply(this, arguments);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let origCb = cb;
|
|
127
|
+
if (
|
|
128
|
+
!origCb &&
|
|
129
|
+
Array.isArray(args) &&
|
|
130
|
+
typeof args[args.length - 1] === 'function'
|
|
131
|
+
) {
|
|
132
|
+
origCb = args[args.length - 1];
|
|
133
|
+
}
|
|
134
|
+
if (origCb && origCb[isWrappedRedisCbSym]) {
|
|
135
|
+
// Avoid re-wrapping send_command called *again* for commands queued
|
|
136
|
+
// before the client was "ready".
|
|
137
|
+
return original.apply(this, arguments);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
agent.logger.debug(
|
|
141
|
+
{ command },
|
|
142
|
+
'intercepted call to RedisClient.prototype.send_command',
|
|
143
|
+
);
|
|
144
|
+
var span = ins.createSpan(command.toUpperCase(), TYPE, SUBTYPE, ACTION, {
|
|
145
|
+
exitSpan: true,
|
|
146
|
+
});
|
|
147
|
+
if (!span) {
|
|
148
|
+
return original.apply(this, arguments);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const connOpts = connOptsFromRedisClient(this);
|
|
152
|
+
span._setDestinationContext(
|
|
153
|
+
getDBDestination(connOpts.host, connOpts.port),
|
|
154
|
+
);
|
|
155
|
+
span.setDbContext({ type: 'redis' });
|
|
156
|
+
|
|
157
|
+
const spanRunContext = ins.currRunContext().enterSpan(span);
|
|
158
|
+
const wrappedCb = makeWrappedCallback(spanRunContext, span, origCb);
|
|
159
|
+
if (cb) {
|
|
160
|
+
cb = wrappedCb;
|
|
161
|
+
} else if (origCb) {
|
|
162
|
+
args[args.length - 1] = wrappedCb;
|
|
163
|
+
} else {
|
|
164
|
+
cb = wrappedCb;
|
|
165
|
+
}
|
|
166
|
+
return ins.withRunContext(
|
|
167
|
+
spanRunContext,
|
|
168
|
+
original,
|
|
169
|
+
this,
|
|
170
|
+
command,
|
|
171
|
+
args,
|
|
172
|
+
cb,
|
|
173
|
+
);
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
};
|