whatap 0.5.26 → 1.0.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/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 +882 -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 -10
- 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
|
@@ -6,24 +6,17 @@
|
|
|
6
6
|
|
|
7
7
|
var TraceContextManager = require('../trace/trace-context-manager'),
|
|
8
8
|
ParsedSql = require('../trace/parsed-sql'),
|
|
9
|
-
SqlStepX = require('../step/sql-stepx'),
|
|
10
|
-
DBCStep = require('../step/dbc-step'),
|
|
11
|
-
ResultSetStep = require('../step/resultset-step'),
|
|
12
|
-
DataTextAgent = require('../data/datatext-agent'),
|
|
13
|
-
StatSql = require('../stat/stat-sql'),
|
|
14
|
-
MeterSql = require('../counter/meter/meter-sql'),
|
|
15
9
|
conf = require('../conf/configure'),
|
|
16
10
|
IntKeyMap = require('../util/intkey-map'),
|
|
17
11
|
EscapeLiteralSQL = require('../util/escape-literal-sql'),
|
|
18
12
|
HashUtil = require('../util/hashutil'),
|
|
19
|
-
StatError = require('../stat/stat-error'),
|
|
20
|
-
TextTypes = require('../lang/text-types'),
|
|
21
|
-
ParamSecurity = require('../util/paramsecurity'),
|
|
22
13
|
Logger = require('../logger'),
|
|
23
14
|
DateUtil = require('../util/dateutil'),
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
shimmer = require('../core/shimmer')
|
|
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
|
+
const { AsyncResource } = require('async_hooks');
|
|
27
20
|
|
|
28
21
|
var Mysql2Observer = function (agent) {
|
|
29
22
|
this.agent = agent;
|
|
@@ -33,12 +26,25 @@ var Mysql2Observer = function (agent) {
|
|
|
33
26
|
var dbc_hash = 0;
|
|
34
27
|
var dbc = 'mysql://';
|
|
35
28
|
|
|
36
|
-
//
|
|
37
|
-
var globalHookedFunctions = new WeakSet();
|
|
38
|
-
// 후킹된 객체 인스턴스들을 추적 (중복 방지 강화)
|
|
29
|
+
// 후킹된 객체 인스턴스들을 추적 (중복 방지)
|
|
39
30
|
var hookedInstances = new WeakSet();
|
|
40
31
|
|
|
41
|
-
|
|
32
|
+
// formatSqlWithParams 함수 개선 - mod를 직접 받아서 사용
|
|
33
|
+
function formatSqlWithParams(sql, params, mod) {
|
|
34
|
+
try {
|
|
35
|
+
if (mod && mod.format && typeof mod.format === 'function') {
|
|
36
|
+
return mod.format(sql, params);
|
|
37
|
+
}
|
|
38
|
+
// fallback: 동적으로 require (성능상 권장하지 않음)
|
|
39
|
+
var mysql = require('mysql2');
|
|
40
|
+
return mysql.format(sql, params);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
return sql;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 쿼리 래핑 함수 (AsyncResource.bind 적용)
|
|
47
|
+
var createQueryWrapper = function(isPromise = false, dbcUrl, mod) {
|
|
42
48
|
return function(original) {
|
|
43
49
|
return function wrappedQuery() {
|
|
44
50
|
var args = Array.prototype.slice.call(arguments);
|
|
@@ -46,12 +52,15 @@ var createQueryWrapper = function(isPromise = false) {
|
|
|
46
52
|
|
|
47
53
|
// ctx가 없으면 추적하지 않고 원본 함수만 실행
|
|
48
54
|
if (ctx == null || args[0] == null) {
|
|
49
|
-
return original.apply(this,
|
|
55
|
+
return original.apply(this, arguments);
|
|
50
56
|
}
|
|
51
57
|
if (args[0].sql == null && typeof args[0] != 'string') {
|
|
52
|
-
return original.apply(this,
|
|
58
|
+
return original.apply(this, arguments);
|
|
53
59
|
}
|
|
54
60
|
|
|
61
|
+
// AsyncResource 생성
|
|
62
|
+
var asyncResource = new AsyncResource('mysql2-query');
|
|
63
|
+
|
|
55
64
|
// 동시 쿼리 처리를 위한 고유 ID 생성
|
|
56
65
|
var queryId = Date.now() + '-' + Math.random().toString(36).substr(2, 9);
|
|
57
66
|
|
|
@@ -60,181 +69,123 @@ var createQueryWrapper = function(isPromise = false) {
|
|
|
60
69
|
ctx._processing_queries = new Set();
|
|
61
70
|
}
|
|
62
71
|
|
|
63
|
-
//
|
|
64
|
-
var
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
// SQL과 파라미터 추출
|
|
73
|
+
var sql = args.length > 0 ? args[0] : undefined;
|
|
74
|
+
var params = args.length > 1 && Array.isArray(args[1]) ? args[1] : undefined;
|
|
75
|
+
|
|
76
|
+
if (typeof sql !== 'string') {
|
|
77
|
+
sql = args[0].sql || undefined;
|
|
78
|
+
if (args[0].values && Array.isArray(args[0].values)) {
|
|
79
|
+
params = args[0].values;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 최종 SQL 생성 (파라미터 바인딩) - mod 직접 전달
|
|
84
|
+
var finalSql = sql;
|
|
85
|
+
if (typeof sql === 'string' && sql.length > 0 && params && params.length > 0) {
|
|
86
|
+
try {
|
|
87
|
+
finalSql = formatSqlWithParams(sql, params, mod);
|
|
88
|
+
} catch (e) {
|
|
89
|
+
Logger.printError('WHATAP-SQL-BINDING', 'Error binding parameters', e, false);
|
|
90
|
+
finalSql = sql; // 바인딩 실패 시 원본 사용
|
|
91
|
+
}
|
|
67
92
|
}
|
|
68
93
|
|
|
69
|
-
|
|
70
|
-
|
|
94
|
+
// 같은 최종 SQL이 동시에 처리되고 있는지 확인
|
|
95
|
+
if (finalSql && ctx._processing_queries.has(finalSql)) {
|
|
96
|
+
return original.apply(this, args);
|
|
71
97
|
}
|
|
72
98
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
DataTextAgent.ERROR.add(dbc_hash, dbc);
|
|
99
|
+
if (finalSql) {
|
|
100
|
+
ctx._processing_queries.add(finalSql);
|
|
101
|
+
}
|
|
77
102
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
ctx.profile.push(dbc_step);
|
|
103
|
+
// HTTP Observer 패턴을 따라 시작 시간 설정
|
|
104
|
+
ctx.start_time = Date.now();
|
|
81
105
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
ctx.profile.push(sql_step);
|
|
106
|
+
// DB 연결 패킷 전송 (elapsed = 0으로 설정)
|
|
107
|
+
ctx.elapsed = 0;
|
|
108
|
+
AsyncSender.send_packet(PacketTypeEnum.TX_DB_CONN, ctx, [dbcUrl]);
|
|
86
109
|
|
|
110
|
+
// SQL 시작 시간 기록
|
|
111
|
+
var sql_start_time = Date.now();
|
|
87
112
|
ctx.footprint('MySql2 Query Start [' + queryId + ']');
|
|
88
113
|
ctx.sql_count++;
|
|
89
114
|
|
|
90
|
-
var
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (typeof sql !== 'string') {
|
|
94
|
-
sql = args[0].sql || undefined;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (typeof sql === 'string' && sql.length > 0) {
|
|
115
|
+
var psql = null;
|
|
116
|
+
if (typeof finalSql === 'string' && finalSql.length > 0) {
|
|
98
117
|
try {
|
|
99
|
-
psql = escapeLiteral(
|
|
100
|
-
Logger.print('WHATAP-SQL-DEBUG', 'Processing SQL [' + queryId + ']: ' +
|
|
118
|
+
psql = escapeLiteral(finalSql);
|
|
119
|
+
Logger.print('WHATAP-SQL-DEBUG', 'Processing SQL [' + queryId + ']: ' + finalSql.substring(0, 200), false);
|
|
101
120
|
} catch (e) {
|
|
102
|
-
Logger.printError('WHATAP-
|
|
121
|
+
Logger.printError('WHATAP-225', 'Mysql2Observer escapeliteral error', e, false);
|
|
103
122
|
}
|
|
104
123
|
} else {
|
|
105
|
-
|
|
106
|
-
psql = escapeLiteral(
|
|
124
|
+
finalSql = '';
|
|
125
|
+
psql = escapeLiteral(finalSql);
|
|
107
126
|
}
|
|
108
127
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
sql_step.dbc = dbc_hash;
|
|
128
|
+
var sqlHash = psql ? psql.sql : 0;
|
|
129
|
+
ctx.active_sqlhash = true;
|
|
130
|
+
ctx.active_dbc = dbc_hash;
|
|
113
131
|
|
|
114
|
-
|
|
115
|
-
|
|
132
|
+
// AsyncResource로 바인딩된 쿼리 콜백 생성
|
|
133
|
+
function createBoundQueryCallback() {
|
|
134
|
+
function queryCallback(err, results, fields) {
|
|
135
|
+
var currentCtx = TraceContextManager.getCurrentContext();
|
|
116
136
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
sqlhash: sql_step.hash,
|
|
123
|
-
dbc: sql_step.dbc,
|
|
124
|
-
queryId: queryId
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
// 백워드 호환성을 위해 마지막 쿼리 정보 유지
|
|
128
|
-
ctx.active_sqlhash = sql_step.hash;
|
|
129
|
-
ctx.active_dbc = sql_step.dbc;
|
|
130
|
-
|
|
131
|
-
if (conf.profile_sql_param_enabled) {
|
|
132
|
-
var params = args.length > 1 && Array.isArray(args[1]) ? args[1] : undefined;
|
|
133
|
-
sql_step.setTrue(1);
|
|
134
|
-
var crc = {value: 0};
|
|
135
|
-
sql_step.p1 = toParamBytes(psql.param, crc);
|
|
136
|
-
if (params != undefined) {
|
|
137
|
-
const result = params.map((param) => {
|
|
138
|
-
if (typeof param === 'string') {
|
|
139
|
-
return `'${param}'`
|
|
140
|
-
}
|
|
141
|
-
return param
|
|
142
|
-
}).toString()
|
|
143
|
-
sql_step.p2 = toParamBytes(result, crc);
|
|
144
|
-
}
|
|
145
|
-
sql_step.pcrc = crc.value;
|
|
146
|
-
}
|
|
137
|
+
// bind된 함수에서도 context가 없는 경우 복원
|
|
138
|
+
if (currentCtx == null) {
|
|
139
|
+
TraceContextManager.resume(ctx.id);
|
|
140
|
+
currentCtx = ctx;
|
|
141
|
+
}
|
|
147
142
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
143
|
+
if (currentCtx == null) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
153
146
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
147
|
+
// 처리 완료된 쿼리를 목록에서 제거
|
|
148
|
+
if (finalSql && currentCtx._processing_queries) {
|
|
149
|
+
currentCtx._processing_queries.delete(finalSql);
|
|
150
|
+
}
|
|
158
151
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
ctx.active_queries = ctx.active_queries.filter(q => q.queryId !== queryId);
|
|
162
|
-
}
|
|
152
|
+
var sql_elapsed = Date.now() - sql_start_time;
|
|
153
|
+
var resultCount = 0;
|
|
163
154
|
|
|
164
|
-
|
|
155
|
+
if (err) {
|
|
156
|
+
handleSqlError(currentCtx, err, sqlHash);
|
|
157
|
+
}
|
|
165
158
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
ctx.error_message = errorStack.join("\n");
|
|
176
|
-
sql_step.error = ctx.error = StatError.addError('mysql2-' + (args[0].code || 'UNKNOWN'),
|
|
177
|
-
args[0].sqlMessage || args[0].message, ctx.service_hash, TextTypes.SQL, sql_step.hash);
|
|
159
|
+
// 결과 개수 계산
|
|
160
|
+
if (!err && results) {
|
|
161
|
+
if (Array.isArray(results)) {
|
|
162
|
+
resultCount = results.length;
|
|
163
|
+
} else if (results.affectedRows !== undefined) {
|
|
164
|
+
resultCount = results.affectedRows;
|
|
165
|
+
} else if (results.changedRows !== undefined) {
|
|
166
|
+
resultCount = results.changedRows;
|
|
178
167
|
}
|
|
168
|
+
}
|
|
179
169
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
args[0].code.indexOf(conf.trace_ignore_err_cls_contains) < 0) {
|
|
184
|
-
shouldAddError = true;
|
|
185
|
-
} else if (conf._is_trace_ignore_err_msg_contains === true && args[0].message &&
|
|
186
|
-
args[0].message.indexOf(conf.trace_ignore_err_msg_contains) < 0) {
|
|
187
|
-
shouldAddError = true;
|
|
188
|
-
} else if (conf._is_trace_ignore_err_cls_contains === false && conf._is_trace_ignore_err_msg_contains === false) {
|
|
189
|
-
shouldAddError = true;
|
|
190
|
-
}
|
|
170
|
+
// TraceSQL 처리
|
|
171
|
+
try {
|
|
172
|
+
TraceSQL.isSlowSQL(currentCtx);
|
|
191
173
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (ctx.error.isZero()) {
|
|
196
|
-
ctx.error = sql_step.error;
|
|
197
|
-
}
|
|
174
|
+
// ResultSet 처리 (SELECT 쿼리)
|
|
175
|
+
if (!err && results && psql && psql.type === 'S') {
|
|
176
|
+
TraceSQL.isTooManyRecords(resultCount, currentCtx);
|
|
198
177
|
}
|
|
199
178
|
} catch (e) {
|
|
200
|
-
Logger.printError('WHATAP-
|
|
179
|
+
Logger.printError('WHATAP-TRACESQL', 'Error in TraceSQL processing', e, false);
|
|
201
180
|
}
|
|
202
|
-
}
|
|
203
181
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
// 핵심 통계 수집 로직
|
|
208
|
-
TraceSQL.isSlowSQL(ctx);
|
|
209
|
-
ctx.footprint('MySql2 Query Done [' + queryId + ']');
|
|
210
|
-
|
|
211
|
-
MeterSql.add(dbc_hash, sql_step.elapsed, args[0] != null);
|
|
212
|
-
StatSql.addSqlTime(ctx.service_hash, sql_step.dbc,
|
|
213
|
-
sql_step.hash, sql_step.elapsed, args[0] != null, 0);
|
|
214
|
-
|
|
215
|
-
// 결과 집합 처리 (SELECT 쿼리) - args[1]이 결과 배열
|
|
216
|
-
if (Array.isArray(args[1]) && psql != null && psql.type === 'S') {
|
|
217
|
-
var result_step = new ResultSetStep();
|
|
218
|
-
result_step.start_time = ctx.getElapsedTime();
|
|
219
|
-
result_step.elapsed = 0;
|
|
220
|
-
result_step.fetch = args[1].length;
|
|
221
|
-
result_step.sqlhash = psql.sql;
|
|
222
|
-
result_step.dbc = dbc_hash;
|
|
223
|
-
ctx.profile.push(result_step);
|
|
224
|
-
|
|
225
|
-
ctx.rs_count = ctx.rs_count ? ctx.rs_count + args[1].length : args[1].length;
|
|
226
|
-
ctx.rs_time = ctx.rs_time ? ctx.rs_time + sql_step.elapsed : sql_step.elapsed;
|
|
227
|
-
|
|
228
|
-
// 핵심 통계 수집 로직
|
|
229
|
-
MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
|
|
230
|
-
StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
|
|
231
|
-
TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
|
|
182
|
+
currentCtx.elapsed = sql_elapsed;
|
|
183
|
+
currentCtx.active_sqlhash = false;
|
|
184
|
+
AsyncSender.send_packet(PacketTypeEnum.TX_SQL, currentCtx, [dbcUrl, finalSql, String(resultCount)]);
|
|
232
185
|
}
|
|
233
186
|
|
|
234
|
-
//
|
|
235
|
-
|
|
236
|
-
sql_step.updated = args[1].affectedRows || 0;
|
|
237
|
-
}
|
|
187
|
+
// AsyncResource로 바인딩하여 안전한 context 보장
|
|
188
|
+
return asyncResource.bind(queryCallback);
|
|
238
189
|
}
|
|
239
190
|
|
|
240
191
|
// Promise 기반 처리 (mysql2/promise)
|
|
@@ -243,119 +194,142 @@ var createQueryWrapper = function(isPromise = false) {
|
|
|
243
194
|
|
|
244
195
|
// Promise 체인 처리
|
|
245
196
|
if (result && typeof result.then === 'function') {
|
|
197
|
+
var boundCallback = createBoundQueryCallback();
|
|
198
|
+
|
|
246
199
|
return result.then(function(queryResult) {
|
|
247
200
|
// MySQL2 Promise 결과는 [rows, fields] 형태
|
|
248
201
|
var rows = Array.isArray(queryResult) ? queryResult[0] : queryResult;
|
|
249
202
|
var fields = Array.isArray(queryResult) ? queryResult[1] : null;
|
|
250
203
|
|
|
251
|
-
//
|
|
252
|
-
|
|
204
|
+
// 바인딩된 콜백 호출
|
|
205
|
+
boundCallback(null, rows, fields);
|
|
253
206
|
return queryResult;
|
|
254
207
|
}).catch(function(error) {
|
|
255
|
-
// 에러 시
|
|
256
|
-
|
|
208
|
+
// 에러 시 바인딩된 콜백 호출
|
|
209
|
+
boundCallback(error, null, null);
|
|
257
210
|
throw error;
|
|
258
211
|
});
|
|
259
212
|
}
|
|
260
213
|
return result;
|
|
261
214
|
}
|
|
262
215
|
|
|
263
|
-
// 콜백 기반 처리 (일반 mysql2)
|
|
264
|
-
var
|
|
216
|
+
// 콜백 기반 처리 (일반 mysql2)
|
|
217
|
+
var callbackWrapped = false;
|
|
218
|
+
var boundCallback = createBoundQueryCallback();
|
|
265
219
|
|
|
266
|
-
// 원본 MysqlObserver의 functionHook 방식을 모방
|
|
267
220
|
// 마지막 인자에서 함수를 찾아서 후킹
|
|
268
|
-
|
|
221
|
+
for (var i = args.length - 1; i >= 0; i--) {
|
|
222
|
+
if (typeof args[i] === 'function') {
|
|
223
|
+
var originalCallback = args[i];
|
|
224
|
+
args[i] = function() {
|
|
225
|
+
var callbackArgs = Array.prototype.slice.call(arguments);
|
|
226
|
+
try {
|
|
227
|
+
// 바인딩된 콜백 먼저 실행
|
|
228
|
+
boundCallback(callbackArgs[0], callbackArgs[1], callbackArgs[2]);
|
|
229
|
+
} catch (e) {
|
|
230
|
+
Logger.printError('WHATAP-CALLBACK', 'Error in callback hook', e, false);
|
|
231
|
+
}
|
|
232
|
+
// 원본 콜백 실행
|
|
233
|
+
if (originalCallback && typeof originalCallback === 'function') {
|
|
234
|
+
return originalCallback.apply(this, callbackArgs);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
callbackWrapped = true;
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
269
241
|
|
|
270
|
-
|
|
271
|
-
if (lastCallback === false && args.length > 0 && args[0]._callback) {
|
|
242
|
+
if (!callbackWrapped && args.length > 0 && args[0]._callback) {
|
|
272
243
|
try {
|
|
273
|
-
// _callback 속성이 있는 경우 직접 후킹
|
|
274
244
|
var originalCallback = args[0]._callback;
|
|
275
245
|
args[0]._callback = function() {
|
|
276
246
|
var callbackArgs = Array.prototype.slice.call(arguments);
|
|
277
247
|
try {
|
|
278
|
-
|
|
248
|
+
boundCallback(callbackArgs[0], callbackArgs[1], callbackArgs[2]);
|
|
279
249
|
} catch (e) {
|
|
280
250
|
Logger.printError('WHATAP-CALLBACK', 'Error in _callback hook', e, false);
|
|
281
251
|
}
|
|
282
|
-
// 원본 콜백 실행
|
|
283
252
|
if (originalCallback && typeof originalCallback === 'function') {
|
|
284
253
|
return originalCallback.apply(this, callbackArgs);
|
|
285
254
|
}
|
|
286
255
|
};
|
|
287
|
-
|
|
256
|
+
callbackWrapped = true;
|
|
288
257
|
} catch (e) {
|
|
289
258
|
Logger.printError('WHATAP-CALLBACK-HOOK', 'Error hooking _callback', e, false);
|
|
290
259
|
}
|
|
291
260
|
}
|
|
292
261
|
|
|
293
|
-
// 수정된 args로 원본 함수 실행
|
|
294
262
|
try {
|
|
295
263
|
return original.apply(this, args);
|
|
296
264
|
} catch (queryError) {
|
|
297
|
-
|
|
298
|
-
queryCallback(null, [queryError, null]);
|
|
265
|
+
boundCallback(queryError, null, null);
|
|
299
266
|
throw queryError;
|
|
300
267
|
}
|
|
301
268
|
};
|
|
302
269
|
};
|
|
303
270
|
};
|
|
304
271
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
* 마지막 함수 인자를 찾아서 콜백으로 후킹
|
|
308
|
-
*/
|
|
309
|
-
function hookLastFunction(args, callback) {
|
|
310
|
-
if (!args || args.length === 0) {
|
|
311
|
-
return false;
|
|
312
|
-
}
|
|
272
|
+
function handleSqlError(ctx, err, sqlHash) {
|
|
273
|
+
if (!err) return;
|
|
313
274
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
275
|
+
try {
|
|
276
|
+
var errorClass = err.code || err.name || err.constructor?.name || 'MySQLError';
|
|
277
|
+
var errorMessage = err.message || err.sqlMessage || 'mysql2 error';
|
|
278
|
+
var errorStack = '';
|
|
279
|
+
|
|
280
|
+
if (conf.trace_sql_error_stack && conf.trace_sql_error_depth && err.stack) {
|
|
281
|
+
var traceDepth = conf.trace_sql_error_depth;
|
|
282
|
+
var stackLines = err.stack.split("\n");
|
|
283
|
+
if (stackLines.length > traceDepth) {
|
|
284
|
+
stackLines = stackLines.slice(0, traceDepth + 1);
|
|
285
|
+
}
|
|
286
|
+
errorStack = stackLines.join("\n");
|
|
287
|
+
ctx.error_message = errorStack;
|
|
288
|
+
}
|
|
322
289
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
290
|
+
var shouldAddError = false;
|
|
291
|
+
if (conf._is_trace_ignore_err_cls_contains === true && errorClass &&
|
|
292
|
+
errorClass.indexOf(conf.trace_ignore_err_cls_contains) < 0) {
|
|
293
|
+
shouldAddError = true;
|
|
294
|
+
} else if (conf._is_trace_ignore_err_msg_contains === true && errorMessage &&
|
|
295
|
+
errorMessage.indexOf(conf.trace_ignore_err_msg_contains) < 0) {
|
|
296
|
+
shouldAddError = true;
|
|
297
|
+
} else if (conf._is_trace_ignore_err_cls_contains === false &&
|
|
298
|
+
conf._is_trace_ignore_err_msg_contains === false) {
|
|
299
|
+
shouldAddError = true;
|
|
300
|
+
}
|
|
329
301
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
throw e;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
};
|
|
302
|
+
if (shouldAddError) {
|
|
303
|
+
if(!ctx.error) ctx.error = 1;
|
|
304
|
+
ctx.status = 500;
|
|
305
|
+
ctx.error_message = errorMessage;
|
|
306
|
+
ctx.errClass = errorClass;
|
|
307
|
+
ctx.errMessage = errorMessage;
|
|
340
308
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
309
|
+
var errors = [errorClass];
|
|
310
|
+
if (errorMessage) {
|
|
311
|
+
errors.push(errorMessage);
|
|
312
|
+
}
|
|
313
|
+
if (errorStack || err.stack) {
|
|
314
|
+
errors.push(errorStack || err.stack);
|
|
315
|
+
}
|
|
316
|
+
AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
|
|
344
317
|
}
|
|
318
|
+
} catch (e) {
|
|
319
|
+
Logger.printError('WHATAP-226', 'Error handling MySQL2 error', e, false);
|
|
345
320
|
}
|
|
346
|
-
|
|
347
|
-
return false;
|
|
348
321
|
}
|
|
349
322
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
*/
|
|
353
|
-
var createConnectionWrapper = function(isPromise = false) {
|
|
323
|
+
// 연결 래핑 함수 (AsyncResource 적용)
|
|
324
|
+
var createConnectionWrapper = function(isPromise = false, mod) {
|
|
354
325
|
return function(original) {
|
|
355
326
|
return function wrappedCreateConnection() {
|
|
356
327
|
var args = Array.prototype.slice.call(arguments);
|
|
357
328
|
var ctx = TraceContextManager.getCurrentContext();
|
|
358
329
|
|
|
330
|
+
// AsyncResource 생성
|
|
331
|
+
var asyncResource = ctx ? new AsyncResource('mysql2-connection') : null;
|
|
332
|
+
|
|
359
333
|
// DB 연결 정보 구성
|
|
360
334
|
if (dbc_hash === 0 && args.length > 0) {
|
|
361
335
|
var info = (args[0] || {});
|
|
@@ -366,18 +340,12 @@ var createConnectionWrapper = function(isPromise = false) {
|
|
|
366
340
|
dbc += '/';
|
|
367
341
|
dbc += info.database || '';
|
|
368
342
|
dbc_hash = HashUtil.hashFromString(dbc);
|
|
369
|
-
DataTextAgent.DBC.add(dbc_hash, dbc);
|
|
370
|
-
DataTextAgent.METHOD.add(dbc_hash, dbc);
|
|
371
|
-
DataTextAgent.ERROR.add(dbc_hash, dbc);
|
|
372
343
|
}
|
|
373
344
|
|
|
374
|
-
|
|
375
|
-
if (ctx && !ctx.db_opening) {
|
|
345
|
+
if (ctx) {
|
|
376
346
|
ctx.db_opening = true;
|
|
377
347
|
ctx.footprint('MySql2 Connecting Start');
|
|
378
|
-
|
|
379
|
-
dbc_step.start_time = ctx.getElapsedTime();
|
|
380
|
-
dbc_step.hash = dbc_hash;
|
|
348
|
+
ctx.start_time = Date.now();
|
|
381
349
|
}
|
|
382
350
|
|
|
383
351
|
var result = original.apply(this, args);
|
|
@@ -390,29 +358,31 @@ var createConnectionWrapper = function(isPromise = false) {
|
|
|
390
358
|
|
|
391
359
|
// Connection의 query와 execute 메서드 후킹
|
|
392
360
|
if (connection.query && !connection.query.__whatap_wrapped__) {
|
|
393
|
-
shimmer.wrap(connection, 'query', createQueryWrapper(isPromise));
|
|
361
|
+
shimmer.wrap(connection, 'query', createQueryWrapper(isPromise, dbc, mod));
|
|
394
362
|
connection.query.__whatap_wrapped__ = true;
|
|
395
363
|
}
|
|
396
364
|
if (connection.execute && !connection.execute.__whatap_wrapped__) {
|
|
397
|
-
shimmer.wrap(connection, 'execute', createQueryWrapper(isPromise));
|
|
365
|
+
shimmer.wrap(connection, 'execute', createQueryWrapper(isPromise, dbc, mod));
|
|
398
366
|
connection.execute.__whatap_wrapped__ = true;
|
|
399
367
|
}
|
|
400
368
|
|
|
401
|
-
// 에러 델리게이트 후킹
|
|
402
|
-
if (connection._protocol && connection._protocol._delegateError
|
|
369
|
+
// 에러 델리게이트 후킹
|
|
370
|
+
if (connection._protocol && connection._protocol._delegateError &&
|
|
371
|
+
!connection._protocol._delegateError.__whatap_wrapped__) {
|
|
403
372
|
try {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
};
|
|
373
|
+
shimmer.wrap(connection._protocol, '_delegateError', function(original) {
|
|
374
|
+
return function wrappedDelegateError() {
|
|
375
|
+
var args = Array.prototype.slice.call(arguments);
|
|
376
|
+
var ctx = TraceContextManager.getCurrentContext();
|
|
377
|
+
|
|
378
|
+
if (ctx && args[0]) {
|
|
379
|
+
handleProtocolError(ctx, args[0]);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return original.apply(this, args);
|
|
383
|
+
};
|
|
384
|
+
});
|
|
385
|
+
connection._protocol._delegateError.__whatap_wrapped__ = true;
|
|
416
386
|
} catch (e) {
|
|
417
387
|
Logger.printError('WHATAP-PROTOCOL-HOOK', 'Error hooking _delegateError', e, false);
|
|
418
388
|
}
|
|
@@ -421,50 +391,30 @@ var createConnectionWrapper = function(isPromise = false) {
|
|
|
421
391
|
return connection;
|
|
422
392
|
}
|
|
423
393
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
var ctx = TraceContextManager.getCurrentContext();
|
|
427
|
-
if (ctx == null || !args[0]) {
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
var laststep = ctx.profile.getLastSteps(1);
|
|
432
|
-
if (laststep == null || laststep.length === 0) {
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
var step = laststep[0];
|
|
437
|
-
if (!args[0].fatal) {
|
|
438
|
-
MeterSql.add(step.dbc, step.elapsed, true);
|
|
439
|
-
StatSql.addSqlTime(ctx.service_hash, step.dbc, step.hash, step.elapsed, true, 0);
|
|
440
|
-
}
|
|
394
|
+
function handleProtocolError(ctx, error) {
|
|
395
|
+
if (!error) return;
|
|
441
396
|
|
|
442
397
|
try {
|
|
443
|
-
var
|
|
444
|
-
var errorMessage =
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
if (
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
if (conf._is_trace_ignore_err_cls_contains === true && errorCode.indexOf(conf.trace_ignore_err_cls_contains) < 0) {
|
|
455
|
-
shouldAddError = true;
|
|
456
|
-
} else if (conf._is_trace_ignore_err_msg_contains === true && errorMessage.indexOf(conf.trace_ignore_err_msg_contains) < 0) {
|
|
457
|
-
shouldAddError = true;
|
|
458
|
-
} else if (conf._is_trace_ignore_err_cls_contains === false && conf._is_trace_ignore_err_msg_contains === false) {
|
|
459
|
-
shouldAddError = true;
|
|
460
|
-
}
|
|
398
|
+
var errorClass = error.code || error.name || error.constructor?.name || 'MySQLError';
|
|
399
|
+
var errorMessage = error.message || 'mysql2 error';
|
|
400
|
+
|
|
401
|
+
var shouldAddError = false;
|
|
402
|
+
if (conf._is_trace_ignore_err_cls_contains === true && errorClass.indexOf(conf.trace_ignore_err_cls_contains) < 0) {
|
|
403
|
+
shouldAddError = true;
|
|
404
|
+
} else if (conf._is_trace_ignore_err_msg_contains === true && errorMessage.indexOf(conf.trace_ignore_err_msg_contains) < 0) {
|
|
405
|
+
shouldAddError = true;
|
|
406
|
+
} else if (conf._is_trace_ignore_err_cls_contains === false && conf._is_trace_ignore_err_msg_contains === false) {
|
|
407
|
+
shouldAddError = true;
|
|
408
|
+
}
|
|
461
409
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
410
|
+
if (shouldAddError) {
|
|
411
|
+
ctx.error = 1;
|
|
412
|
+
ctx.status = 500;
|
|
413
|
+
var errors = [errorClass];
|
|
414
|
+
if (errorMessage) {
|
|
415
|
+
errors.push(errorMessage);
|
|
467
416
|
}
|
|
417
|
+
AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
|
|
468
418
|
}
|
|
469
419
|
} catch (e) {
|
|
470
420
|
Logger.printError('WHATAP-PROTOCOL-ERROR-HANDLING', 'Error processing protocol error', e, false);
|
|
@@ -473,37 +423,41 @@ var createConnectionWrapper = function(isPromise = false) {
|
|
|
473
423
|
|
|
474
424
|
// Promise 기반 연결 처리
|
|
475
425
|
if (isPromise && result && typeof result.then === 'function') {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
ctx
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
426
|
+
if (asyncResource) {
|
|
427
|
+
return result.then(asyncResource.bind(function(connection) {
|
|
428
|
+
hookConnection(connection);
|
|
429
|
+
|
|
430
|
+
if (ctx) {
|
|
431
|
+
ctx.elapsed = Date.now() - ctx.start_time;
|
|
432
|
+
ctx.footprint('MySql2 Connecting Done');
|
|
433
|
+
ctx.db_opening = false;
|
|
434
|
+
}
|
|
435
|
+
return connection;
|
|
436
|
+
})).catch(asyncResource.bind(function(error) {
|
|
437
|
+
if (ctx) {
|
|
438
|
+
ctx.elapsed = Date.now() - ctx.start_time;
|
|
439
|
+
ctx.footprint('MySql2 Connecting Error');
|
|
440
|
+
ctx.db_opening = false;
|
|
441
|
+
handleSqlError(ctx, error, 0);
|
|
442
|
+
}
|
|
443
|
+
throw error;
|
|
444
|
+
}));
|
|
445
|
+
} else {
|
|
446
|
+
return result.then(function(connection) {
|
|
447
|
+
hookConnection(connection);
|
|
448
|
+
return connection;
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
} else {
|
|
452
|
+
// 동기/콜백 기반 연결 처리
|
|
498
453
|
if (result && typeof result === 'object') {
|
|
499
454
|
hookConnection(result);
|
|
500
455
|
}
|
|
501
456
|
|
|
502
|
-
if (ctx
|
|
457
|
+
if (ctx) {
|
|
458
|
+
ctx.elapsed = Date.now() - ctx.start_time;
|
|
503
459
|
ctx.footprint('MySql2 Connecting Done');
|
|
504
460
|
ctx.db_opening = false;
|
|
505
|
-
dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
|
|
506
|
-
ctx.profile.push(dbc_step);
|
|
507
461
|
}
|
|
508
462
|
}
|
|
509
463
|
|
|
@@ -512,10 +466,7 @@ var createConnectionWrapper = function(isPromise = false) {
|
|
|
512
466
|
};
|
|
513
467
|
};
|
|
514
468
|
|
|
515
|
-
|
|
516
|
-
* 개선된 Pool 래퍼 - 원본 패턴 적용
|
|
517
|
-
*/
|
|
518
|
-
var createPoolWrapper = function(isPromise = false) {
|
|
469
|
+
var createPoolWrapper = function(isPromise = false, mod) {
|
|
519
470
|
return function(original) {
|
|
520
471
|
return function wrappedCreatePool() {
|
|
521
472
|
var args = Array.prototype.slice.call(arguments);
|
|
@@ -530,28 +481,22 @@ var createPoolWrapper = function(isPromise = false) {
|
|
|
530
481
|
dbc += '/';
|
|
531
482
|
dbc += info.database || '';
|
|
532
483
|
dbc_hash = HashUtil.hashFromString(dbc);
|
|
533
|
-
DataTextAgent.DBC.add(dbc_hash, dbc);
|
|
534
|
-
DataTextAgent.METHOD.add(dbc_hash, dbc);
|
|
535
|
-
DataTextAgent.ERROR.add(dbc_hash, dbc);
|
|
536
484
|
}
|
|
537
485
|
|
|
538
486
|
var pool = original.apply(this, args);
|
|
539
487
|
|
|
540
|
-
// Pool 객체 후킹
|
|
541
488
|
if (pool && !hookedInstances.has(pool)) {
|
|
542
489
|
hookedInstances.add(pool);
|
|
543
490
|
|
|
544
|
-
// Pool의 query와 execute 메서드 후킹
|
|
545
491
|
if (pool.query && !pool.query.__whatap_wrapped__) {
|
|
546
|
-
shimmer.wrap(pool, 'query', createQueryWrapper(isPromise));
|
|
492
|
+
shimmer.wrap(pool, 'query', createQueryWrapper(isPromise, dbc, mod));
|
|
547
493
|
pool.query.__whatap_wrapped__ = true;
|
|
548
494
|
}
|
|
549
495
|
if (pool.execute && !pool.execute.__whatap_wrapped__) {
|
|
550
|
-
shimmer.wrap(pool, 'execute', createQueryWrapper(isPromise));
|
|
496
|
+
shimmer.wrap(pool, 'execute', createQueryWrapper(isPromise, dbc, mod));
|
|
551
497
|
pool.execute.__whatap_wrapped__ = true;
|
|
552
498
|
}
|
|
553
499
|
|
|
554
|
-
// getConnection 메서드 후킹 - 원본 패턴 적용
|
|
555
500
|
if (pool.getConnection && !pool.getConnection.__whatap_wrapped__) {
|
|
556
501
|
shimmer.wrap(pool, 'getConnection', function(original) {
|
|
557
502
|
return function wrappedGetConnection() {
|
|
@@ -562,42 +507,76 @@ var createPoolWrapper = function(isPromise = false) {
|
|
|
562
507
|
return original.apply(this, args);
|
|
563
508
|
}
|
|
564
509
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
510
|
+
var asyncResource = new AsyncResource('mysql2-get-connection');
|
|
511
|
+
ctx.start_time = Date.now();
|
|
512
|
+
|
|
513
|
+
for (var i = args.length - 1; i >= 0; i--) {
|
|
514
|
+
if (typeof args[i] === 'function') {
|
|
515
|
+
var originalCallback = args[i];
|
|
516
|
+
// AsyncResource로 바인딩된 콜백 생성
|
|
517
|
+
args[i] = asyncResource.bind(function() {
|
|
518
|
+
var callbackArgs = Array.prototype.slice.call(arguments);
|
|
519
|
+
var err = callbackArgs[0];
|
|
520
|
+
var conn = callbackArgs[1];
|
|
521
|
+
|
|
522
|
+
var currentCtx = TraceContextManager.getCurrentContext();
|
|
523
|
+
if (!currentCtx) {
|
|
524
|
+
TraceContextManager.resume(ctx.id);
|
|
525
|
+
currentCtx = ctx;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (currentCtx) {
|
|
529
|
+
currentCtx.elapsed = Date.now() - ctx.start_time;
|
|
530
|
+
if (!err && conn && !conn.__query_hook__) {
|
|
531
|
+
conn.__query_hook__ = true;
|
|
532
|
+
|
|
533
|
+
// Connection 객체의 메서드들 후킹
|
|
534
|
+
if (conn.query && !conn.query.__whatap_wrapped__) {
|
|
535
|
+
shimmer.wrap(conn, 'query', createQueryWrapper(isPromise, dbc, mod));
|
|
536
|
+
conn.query.__whatap_wrapped__ = true;
|
|
537
|
+
}
|
|
538
|
+
if (conn.execute && !conn.execute.__whatap_wrapped__) {
|
|
539
|
+
shimmer.wrap(conn, 'execute', createQueryWrapper(isPromise, dbc, mod));
|
|
540
|
+
conn.execute.__whatap_wrapped__ = true;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return originalCallback.apply(this, callbackArgs);
|
|
546
|
+
});
|
|
547
|
+
break;
|
|
572
548
|
}
|
|
549
|
+
}
|
|
573
550
|
|
|
574
|
-
|
|
575
|
-
if (conn && !conn.__query_hook__) {
|
|
576
|
-
conn.__query_hook__ = true;
|
|
551
|
+
var result = original.apply(this, args);
|
|
577
552
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
conn.execute.__whatap_wrapped__ = true;
|
|
553
|
+
// Promise 기반 처리
|
|
554
|
+
if (result && typeof result.then === 'function' && args.every(arg => typeof arg !== 'function')) {
|
|
555
|
+
return result.then(asyncResource.bind(function(connection) {
|
|
556
|
+
var currentCtx = TraceContextManager.getCurrentContext();
|
|
557
|
+
if (!currentCtx) {
|
|
558
|
+
TraceContextManager.resume(ctx.id);
|
|
559
|
+
currentCtx = ctx;
|
|
586
560
|
}
|
|
587
|
-
}
|
|
588
|
-
});
|
|
589
561
|
|
|
590
|
-
|
|
562
|
+
if (currentCtx) {
|
|
563
|
+
currentCtx.elapsed = Date.now() - ctx.start_time;
|
|
564
|
+
if (connection && !connection.__query_hook__) {
|
|
565
|
+
connection.__query_hook__ = true;
|
|
566
|
+
|
|
567
|
+
if (connection.query && !connection.query.__whatap_wrapped__) {
|
|
568
|
+
shimmer.wrap(connection, 'query', createQueryWrapper(isPromise, dbc, mod));
|
|
569
|
+
connection.query.__whatap_wrapped__ = true;
|
|
570
|
+
}
|
|
571
|
+
if (connection.execute && !connection.execute.__whatap_wrapped__) {
|
|
572
|
+
shimmer.wrap(connection, 'execute', createQueryWrapper(isPromise, dbc, mod));
|
|
573
|
+
connection.execute.__whatap_wrapped__ = true;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
591
577
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
if (this._allConnections != undefined) {
|
|
595
|
-
var all = this._allConnections.length;
|
|
596
|
-
var idle = this._freeConnections.length;
|
|
597
|
-
MeterSql.setConnection((all - idle), idle, dbc);
|
|
598
|
-
}
|
|
599
|
-
} catch (e) {
|
|
600
|
-
Logger.printError('WHATAP-CONNECTION-STATS', 'Error updating connection stats', e, false);
|
|
578
|
+
return connection;
|
|
579
|
+
}));
|
|
601
580
|
}
|
|
602
581
|
|
|
603
582
|
return result;
|
|
@@ -612,50 +591,6 @@ var createPoolWrapper = function(isPromise = false) {
|
|
|
612
591
|
};
|
|
613
592
|
};
|
|
614
593
|
|
|
615
|
-
var toParamBytes = function (p, crc) {
|
|
616
|
-
if (p == null || p.length === 0) {
|
|
617
|
-
return null;
|
|
618
|
-
}
|
|
619
|
-
try {
|
|
620
|
-
return ParamSecurity.encrypt(Buffer.from(p, 'utf8'), crc);
|
|
621
|
-
} catch (e) {
|
|
622
|
-
return null;
|
|
623
|
-
}
|
|
624
|
-
};
|
|
625
|
-
|
|
626
|
-
Mysql2Observer.prototype.inject = function (mod, moduleName) {
|
|
627
|
-
if (mod.__whatap_observe__) {
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
mod.__whatap_observe__ = true;
|
|
631
|
-
Logger.initPrint("Mysql2Observer");
|
|
632
|
-
|
|
633
|
-
if (conf.sql_enabled === false) {
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
// mysql2/promise인지 일반 mysql2인지 구분
|
|
638
|
-
var isPromise = moduleName && moduleName.includes('promise');
|
|
639
|
-
|
|
640
|
-
Logger.print('WHATAP-MYSQL2-INJECT', 'Injecting MySQL2 module: ' + moduleName + ', Promise mode: ' + isPromise, false);
|
|
641
|
-
|
|
642
|
-
// 전역 함수 추적으로 중복 후킹 방지
|
|
643
|
-
if (mod.createConnection && !globalHookedFunctions.has(mod.createConnection)) {
|
|
644
|
-
globalHookedFunctions.add(mod.createConnection);
|
|
645
|
-
shimmer.wrap(mod, 'createConnection', createConnectionWrapper(isPromise));
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
if (mod.createPool && !globalHookedFunctions.has(mod.createPool)) {
|
|
649
|
-
globalHookedFunctions.add(mod.createPool);
|
|
650
|
-
shimmer.wrap(mod, 'createPool', createPoolWrapper(isPromise));
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
if (mod.createPoolCluster && !globalHookedFunctions.has(mod.createPoolCluster)) {
|
|
654
|
-
globalHookedFunctions.add(mod.createPoolCluster);
|
|
655
|
-
shimmer.wrap(mod, 'createPoolCluster', createPoolWrapper(isPromise));
|
|
656
|
-
}
|
|
657
|
-
};
|
|
658
|
-
|
|
659
594
|
var checkedSql = new IntKeyMap(2000).setMax(2000);
|
|
660
595
|
var nonLiteSql = new IntKeyMap(5000).setMax(5000);
|
|
661
596
|
var date = DateUtil.yyyymmdd();
|
|
@@ -688,8 +623,6 @@ function escapeLiteral(sql) {
|
|
|
688
623
|
els.process();
|
|
689
624
|
|
|
690
625
|
var hash = HashUtil.hashFromString(els.getParsedSql());
|
|
691
|
-
DataTextAgent.SQL.add(hash, els.getParsedSql());
|
|
692
|
-
|
|
693
626
|
if (hash === sqlHash) {
|
|
694
627
|
psql = new ParsedSql(els.sqlType, hash, null);
|
|
695
628
|
nonLiteSql.put(sqlHash, psql);
|
|
@@ -700,4 +633,33 @@ function escapeLiteral(sql) {
|
|
|
700
633
|
return psql;
|
|
701
634
|
}
|
|
702
635
|
|
|
636
|
+
Mysql2Observer.prototype.inject = function (mod, moduleName) {
|
|
637
|
+
if (mod.__whatap_observe__) {
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
mod.__whatap_observe__ = true;
|
|
641
|
+
Logger.initPrint("Mysql2Observer");
|
|
642
|
+
|
|
643
|
+
if (conf.sql_enabled === false) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
var isPromise = moduleName && moduleName.includes('promise');
|
|
648
|
+
|
|
649
|
+
if (mod.createConnection && !mod.createConnection.__whatap_wrapped__) {
|
|
650
|
+
shimmer.wrap(mod, 'createConnection', createConnectionWrapper(isPromise, mod));
|
|
651
|
+
mod.createConnection.__whatap_wrapped__ = true;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if (mod.createPool && !mod.createPool.__whatap_wrapped__) {
|
|
655
|
+
shimmer.wrap(mod, 'createPool', createPoolWrapper(isPromise, mod));
|
|
656
|
+
mod.createPool.__whatap_wrapped__ = true;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (mod.createPoolCluster && !mod.createPoolCluster.__whatap_wrapped__) {
|
|
660
|
+
shimmer.wrap(mod, 'createPoolCluster', createPoolWrapper(isPromise, mod));
|
|
661
|
+
mod.createPoolCluster.__whatap_wrapped__ = true;
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
|
|
703
665
|
exports.Mysql2Observer = Mysql2Observer;
|