whatap 1.0.2 → 1.0.3-canary.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/README.md +77 -33
- package/agent/darwin/arm64/whatap_nodejs +0 -0
- package/agent/linux/amd64/whatap_nodejs +0 -0
- package/agent/linux/arm64/whatap_nodejs +0 -0
- package/build.txt +4 -0
- package/lib/conf/config-default.js +3 -10
- package/lib/conf/configure.js +349 -369
- package/lib/conf/license.js +1 -1
- package/lib/control/packagectr-helper.js +3 -34
- package/lib/core/agent.js +888 -176
- package/lib/core/interceptor.js +6 -6
- package/lib/core/shimmer.js +36 -82
- package/lib/counter/counter-manager.js +8 -79
- package/lib/counter/task/activetransaction.js +17 -68
- package/lib/io/data-inputx.js +3 -13
- package/lib/io/data-outputx.js +206 -268
- package/lib/logger.js +6 -6
- package/lib/net/security-master.js +20 -139
- package/lib/observers/apollo-server-observer.js +27 -33
- package/lib/observers/global-observer.js +80 -155
- package/lib/observers/http-observer.js +236 -666
- package/lib/observers/ioredis-observer.js +294 -0
- package/lib/observers/maria-observer.js +362 -204
- package/lib/observers/mongodb-observer.js +226 -169
- package/lib/observers/mongoose-observer.js +323 -518
- package/lib/observers/mssql-observer.js +418 -177
- package/lib/observers/mysql-observer.js +449 -342
- package/lib/observers/mysql2-observer.js +358 -396
- package/lib/observers/oracle-observer.js +384 -559
- package/lib/observers/pgsql-observer.js +489 -231
- package/lib/observers/prisma-observer.js +92 -303
- package/lib/observers/process-observer.js +35 -79
- package/lib/observers/redis-observer.js +331 -166
- package/lib/observers/socket.io-observer.js +187 -226
- package/lib/observers/websocket-observer.js +301 -175
- package/lib/pack/counter-pack.js +0 -3
- package/lib/pack/log-sink-pack.js +52 -14
- package/lib/pack/tagcount-pack.js +4 -4
- package/lib/{counter/task → system}/gc-action.js +74 -27
- package/lib/trace/trace-context-manager.js +25 -113
- package/lib/trace/trace-context.js +7 -21
- package/lib/trace/trace-httpc.js +11 -17
- package/lib/trace/trace-sql.js +21 -29
- package/lib/udp/async_sender.js +119 -0
- package/lib/udp/index.js +17 -0
- package/lib/udp/packet_enum.js +52 -0
- package/lib/udp/packet_queue.js +69 -0
- package/lib/udp/packet_type_enum.js +33 -0
- package/lib/udp/param_def.js +72 -0
- package/lib/udp/udp_session.js +336 -0
- package/lib/util/escape-literal-sql.js +5 -5
- package/lib/util/hashutil.js +18 -18
- package/lib/util/keygen.js +3 -0
- package/lib/util/linkedset.js +2 -1
- package/lib/util/nodeutil.js +1 -2
- package/lib/util/sql-util.js +178 -0
- package/lib/util/trace-helper.js +91 -0
- package/lib/util/transfer.js +58 -0
- package/lib/value/map-value.js +2 -3
- package/package.json +5 -9
- package/whatap.conf +4 -1
- package/lib/conf/conf-sys-mon.js +0 -101
- package/lib/control/cmd-config.js +0 -24
- package/lib/control/control-handler.js +0 -367
- package/lib/core/request-agent.js +0 -27
- package/lib/counter/meter/meter-activex.js +0 -67
- package/lib/counter/meter/meter-httpc.js +0 -57
- package/lib/counter/meter/meter-resource.js +0 -9
- package/lib/counter/meter/meter-service.js +0 -168
- package/lib/counter/meter/meter-socket.io.js +0 -51
- package/lib/counter/meter/meter-sql.js +0 -71
- package/lib/counter/meter/meter-users.js +0 -58
- package/lib/counter/meter.js +0 -183
- package/lib/counter/task/agentinfo.js +0 -107
- package/lib/counter/task/gcstat.js +0 -34
- package/lib/counter/task/heapmem.js +0 -25
- package/lib/counter/task/httpc.js +0 -76
- package/lib/counter/task/metering-info.js +0 -125
- package/lib/counter/task/proc-cpu.js +0 -29
- package/lib/counter/task/realtimeuser.js +0 -31
- package/lib/counter/task/res/systemECSTask.js +0 -39
- package/lib/counter/task/res/systemKubeTask.js +0 -53
- package/lib/counter/task/res/util/awsEcsClientThread.js +0 -218
- package/lib/counter/task/res/util/linuxProcStatUtil.js +0 -14
- package/lib/counter/task/res-sys-cpu.js +0 -62
- package/lib/counter/task/service.js +0 -202
- package/lib/counter/task/socketio.js +0 -30
- package/lib/counter/task/sql.js +0 -105
- package/lib/counter/task/systemperf.js +0 -43
- package/lib/data/datapack-sender.js +0 -289
- package/lib/data/dataprofile-agent.js +0 -162
- package/lib/data/datatext-agent.js +0 -135
- package/lib/data/event-level.js +0 -15
- package/lib/data/test.js +0 -49
- package/lib/data/zipprofile.js +0 -197
- package/lib/env/constants.js +0 -21
- package/lib/error/error-handler.js +0 -437
- package/lib/kube/kube-client.js +0 -144
- package/lib/lang/text-types.js +0 -58
- package/lib/logsink/line-log-util.js +0 -87
- package/lib/logsink/line-log.js +0 -12
- package/lib/logsink/log-sender.js +0 -78
- package/lib/logsink/log-tracer.js +0 -40
- package/lib/logsink/sender-util.js +0 -56
- package/lib/logsink/zip/zip-send.js +0 -177
- package/lib/net/netflag.js +0 -55
- package/lib/net/receiver.js +0 -66
- package/lib/net/sender.js +0 -141
- package/lib/net/tcp-return.js +0 -18
- package/lib/net/tcp-session.js +0 -286
- package/lib/net/tcpreq-client-proxy.js +0 -70
- package/lib/net/tcprequest-mgr.js +0 -58
- package/lib/observers/cluster-observer.js +0 -22
- package/lib/observers/express-observer.js +0 -215
- package/lib/observers/file-observer.js +0 -184
- package/lib/observers/grpc-observer.js +0 -336
- package/lib/observers/memcached-observer.js +0 -56
- package/lib/observers/mongo-observer.js +0 -317
- package/lib/observers/net-observer.js +0 -77
- package/lib/observers/promise-observer.js +0 -31
- package/lib/observers/schedule-observer.js +0 -67
- package/lib/observers/stream-observer.js +0 -19
- package/lib/observers/thrift-observer.js +0 -197
- package/lib/pack/activestack-pack.js +0 -55
- package/lib/pack/apenum.js +0 -8
- package/lib/pack/errorsnap-pack.js +0 -69
- package/lib/pack/event-pack.js +0 -54
- package/lib/pack/hitmap-pack.js +0 -63
- package/lib/pack/hitmap-pack1.js +0 -152
- package/lib/pack/netstat.js +0 -15
- package/lib/pack/otype.js +0 -7
- package/lib/pack/profile-pack.js +0 -49
- package/lib/pack/realtimeuser-pack.js +0 -41
- package/lib/pack/stat-general-pack.js +0 -96
- package/lib/pack/staterror-pack.js +0 -120
- package/lib/pack/stathttpc-pack.js +0 -66
- package/lib/pack/stathttpc-rec.js +0 -78
- package/lib/pack/statremote-pack.js +0 -46
- package/lib/pack/statservice-pack.js +0 -63
- package/lib/pack/statservice-pack1.js +0 -88
- package/lib/pack/statservice-rec.js +0 -292
- package/lib/pack/statservice-rec_dep.js +0 -151
- package/lib/pack/statsql-pack.js +0 -69
- package/lib/pack/statsql-rec.js +0 -100
- package/lib/pack/statuseragent-pack.js +0 -44
- package/lib/pack/tagctr.js +0 -15
- package/lib/pack/text-pack.js +0 -50
- package/lib/pack/time-count.js +0 -25
- package/lib/pack/websocket.js +0 -15
- package/lib/pack/zip-pack.js +0 -70
- package/lib/pii/pii-item.js +0 -31
- package/lib/pii/pii-mask.js +0 -174
- package/lib/plugin/plugin-loadermanager.js +0 -57
- package/lib/plugin/plugin.js +0 -75
- package/lib/service/tx-record.js +0 -332
- package/lib/stat/stat-error.js +0 -116
- package/lib/stat/stat-httpc.js +0 -98
- package/lib/stat/stat-remote-ip.js +0 -46
- package/lib/stat/stat-remote-ipurl.js +0 -88
- package/lib/stat/stat-sql.js +0 -113
- package/lib/stat/stat-tranx.js +0 -58
- package/lib/stat/stat-tx-caller.js +0 -160
- package/lib/stat/stat-tx-domain.js +0 -111
- package/lib/stat/stat-tx-referer.js +0 -112
- package/lib/stat/stat-useragent.js +0 -48
- package/lib/stat/timingsender.js +0 -76
- package/lib/step/activestack-step.js +0 -38
- package/lib/step/dbc-step.js +0 -36
- package/lib/step/http-stepx.js +0 -67
- package/lib/step/message-step.js +0 -40
- package/lib/step/method-stepx.js +0 -45
- package/lib/step/resultset-step.js +0 -40
- package/lib/step/securemsg-step.js +0 -44
- package/lib/step/socket-step.js +0 -46
- package/lib/step/sql-stepx.js +0 -68
- package/lib/step/sqlxtype.js +0 -16
- package/lib/step/step.js +0 -66
- package/lib/step/stepenum.js +0 -54
- package/lib/topology/link.js +0 -63
- package/lib/topology/nodeinfo.js +0 -123
- package/lib/topology/status-detector.js +0 -111
- package/lib/util/anylist.js +0 -103
- package/lib/util/cardinality/hyperloglog.js +0 -106
- package/lib/util/cardinality/murmurhash.js +0 -31
- package/lib/util/cardinality/registerset.js +0 -75
- package/lib/util/errordata.js +0 -21
- package/lib/util/iputil_x.js +0 -527
- package/lib/util/kube-util.js +0 -73
- package/lib/util/paramsecurity.js +0 -80
- package/lib/util/pre-process.js +0 -13
- package/lib/util/process-seq.js +0 -166
- package/lib/util/property-util.js +0 -36
- package/lib/util/request-queue.js +0 -70
- package/lib/util/requestdouble-queue.js +0 -72
- package/lib/util/resourceprofile.js +0 -157
- package/lib/util/stop-watch.js +0 -30
- package/lib/util/system-util.js +0 -10
- package/lib/util/userid-util.js +0 -57
|
@@ -5,25 +5,18 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
var TraceContextManager = require('../trace/trace-context-manager'),
|
|
8
|
-
ParsedSql
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
ParamSecurity = require('../util/paramsecurity'),
|
|
21
|
-
Logger = require('../logger'),
|
|
22
|
-
Buffer = require('buffer').Buffer,
|
|
23
|
-
DateUtil = require('../util/dateutil'),
|
|
24
|
-
TraceSQL = require('../trace/trace-sql');
|
|
25
|
-
const shimmer = require('../core/shimmer');
|
|
26
|
-
const ResultSetStep = require("../step/resultset-step");
|
|
8
|
+
ParsedSql = require('../trace/parsed-sql'),
|
|
9
|
+
conf = require('../conf/configure'),
|
|
10
|
+
IntKeyMap = require('../util/intkey-map'),
|
|
11
|
+
EscapeLiteralSQL = require('../util/escape-literal-sql'),
|
|
12
|
+
HashUtil = require('../util/hashutil'),
|
|
13
|
+
Logger = require('../logger'),
|
|
14
|
+
DateUtil = require('../util/dateutil'),
|
|
15
|
+
AsyncSender = require('../udp/async_sender'),
|
|
16
|
+
PacketTypeEnum = require('../udp/packet_type_enum'),
|
|
17
|
+
shimmer = require('../core/shimmer'),
|
|
18
|
+
TraceSQL = require('../trace/trace-sql'),
|
|
19
|
+
AsyncResource = require('async_hooks').AsyncResource;
|
|
27
20
|
|
|
28
21
|
var PgSqlObserver = function (agent) {
|
|
29
22
|
this.agent = agent;
|
|
@@ -31,274 +24,539 @@ var PgSqlObserver = function (agent) {
|
|
|
31
24
|
};
|
|
32
25
|
|
|
33
26
|
var dbc_hash = 0;
|
|
34
|
-
var dbc
|
|
35
|
-
PgSqlObserver.prototype.inject = function (mod, moduleName) {
|
|
36
|
-
if (mod.__whatap_observe__) { return; }
|
|
37
|
-
mod.__whatap_observe__ = true;
|
|
27
|
+
var dbc = 'postgresql://';
|
|
38
28
|
|
|
39
|
-
|
|
29
|
+
var hookedInstances = new WeakSet();
|
|
40
30
|
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
var checkedSql = new IntKeyMap(2000).setMax(2000);
|
|
32
|
+
var nonLiteSql = new IntKeyMap(5000).setMax(5000);
|
|
33
|
+
var date = DateUtil.yyyymmdd();
|
|
43
34
|
|
|
44
|
-
|
|
35
|
+
function handleSqlError(ctx, err) {
|
|
36
|
+
if (!err) return;
|
|
45
37
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
38
|
+
try {
|
|
39
|
+
var errorClass = err.code || err.name || err.constructor?.name || 'PostgreSQLError';
|
|
40
|
+
var errorMessage = err.message || 'postgresql error';
|
|
41
|
+
var errorStack = '';
|
|
42
|
+
|
|
43
|
+
if (conf.trace_sql_error_stack && conf.trace_sql_error_depth && err.stack) {
|
|
44
|
+
var traceDepth = conf.trace_sql_error_depth;
|
|
45
|
+
var stackLines = err.stack.split("\n");
|
|
46
|
+
if (stackLines.length > traceDepth) {
|
|
47
|
+
stackLines = stackLines.slice(0, traceDepth + 1);
|
|
48
|
+
}
|
|
49
|
+
errorStack = stackLines.join("\n");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
var shouldAddError = false;
|
|
53
|
+
if (conf._is_trace_ignore_err_cls_contains === true && errorClass &&
|
|
54
|
+
errorClass.indexOf(conf.trace_ignore_err_cls_contains) < 0) {
|
|
55
|
+
shouldAddError = true;
|
|
56
|
+
} else if (conf._is_trace_ignore_err_msg_contains === true && errorMessage &&
|
|
57
|
+
errorMessage.indexOf(conf.trace_ignore_err_msg_contains) < 0) {
|
|
58
|
+
shouldAddError = true;
|
|
59
|
+
} else if (conf._is_trace_ignore_err_cls_contains === false &&
|
|
60
|
+
conf._is_trace_ignore_err_msg_contains === false) {
|
|
61
|
+
shouldAddError = true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (shouldAddError) {
|
|
65
|
+
if (!ctx.error) ctx.error = 1;
|
|
66
|
+
ctx.status = 500;
|
|
67
|
+
var errors = [errorClass];
|
|
68
|
+
if (errorMessage) {
|
|
69
|
+
errors.push(errorMessage);
|
|
70
|
+
}
|
|
71
|
+
if (errorStack || err.stack) {
|
|
72
|
+
errors.push(errorStack || err.stack);
|
|
51
73
|
}
|
|
52
74
|
|
|
53
|
-
|
|
54
|
-
|
|
75
|
+
AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
|
|
76
|
+
}
|
|
77
|
+
} catch (e) {
|
|
78
|
+
Logger.printError('WHATAP-204', 'Error handling PgSQL error', e, false);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
55
81
|
|
|
56
|
-
|
|
57
|
-
|
|
82
|
+
function setupDbcInfo(args) {
|
|
83
|
+
if (dbc_hash === 0 && args.length > 0) {
|
|
84
|
+
var info = args[0] || {};
|
|
85
|
+
dbc = 'postgresql://';
|
|
86
|
+
dbc += (info.user || '') + '@';
|
|
87
|
+
dbc += (info.host || '') + '/';
|
|
88
|
+
dbc += info.database || '';
|
|
89
|
+
dbc_hash = HashUtil.hashFromString(dbc);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
58
92
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// ctx.footprint('PgSql Connecting Done');
|
|
80
|
-
// ctx.profile.push(dbc_step);
|
|
81
|
-
}
|
|
82
|
-
};
|
|
93
|
+
function hookConnectionMethods(connection, agent) {
|
|
94
|
+
if (connection && !hookedInstances.has(connection)) {
|
|
95
|
+
hookedInstances.add(connection);
|
|
96
|
+
Logger.print('WHATAP-201', 'Hooking new connection object', false);
|
|
97
|
+
|
|
98
|
+
if (dbc_hash === 0 && connection.connectionParameters) {
|
|
99
|
+
var info = connection.connectionParameters;
|
|
100
|
+
dbc = 'postgresql://';
|
|
101
|
+
dbc += (info.user || '') + '@';
|
|
102
|
+
dbc += (info.host || '') + '/';
|
|
103
|
+
dbc += info.database || '';
|
|
104
|
+
dbc_hash = HashUtil.hashFromString(dbc);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (connection.query && !connection.query.__whatap_wrapped__) {
|
|
108
|
+
shimmer.wrap(connection, 'query', createQueryWrapper(agent, dbc));
|
|
109
|
+
connection.query.__whatap_wrapped__ = true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
83
113
|
|
|
84
|
-
|
|
85
|
-
|
|
114
|
+
function escapeLiteral(sql) {
|
|
115
|
+
if (sql == null) {
|
|
116
|
+
sql = '';
|
|
117
|
+
}
|
|
86
118
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
119
|
+
if (date !== DateUtil.yyyymmdd()) {
|
|
120
|
+
checkedSql.clear();
|
|
121
|
+
nonLiteSql.clear();
|
|
122
|
+
date = DateUtil.yyyymmdd();
|
|
123
|
+
}
|
|
92
124
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
125
|
+
var sqlHash = HashUtil.hashFromString(sql);
|
|
126
|
+
var psql = nonLiteSql.get(sqlHash);
|
|
127
|
+
|
|
128
|
+
if (psql != null) {
|
|
129
|
+
return psql;
|
|
130
|
+
}
|
|
131
|
+
psql = checkedSql.get(sqlHash);
|
|
132
|
+
|
|
133
|
+
if (psql != null) {
|
|
134
|
+
return psql;
|
|
135
|
+
}
|
|
100
136
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
137
|
+
var els = new EscapeLiteralSQL(sql);
|
|
138
|
+
els.process();
|
|
139
|
+
|
|
140
|
+
var hash = HashUtil.hashFromString(els.getParsedSql());
|
|
141
|
+
|
|
142
|
+
if (hash === sqlHash) {
|
|
143
|
+
psql = new ParsedSql(els.sqlType, hash, null);
|
|
144
|
+
nonLiteSql.put(sqlHash, psql);
|
|
145
|
+
} else {
|
|
146
|
+
psql = new ParsedSql(els.sqlType, hash, els.getParameter());
|
|
147
|
+
checkedSql.put(sqlHash, psql);
|
|
148
|
+
}
|
|
149
|
+
return psql;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
var createQueryWrapper = function(agent, dbcUrl) {
|
|
153
|
+
return function(original) {
|
|
154
|
+
return function wrappedQuery() {
|
|
155
|
+
var args = Array.prototype.slice.call(arguments);
|
|
156
|
+
var ctx = TraceContextManager.getCurrentContext();
|
|
157
|
+
|
|
158
|
+
if (ctx == null || args[0] == null) {
|
|
105
159
|
return original.apply(this, arguments);
|
|
106
160
|
}
|
|
107
161
|
|
|
108
|
-
if(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
dbc_step.start_time = ctx.getElapsedTime();
|
|
112
|
-
}
|
|
113
|
-
dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
|
|
114
|
-
dbc_step.hash = dbc_hash;
|
|
162
|
+
if (dbcUrl === 'postgresql://' || !dbc_hash) {
|
|
163
|
+
return original.apply(this, arguments);
|
|
164
|
+
}
|
|
115
165
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
DataTextAgent.ERROR.add(dbc_hash, dbc);
|
|
166
|
+
const asyncResource = new AsyncResource('pg-query');
|
|
167
|
+
const callbackResource = new AsyncResource('pg-callback');
|
|
119
168
|
|
|
120
|
-
|
|
121
|
-
ctx.
|
|
122
|
-
ctx.
|
|
123
|
-
|
|
124
|
-
|
|
169
|
+
return asyncResource.runInAsyncScope(() => {
|
|
170
|
+
ctx.start_time = Date.now();
|
|
171
|
+
ctx.elapsed = 0;
|
|
172
|
+
ctx.active_sqlhash = true;
|
|
173
|
+
AsyncSender.send_packet(PacketTypeEnum.TX_DB_CONN, ctx, [dbcUrl]);
|
|
125
174
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
ctx.footprint('PgSql Query Start');
|
|
175
|
+
var sql_start_time = Date.now();
|
|
176
|
+
ctx.footprint('PgSql Query Start');
|
|
177
|
+
ctx.sql_count++;
|
|
130
178
|
|
|
131
|
-
|
|
179
|
+
var sql = args.length > 0 ? args[0] : undefined;
|
|
180
|
+
var params = null;
|
|
181
|
+
var finalSql = '';
|
|
132
182
|
|
|
133
|
-
let sql = arguments[0];
|
|
134
|
-
let psql = null;
|
|
135
|
-
if (typeof sql === 'string' && sql.length > 0) {
|
|
136
183
|
try {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
184
|
+
if (sql !== null && typeof sql === 'object') {
|
|
185
|
+
finalSql = sql.text || sql.query || '';
|
|
186
|
+
params = sql.values || null;
|
|
187
|
+
} else if (typeof sql === 'string') {
|
|
188
|
+
finalSql = sql;
|
|
189
|
+
params = args.length > 1 && Array.isArray(args[1]) ? args[1] : null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (typeof finalSql === 'string' && finalSql.length > 0 && params && Array.isArray(params) && params.length > 0) {
|
|
193
|
+
params.forEach((param, index) => {
|
|
194
|
+
const placeholder = '$' + (index + 1);
|
|
195
|
+
const value = param === null ? 'NULL' :
|
|
196
|
+
typeof param === 'string' ? `'${param.replace(/'/g, "''")}'` :
|
|
197
|
+
String(param);
|
|
198
|
+
finalSql = finalSql.replace(new RegExp('\\' + placeholder + '\\b', 'g'), value);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
} catch (formatError) {
|
|
202
|
+
Logger.printError('WHATAP-205', 'Error formatting PostgreSQL query', formatError, false);
|
|
203
|
+
if (typeof sql === 'string') {
|
|
204
|
+
finalSql = sql;
|
|
205
|
+
} else if (sql && sql.text) {
|
|
206
|
+
finalSql = sql.text;
|
|
207
|
+
}
|
|
140
208
|
}
|
|
141
|
-
} else {
|
|
142
|
-
sql = '';
|
|
143
|
-
psql = escapeLiteral(sql);
|
|
144
|
-
}
|
|
145
209
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
var crc = {value : 0};
|
|
161
|
-
sql_step.p1 = toParamBytes(psql.param, crc);
|
|
162
|
-
if(params != undefined) {
|
|
163
|
-
const result = params.map((param) => {
|
|
164
|
-
if(typeof param === 'string'){
|
|
165
|
-
return `'${param}'`
|
|
166
|
-
}
|
|
167
|
-
return param
|
|
168
|
-
}).toString()
|
|
169
|
-
sql_step.p2 = toParamBytes(result, crc);
|
|
210
|
+
if (typeof finalSql !== 'string' || finalSql.length === 0) {
|
|
211
|
+
finalSql = '';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
var psql = null;
|
|
215
|
+
if (typeof finalSql === 'string' && finalSql.length > 0) {
|
|
216
|
+
try {
|
|
217
|
+
psql = escapeLiteral(finalSql);
|
|
218
|
+
Logger.print('WHATAP-202', 'Processing SQL: ' + finalSql.substring(0, 200), false);
|
|
219
|
+
} catch (e) {
|
|
220
|
+
Logger.printError('WHATAP-206', 'PgSqlObserver escapeliteral error', e, false);
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
psql = escapeLiteral(finalSql);
|
|
170
224
|
}
|
|
171
|
-
sql_step.pcrc = crc.value;
|
|
172
|
-
}
|
|
173
225
|
|
|
174
|
-
|
|
175
|
-
|
|
226
|
+
var sqlHash = psql ? psql.sql : 0;
|
|
227
|
+
ctx.active_sqlhash = sqlHash;
|
|
228
|
+
ctx.active_dbc = dbc_hash;
|
|
176
229
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
result_step.elapsed = 0;
|
|
183
|
-
result_step.fetch = res.rowCount;
|
|
184
|
-
result_step.sqlhash = psql.sql;
|
|
185
|
-
result_step.dbc = dbc_hash;
|
|
186
|
-
ctx.profile.push(result_step);
|
|
230
|
+
function queryCallback(err, results) {
|
|
231
|
+
var currentCtx = TraceContextManager.getCurrentContext();
|
|
232
|
+
if (currentCtx == null) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
187
235
|
|
|
188
|
-
|
|
236
|
+
TraceContextManager.resume(currentCtx.id);
|
|
189
237
|
|
|
190
|
-
|
|
191
|
-
|
|
238
|
+
var sql_elapsed = Date.now() - sql_start_time;
|
|
239
|
+
var resultCount = 0;
|
|
192
240
|
|
|
193
|
-
|
|
241
|
+
if (err) {
|
|
242
|
+
handleSqlError(currentCtx, err);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (!err && results) {
|
|
246
|
+
if (results.rowCount !== undefined) {
|
|
247
|
+
resultCount = results.rowCount;
|
|
248
|
+
} else if (results.rows && Array.isArray(results.rows)) {
|
|
249
|
+
resultCount = results.rows.length;
|
|
194
250
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
var errorStack = err.stack.split("\n");
|
|
203
|
-
if (errorStack.length > traceDepth) {
|
|
204
|
-
errorStack = errorStack.slice(0, traceDepth + 1);
|
|
205
|
-
}
|
|
206
|
-
ctx.error_message = errorStack.join("\n");
|
|
207
|
-
sql_step.error = ctx.error = StatError.addError('pgsql -' + err.code, err.message, ctx.service_hash, TextTypes.SQL, null);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
TraceSQL.isSlowSQL(currentCtx);
|
|
255
|
+
if (results && results.command && results.command === 'SELECT' && psql != null && psql.type === 'S') {
|
|
256
|
+
TraceSQL.isTooManyRecords(resultCount, currentCtx);
|
|
208
257
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
258
|
+
} catch (e) {
|
|
259
|
+
Logger.printError('WHATAP-207', 'Error in TraceSQL processing', e, false);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
currentCtx.elapsed = sql_elapsed;
|
|
263
|
+
currentCtx.active_sqlhash = false;
|
|
264
|
+
AsyncSender.send_packet(PacketTypeEnum.TX_SQL, currentCtx, [dbcUrl, finalSql, String(resultCount)]);
|
|
265
|
+
|
|
266
|
+
currentCtx.footprint('PgSql Query Done');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
var callbackWrapped = false;
|
|
270
|
+
var hasCallback = false;
|
|
271
|
+
|
|
272
|
+
for (var i = args.length - 1; i >= 0; i--) {
|
|
273
|
+
if (typeof args[i] === 'function') {
|
|
274
|
+
hasCallback = true;
|
|
275
|
+
var originalCallback = args[i];
|
|
276
|
+
args[i] = callbackResource.bind(function() {
|
|
277
|
+
var callbackArgs = Array.prototype.slice.call(arguments);
|
|
278
|
+
try {
|
|
279
|
+
queryCallback(callbackArgs[0], callbackArgs[1]);
|
|
280
|
+
} catch (e) {
|
|
281
|
+
Logger.printError('WHATAP-208', 'Error in callback hook', e, false);
|
|
219
282
|
}
|
|
220
|
-
|
|
221
|
-
|
|
283
|
+
if (originalCallback && typeof originalCallback === 'function') {
|
|
284
|
+
return originalCallback.apply(this, callbackArgs);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
callbackWrapped = true;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
const result = original.apply(this, args);
|
|
294
|
+
|
|
295
|
+
// Promise 기반 처리
|
|
296
|
+
if (!hasCallback && result && typeof result.then === 'function') {
|
|
297
|
+
return result.then(function(res) {
|
|
298
|
+
queryCallback(null, res);
|
|
299
|
+
return res;
|
|
300
|
+
}).catch(function(err) {
|
|
301
|
+
queryCallback(err, null);
|
|
302
|
+
throw err;
|
|
303
|
+
});
|
|
222
304
|
}
|
|
305
|
+
|
|
223
306
|
return result;
|
|
307
|
+
} catch (queryError) {
|
|
308
|
+
queryCallback(queryError, null);
|
|
309
|
+
throw queryError;
|
|
224
310
|
}
|
|
225
|
-
}
|
|
226
|
-
self._handleError(ctx, sql_step, err);
|
|
227
|
-
throw err;
|
|
228
|
-
}
|
|
311
|
+
});
|
|
229
312
|
};
|
|
230
|
-
}
|
|
313
|
+
};
|
|
231
314
|
};
|
|
232
315
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
316
|
+
var wrapPoolConnect = function(agent) {
|
|
317
|
+
return function(original) {
|
|
318
|
+
return function wrappedPoolConnect() {
|
|
319
|
+
var args = Array.prototype.slice.call(arguments);
|
|
320
|
+
var ctx = TraceContextManager.getCurrentContext();
|
|
236
321
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
322
|
+
if (ctx == null) {
|
|
323
|
+
return original.apply(this, args);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// AsyncResource로 connect 컨텍스트 유지
|
|
327
|
+
const connectionResource = new AsyncResource('pg-pool-connect');
|
|
328
|
+
return connectionResource.runInAsyncScope(() => {
|
|
329
|
+
ctx.start_time = Date.now();
|
|
330
|
+
|
|
331
|
+
// 콜백 래핑
|
|
332
|
+
for (var i = args.length - 1; i >= 0; i--) {
|
|
333
|
+
if (typeof args[i] === 'function') {
|
|
334
|
+
var originalCallback = args[i];
|
|
335
|
+
// AsyncResource로 콜백 바인딩
|
|
336
|
+
args[i] = connectionResource.bind(function() {
|
|
337
|
+
var callbackArgs = Array.prototype.slice.call(arguments);
|
|
338
|
+
var err = callbackArgs[0];
|
|
339
|
+
var client = callbackArgs[1];
|
|
340
|
+
|
|
341
|
+
TraceContextManager.resume(ctx.id);
|
|
342
|
+
ctx.elapsed = Date.now() - ctx.start_time;
|
|
343
|
+
|
|
344
|
+
if (!err && client && !client.__query_hook__) {
|
|
345
|
+
client.__query_hook__ = true;
|
|
346
|
+
hookConnectionMethods(client, agent);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return originalCallback.apply(this, callbackArgs);
|
|
350
|
+
});
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
var result = original.apply(this, args);
|
|
240
356
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
357
|
+
// Promise 기반 처리
|
|
358
|
+
if (result && typeof result.then === 'function' && args.every(arg => typeof arg !== 'function')) {
|
|
359
|
+
return result.then(connectionResource.bind(function(client) {
|
|
360
|
+
TraceContextManager.resume(ctx.id);
|
|
361
|
+
ctx.elapsed = Date.now() - ctx.start_time;
|
|
246
362
|
|
|
247
|
-
|
|
248
|
-
|
|
363
|
+
if (client && !client.__query_hook__) {
|
|
364
|
+
client.__query_hook__ = true;
|
|
365
|
+
hookConnectionMethods(client, agent);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return client;
|
|
369
|
+
}));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return result;
|
|
373
|
+
});
|
|
374
|
+
};
|
|
375
|
+
};
|
|
249
376
|
};
|
|
250
377
|
|
|
251
|
-
|
|
252
|
-
if(
|
|
253
|
-
return
|
|
378
|
+
PgSqlObserver.prototype.inject = function (mod, moduleName) {
|
|
379
|
+
if (mod.__whatap_observe__) {
|
|
380
|
+
return;
|
|
254
381
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
382
|
+
mod.__whatap_observe__ = true;
|
|
383
|
+
Logger.initPrint("PgSqlObserver");
|
|
384
|
+
|
|
385
|
+
if (conf.sql_enabled === false) {
|
|
386
|
+
return;
|
|
259
387
|
}
|
|
260
|
-
};
|
|
261
388
|
|
|
262
|
-
var
|
|
263
|
-
var nonLiteSql = new IntKeyMap(5000).setMax(5000);
|
|
264
|
-
var date = DateUtil.yyyymmdd();
|
|
389
|
+
var self = this;
|
|
265
390
|
|
|
266
|
-
|
|
267
|
-
if(
|
|
391
|
+
// Client 생성자 래핑
|
|
392
|
+
if (mod.Client && !mod.Client.__whatap_wrapped__) {
|
|
393
|
+
shimmer.wrap(mod, 'Client', function(original) {
|
|
394
|
+
return function wrappedClient() {
|
|
395
|
+
var args = Array.prototype.slice.call(arguments);
|
|
396
|
+
var ctx = TraceContextManager.getCurrentContext();
|
|
397
|
+
|
|
398
|
+
setupDbcInfo(args);
|
|
399
|
+
|
|
400
|
+
// if (ctx && !ctx.db_opening) {
|
|
401
|
+
// ctx.db_opening = true;
|
|
402
|
+
// ctx.footprint('PgSql Connecting Start');
|
|
403
|
+
// ctx.start_time = Date.now();
|
|
404
|
+
// }
|
|
405
|
+
|
|
406
|
+
var client = new (Function.prototype.bind.apply(original, [null].concat(args)));
|
|
407
|
+
|
|
408
|
+
hookConnectionMethods(client, self.agent);
|
|
409
|
+
|
|
410
|
+
if (client && client.connect && !client.connect.__whatap_wrapped__) {
|
|
411
|
+
shimmer.wrap(client, 'connect', function(original) {
|
|
412
|
+
return function wrappedConnect() {
|
|
413
|
+
var args = Array.prototype.slice.call(arguments);
|
|
414
|
+
var ctx = TraceContextManager.getCurrentContext();
|
|
415
|
+
|
|
416
|
+
if (ctx) {
|
|
417
|
+
ctx.start_time = Date.now();
|
|
418
|
+
ctx.footprint('PgSql Connecting Start');
|
|
419
|
+
ctx.db_opening = true;
|
|
420
|
+
|
|
421
|
+
// 콜백 래핑
|
|
422
|
+
for (var i = args.length - 1; i >= 0; i--) {
|
|
423
|
+
if (typeof args[i] === 'function') {
|
|
424
|
+
var originalCallback = args[i];
|
|
425
|
+
args[i] = function() {
|
|
426
|
+
var callbackArgs = Array.prototype.slice.call(arguments);
|
|
427
|
+
var err = callbackArgs[0];
|
|
428
|
+
|
|
429
|
+
if (err) {
|
|
430
|
+
handleSqlError(ctx, err);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
ctx.db_opening = false;
|
|
434
|
+
ctx.footprint('PgSql Connecting Done');
|
|
435
|
+
TraceContextManager.resume(ctx.id);
|
|
436
|
+
|
|
437
|
+
return originalCallback.apply(this, callbackArgs);
|
|
438
|
+
};
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
268
443
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
444
|
+
var result = original.apply(this, args);
|
|
445
|
+
|
|
446
|
+
// Promise 기반 처리
|
|
447
|
+
if (result && typeof result.then === 'function') {
|
|
448
|
+
return result.then(function() {
|
|
449
|
+
if (ctx) {
|
|
450
|
+
ctx.db_opening = false;
|
|
451
|
+
ctx.footprint('PgSql Connecting Done');
|
|
452
|
+
}
|
|
453
|
+
return result;
|
|
454
|
+
}).catch(function(err) {
|
|
455
|
+
if (ctx) {
|
|
456
|
+
ctx.db_opening = false;
|
|
457
|
+
ctx.footprint('PgSql Connecting Error');
|
|
458
|
+
handleSqlError(ctx, err);
|
|
459
|
+
}
|
|
460
|
+
throw err;
|
|
461
|
+
});
|
|
462
|
+
}
|
|
275
463
|
|
|
276
|
-
|
|
277
|
-
|
|
464
|
+
return result;
|
|
465
|
+
};
|
|
466
|
+
});
|
|
467
|
+
client.connect.__whatap_wrapped__ = true;
|
|
468
|
+
}
|
|
278
469
|
|
|
279
|
-
|
|
280
|
-
|
|
470
|
+
if (ctx) {
|
|
471
|
+
ctx.elapsed = Date.now() - ctx.start_time;
|
|
472
|
+
ctx.footprint('PgSql Client Created');
|
|
473
|
+
ctx.db_opening = false;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return client;
|
|
477
|
+
};
|
|
478
|
+
});
|
|
479
|
+
mod.Client.__whatap_wrapped__ = true;
|
|
281
480
|
}
|
|
282
|
-
psql = checkedSql.get(sqlHash);
|
|
283
481
|
|
|
284
|
-
if(
|
|
285
|
-
|
|
482
|
+
if (mod.Pool && !mod.Pool.__whatap_wrapped__) {
|
|
483
|
+
shimmer.wrap(mod, 'Pool', function(original) {
|
|
484
|
+
return function wrappedPool() {
|
|
485
|
+
var args = Array.prototype.slice.call(arguments);
|
|
486
|
+
setupDbcInfo(args);
|
|
487
|
+
|
|
488
|
+
var pool = new (Function.prototype.bind.apply(original, [null].concat(args)));
|
|
489
|
+
|
|
490
|
+
if (pool && !hookedInstances.has(pool)) {
|
|
491
|
+
hookedInstances.add(pool);
|
|
492
|
+
|
|
493
|
+
// Pool의 query 메서드 후킹
|
|
494
|
+
if (pool.query && !pool.query.__whatap_wrapped__) {
|
|
495
|
+
shimmer.wrap(pool, 'query', createQueryWrapper(self.agent, dbc));
|
|
496
|
+
pool.query.__whatap_wrapped__ = true;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Pool의 connect 메서드는 수동 연결 시에만 후킹
|
|
500
|
+
// pool.query() 내부에서 자동으로 연결을 가져올 때는 후킹하지 않음
|
|
501
|
+
if (pool.connect && !pool.connect.__whatap_wrapped__) {
|
|
502
|
+
shimmer.wrap(pool, 'connect', function(original) {
|
|
503
|
+
return function wrappedConnect() {
|
|
504
|
+
var args = Array.prototype.slice.call(arguments);
|
|
505
|
+
var result = original.apply(this, args);
|
|
506
|
+
|
|
507
|
+
// Promise 기반 처리 - 연결 객체에만 후킹 (query는 후킹하지 않음)
|
|
508
|
+
if (result && typeof result.then === 'function') {
|
|
509
|
+
return result.then(function(client) {
|
|
510
|
+
// client에는 query 후킹하지 않음 (pool.query()에서 이미 추적됨)
|
|
511
|
+
return client;
|
|
512
|
+
});
|
|
513
|
+
} else if (result && typeof result === 'object') {
|
|
514
|
+
// 동기적으로 반환된 client에도 query 후킹하지 않음
|
|
515
|
+
return result;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return result;
|
|
519
|
+
};
|
|
520
|
+
});
|
|
521
|
+
pool.connect.__whatap_wrapped__ = true;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return pool;
|
|
526
|
+
};
|
|
527
|
+
});
|
|
528
|
+
mod.Pool.__whatap_wrapped__ = true;
|
|
286
529
|
}
|
|
287
530
|
|
|
288
|
-
|
|
289
|
-
|
|
531
|
+
if (mod.Client && mod.Client.prototype && mod.Client.prototype.query && !mod.Client.prototype.query.__whatap_wrapped__) {
|
|
532
|
+
shimmer.wrap(mod.Client.prototype, 'query', function(original) {
|
|
533
|
+
return function wrappedQuery() {
|
|
534
|
+
var args = Array.prototype.slice.call(arguments);
|
|
535
|
+
var ctx = TraceContextManager.getCurrentContext();
|
|
290
536
|
|
|
291
|
-
|
|
292
|
-
|
|
537
|
+
if (ctx == null || args[0] == null) {
|
|
538
|
+
return original.apply(this, arguments);
|
|
539
|
+
}
|
|
293
540
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
541
|
+
if (dbc_hash === 0 && this.connectionParameters) {
|
|
542
|
+
var info = this.connectionParameters;
|
|
543
|
+
dbc = 'postgresql://';
|
|
544
|
+
dbc += (info.user || '') + '@';
|
|
545
|
+
dbc += (info.host || '') + '/';
|
|
546
|
+
dbc += info.database || '';
|
|
547
|
+
dbc_hash = HashUtil.hashFromString(dbc);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return createQueryWrapper(self.agent, dbc)(original).apply(this, args);
|
|
551
|
+
};
|
|
552
|
+
});
|
|
553
|
+
mod.Client.prototype.query.__whatap_wrapped__ = true;
|
|
300
554
|
}
|
|
301
|
-
|
|
302
|
-
|
|
555
|
+
|
|
556
|
+
if (mod.Pool && mod.Pool.prototype && mod.Pool.prototype.query && !mod.Pool.prototype.query.__whatap_wrapped__) {
|
|
557
|
+
shimmer.wrap(mod.Pool.prototype, 'query', createQueryWrapper(self.agent, dbc));
|
|
558
|
+
mod.Pool.prototype.query.__whatap_wrapped__ = true;
|
|
559
|
+
}
|
|
560
|
+
};
|
|
303
561
|
|
|
304
562
|
exports.PgSqlObserver = PgSqlObserver;
|