whatap 1.0.1 → 1.0.2
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 +32 -78
- package/lib/conf/conf-sys-mon.js +101 -0
- package/lib/conf/config-default.js +10 -3
- package/lib/conf/configure.js +369 -349
- package/lib/conf/license.js +1 -1
- package/lib/control/cmd-config.js +24 -0
- package/lib/control/control-handler.js +367 -0
- package/lib/control/packagectr-helper.js +34 -3
- package/lib/core/agent.js +176 -882
- package/lib/core/interceptor.js +6 -6
- package/lib/core/request-agent.js +27 -0
- package/lib/core/shimmer.js +82 -36
- package/lib/counter/counter-manager.js +79 -8
- package/lib/counter/meter/meter-activex.js +67 -0
- package/lib/counter/meter/meter-httpc.js +57 -0
- package/lib/counter/meter/meter-resource.js +9 -0
- package/lib/counter/meter/meter-service.js +168 -0
- package/lib/counter/meter/meter-socket.io.js +51 -0
- package/lib/counter/meter/meter-sql.js +71 -0
- package/lib/counter/meter/meter-users.js +58 -0
- package/lib/counter/meter.js +183 -0
- package/lib/counter/task/activetransaction.js +68 -17
- package/lib/counter/task/agentinfo.js +107 -0
- package/lib/{system → counter/task}/gc-action.js +27 -74
- package/lib/counter/task/gcstat.js +34 -0
- package/lib/counter/task/heapmem.js +25 -0
- package/lib/counter/task/httpc.js +76 -0
- package/lib/counter/task/metering-info.js +125 -0
- package/lib/counter/task/proc-cpu.js +29 -0
- package/lib/counter/task/realtimeuser.js +31 -0
- package/lib/counter/task/res/systemECSTask.js +39 -0
- package/lib/counter/task/res/systemKubeTask.js +53 -0
- package/lib/counter/task/res/util/awsEcsClientThread.js +218 -0
- package/lib/counter/task/res/util/linuxProcStatUtil.js +14 -0
- package/lib/counter/task/res-sys-cpu.js +62 -0
- package/lib/counter/task/service.js +202 -0
- package/lib/counter/task/socketio.js +30 -0
- package/lib/counter/task/sql.js +105 -0
- package/lib/counter/task/systemperf.js +43 -0
- package/lib/data/datapack-sender.js +289 -0
- package/lib/data/dataprofile-agent.js +162 -0
- package/lib/data/datatext-agent.js +135 -0
- package/lib/data/event-level.js +15 -0
- package/lib/data/test.js +49 -0
- package/lib/data/zipprofile.js +197 -0
- package/lib/env/constants.js +21 -0
- package/lib/error/error-handler.js +437 -0
- package/lib/io/data-inputx.js +13 -3
- package/lib/io/data-outputx.js +268 -206
- package/lib/kube/kube-client.js +144 -0
- package/lib/lang/text-types.js +58 -0
- package/lib/logger.js +6 -6
- package/lib/logsink/line-log-util.js +87 -0
- package/lib/logsink/line-log.js +12 -0
- package/lib/logsink/log-sender.js +78 -0
- package/lib/logsink/log-tracer.js +40 -0
- package/lib/logsink/sender-util.js +56 -0
- package/lib/logsink/zip/zip-send.js +177 -0
- package/lib/net/netflag.js +55 -0
- package/lib/net/receiver.js +66 -0
- package/lib/net/security-master.js +139 -20
- package/lib/net/sender.js +141 -0
- package/lib/net/tcp-return.js +18 -0
- package/lib/net/tcp-session.js +286 -0
- package/lib/net/tcpreq-client-proxy.js +70 -0
- package/lib/net/tcprequest-mgr.js +58 -0
- package/lib/observers/apollo-server-observer.js +33 -27
- package/lib/observers/cluster-observer.js +22 -0
- package/lib/observers/express-observer.js +215 -0
- package/lib/observers/file-observer.js +184 -0
- package/lib/observers/global-observer.js +155 -80
- package/lib/observers/grpc-observer.js +336 -0
- package/lib/observers/http-observer.js +666 -236
- package/lib/observers/maria-observer.js +204 -362
- package/lib/observers/memcached-observer.js +56 -0
- package/lib/observers/mongo-observer.js +317 -0
- package/lib/observers/mongodb-observer.js +169 -226
- package/lib/observers/mongoose-observer.js +518 -323
- package/lib/observers/mssql-observer.js +177 -418
- package/lib/observers/mysql-observer.js +342 -449
- package/lib/observers/mysql2-observer.js +396 -358
- package/lib/observers/net-observer.js +77 -0
- package/lib/observers/oracle-observer.js +559 -384
- package/lib/observers/pgsql-observer.js +231 -489
- package/lib/observers/prisma-observer.js +303 -92
- package/lib/observers/process-observer.js +79 -35
- package/lib/observers/promise-observer.js +31 -0
- package/lib/observers/redis-observer.js +166 -331
- package/lib/observers/schedule-observer.js +67 -0
- package/lib/observers/socket.io-observer.js +226 -187
- package/lib/observers/stream-observer.js +19 -0
- package/lib/observers/thrift-observer.js +197 -0
- package/lib/observers/websocket-observer.js +175 -301
- package/lib/pack/activestack-pack.js +55 -0
- package/lib/pack/apenum.js +8 -0
- package/lib/pack/counter-pack.js +3 -0
- package/lib/pack/errorsnap-pack.js +69 -0
- package/lib/pack/event-pack.js +54 -0
- package/lib/pack/hitmap-pack.js +63 -0
- package/lib/pack/hitmap-pack1.js +152 -0
- package/lib/pack/log-sink-pack.js +14 -52
- package/lib/pack/netstat.js +15 -0
- package/lib/pack/otype.js +7 -0
- package/lib/pack/profile-pack.js +49 -0
- package/lib/pack/realtimeuser-pack.js +41 -0
- package/lib/pack/stat-general-pack.js +96 -0
- package/lib/pack/staterror-pack.js +120 -0
- package/lib/pack/stathttpc-pack.js +66 -0
- package/lib/pack/stathttpc-rec.js +78 -0
- package/lib/pack/statremote-pack.js +46 -0
- package/lib/pack/statservice-pack.js +63 -0
- package/lib/pack/statservice-pack1.js +88 -0
- package/lib/pack/statservice-rec.js +292 -0
- package/lib/pack/statservice-rec_dep.js +151 -0
- package/lib/pack/statsql-pack.js +69 -0
- package/lib/pack/statsql-rec.js +100 -0
- package/lib/pack/statuseragent-pack.js +44 -0
- package/lib/pack/tagcount-pack.js +4 -4
- package/lib/pack/tagctr.js +15 -0
- package/lib/pack/text-pack.js +50 -0
- package/lib/pack/time-count.js +25 -0
- package/lib/pack/websocket.js +15 -0
- package/lib/pack/zip-pack.js +70 -0
- package/lib/pii/pii-item.js +31 -0
- package/lib/pii/pii-mask.js +174 -0
- package/lib/plugin/plugin-loadermanager.js +57 -0
- package/lib/plugin/plugin.js +75 -0
- package/lib/service/tx-record.js +332 -0
- package/lib/stat/stat-error.js +116 -0
- package/lib/stat/stat-httpc.js +98 -0
- package/lib/stat/stat-remote-ip.js +46 -0
- package/lib/stat/stat-remote-ipurl.js +88 -0
- package/lib/stat/stat-sql.js +113 -0
- package/lib/stat/stat-tranx.js +58 -0
- package/lib/stat/stat-tx-caller.js +160 -0
- package/lib/stat/stat-tx-domain.js +111 -0
- package/lib/stat/stat-tx-referer.js +112 -0
- package/lib/stat/stat-useragent.js +48 -0
- package/lib/stat/timingsender.js +76 -0
- package/lib/step/activestack-step.js +38 -0
- package/lib/step/dbc-step.js +36 -0
- package/lib/step/http-stepx.js +67 -0
- package/lib/step/message-step.js +40 -0
- package/lib/step/method-stepx.js +45 -0
- package/lib/step/resultset-step.js +40 -0
- package/lib/step/securemsg-step.js +44 -0
- package/lib/step/socket-step.js +46 -0
- package/lib/step/sql-stepx.js +68 -0
- package/lib/step/sqlxtype.js +16 -0
- package/lib/step/step.js +66 -0
- package/lib/step/stepenum.js +54 -0
- package/lib/topology/link.js +63 -0
- package/lib/topology/nodeinfo.js +123 -0
- package/lib/topology/status-detector.js +111 -0
- package/lib/trace/trace-context-manager.js +113 -25
- package/lib/trace/trace-context.js +21 -7
- package/lib/trace/trace-httpc.js +17 -11
- package/lib/trace/trace-sql.js +29 -21
- package/lib/util/anylist.js +103 -0
- package/lib/util/cardinality/hyperloglog.js +106 -0
- package/lib/util/cardinality/murmurhash.js +31 -0
- package/lib/util/cardinality/registerset.js +75 -0
- package/lib/util/errordata.js +21 -0
- package/lib/util/escape-literal-sql.js +5 -5
- package/lib/util/hashutil.js +18 -18
- package/lib/util/iputil_x.js +527 -0
- package/lib/util/keygen.js +0 -3
- package/lib/util/kube-util.js +73 -0
- package/lib/util/linkedset.js +1 -2
- package/lib/util/nodeutil.js +2 -1
- package/lib/util/paramsecurity.js +80 -0
- package/lib/util/pre-process.js +13 -0
- package/lib/util/process-seq.js +166 -0
- package/lib/util/property-util.js +36 -0
- package/lib/util/request-queue.js +70 -0
- package/lib/util/requestdouble-queue.js +72 -0
- package/lib/util/resourceprofile.js +157 -0
- package/lib/util/stop-watch.js +30 -0
- package/lib/util/system-util.js +10 -0
- package/lib/util/userid-util.js +57 -0
- package/lib/value/map-value.js +3 -2
- package/package.json +9 -4
- package/whatap.conf +1 -4
- 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 +0 -4
- package/lib/observers/ioredis-observer.js +0 -294
- package/lib/udp/async_sender.js +0 -119
- package/lib/udp/index.js +0 -17
- package/lib/udp/packet_enum.js +0 -52
- package/lib/udp/packet_queue.js +0 -69
- package/lib/udp/packet_type_enum.js +0 -33
- package/lib/udp/param_def.js +0 -72
- package/lib/udp/udp_session.js +0 -336
- package/lib/util/sql-util.js +0 -178
- package/lib/util/trace-helper.js +0 -91
- package/lib/util/transfer.js +0 -58
|
@@ -6,17 +6,24 @@
|
|
|
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'),
|
|
9
15
|
conf = require('../conf/configure'),
|
|
10
16
|
IntKeyMap = require('../util/intkey-map'),
|
|
11
17
|
EscapeLiteralSQL = require('../util/escape-literal-sql'),
|
|
12
18
|
HashUtil = require('../util/hashutil'),
|
|
19
|
+
StatError = require('../stat/stat-error'),
|
|
20
|
+
TextTypes = require('../lang/text-types'),
|
|
21
|
+
ParamSecurity = require('../util/paramsecurity'),
|
|
13
22
|
Logger = require('../logger'),
|
|
14
23
|
DateUtil = require('../util/dateutil'),
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
shimmer = require('../core/shimmer')
|
|
18
|
-
TraceSQL = require('../trace/trace-sql');
|
|
19
|
-
const { AsyncResource } = require('async_hooks');
|
|
24
|
+
Buffer = require('buffer').Buffer,
|
|
25
|
+
TraceSQL = require('../trace/trace-sql'),
|
|
26
|
+
shimmer = require('../core/shimmer');
|
|
20
27
|
|
|
21
28
|
var Mysql2Observer = function (agent) {
|
|
22
29
|
this.agent = agent;
|
|
@@ -26,25 +33,12 @@ var Mysql2Observer = function (agent) {
|
|
|
26
33
|
var dbc_hash = 0;
|
|
27
34
|
var dbc = 'mysql://';
|
|
28
35
|
|
|
29
|
-
// 후킹된
|
|
36
|
+
// 전역으로 후킹된 함수들을 추적
|
|
37
|
+
var globalHookedFunctions = new WeakSet();
|
|
38
|
+
// 후킹된 객체 인스턴스들을 추적 (중복 방지 강화)
|
|
30
39
|
var hookedInstances = new WeakSet();
|
|
31
40
|
|
|
32
|
-
|
|
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) {
|
|
41
|
+
var createQueryWrapper = function(isPromise = false) {
|
|
48
42
|
return function(original) {
|
|
49
43
|
return function wrappedQuery() {
|
|
50
44
|
var args = Array.prototype.slice.call(arguments);
|
|
@@ -52,15 +46,12 @@ var createQueryWrapper = function(isPromise = false, dbcUrl, mod) {
|
|
|
52
46
|
|
|
53
47
|
// ctx가 없으면 추적하지 않고 원본 함수만 실행
|
|
54
48
|
if (ctx == null || args[0] == null) {
|
|
55
|
-
return original.apply(this,
|
|
49
|
+
return original.apply(this, args);
|
|
56
50
|
}
|
|
57
51
|
if (args[0].sql == null && typeof args[0] != 'string') {
|
|
58
|
-
return original.apply(this,
|
|
52
|
+
return original.apply(this, args);
|
|
59
53
|
}
|
|
60
54
|
|
|
61
|
-
// AsyncResource 생성
|
|
62
|
-
var asyncResource = new AsyncResource('mysql2-query');
|
|
63
|
-
|
|
64
55
|
// 동시 쿼리 처리를 위한 고유 ID 생성
|
|
65
56
|
var queryId = Date.now() + '-' + Math.random().toString(36).substr(2, 9);
|
|
66
57
|
|
|
@@ -69,123 +60,181 @@ var createQueryWrapper = function(isPromise = false, dbcUrl, mod) {
|
|
|
69
60
|
ctx._processing_queries = new Set();
|
|
70
61
|
}
|
|
71
62
|
|
|
72
|
-
// SQL
|
|
73
|
-
var
|
|
74
|
-
|
|
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
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// 같은 최종 SQL이 동시에 처리되고 있는지 확인
|
|
95
|
-
if (finalSql && ctx._processing_queries.has(finalSql)) {
|
|
63
|
+
// 같은 SQL이 동시에 처리되고 있는지 확인
|
|
64
|
+
var sqlKey = typeof args[0] === 'string' ? args[0] : (args[0] && args[0].sql);
|
|
65
|
+
if (sqlKey && ctx._processing_queries.has(sqlKey)) {
|
|
96
66
|
return original.apply(this, args);
|
|
97
67
|
}
|
|
98
68
|
|
|
99
|
-
if (
|
|
100
|
-
ctx._processing_queries.add(
|
|
69
|
+
if (sqlKey) {
|
|
70
|
+
ctx._processing_queries.add(sqlKey);
|
|
101
71
|
}
|
|
102
72
|
|
|
103
|
-
|
|
104
|
-
|
|
73
|
+
var dbc_step = new DBCStep();
|
|
74
|
+
DataTextAgent.DBC.add(dbc_hash, dbc);
|
|
75
|
+
DataTextAgent.METHOD.add(dbc_hash, dbc);
|
|
76
|
+
DataTextAgent.ERROR.add(dbc_hash, dbc);
|
|
77
|
+
|
|
78
|
+
dbc_step.hash = dbc_hash;
|
|
79
|
+
dbc_step.start_time = ctx.getElapsedTime();
|
|
80
|
+
ctx.profile.push(dbc_step);
|
|
105
81
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
82
|
+
var sql_step = new SqlStepX();
|
|
83
|
+
sql_step.queryId = queryId;
|
|
84
|
+
sql_step.start_time = ctx.getElapsedTime();
|
|
85
|
+
ctx.profile.push(sql_step);
|
|
109
86
|
|
|
110
|
-
// SQL 시작 시간 기록
|
|
111
|
-
var sql_start_time = Date.now();
|
|
112
87
|
ctx.footprint('MySql2 Query Start [' + queryId + ']');
|
|
113
88
|
ctx.sql_count++;
|
|
114
89
|
|
|
115
|
-
var
|
|
116
|
-
|
|
90
|
+
var sql = args.length > 0 ? args[0] : undefined,
|
|
91
|
+
psql = null;
|
|
92
|
+
|
|
93
|
+
if (typeof sql !== 'string') {
|
|
94
|
+
sql = args[0].sql || undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (typeof sql === 'string' && sql.length > 0) {
|
|
117
98
|
try {
|
|
118
|
-
psql = escapeLiteral(
|
|
119
|
-
Logger.print('WHATAP-SQL-DEBUG', 'Processing SQL [' + queryId + ']: ' +
|
|
99
|
+
psql = escapeLiteral(sql);
|
|
100
|
+
Logger.print('WHATAP-SQL-DEBUG', 'Processing SQL [' + queryId + ']: ' + sql.substring(0, 100), false);
|
|
120
101
|
} catch (e) {
|
|
121
|
-
Logger.printError('WHATAP-
|
|
102
|
+
Logger.printError('WHATAP-191', 'Mysql2Observer escapeliteral error', e, false);
|
|
122
103
|
}
|
|
123
104
|
} else {
|
|
124
|
-
|
|
125
|
-
psql = escapeLiteral(
|
|
105
|
+
sql = '';
|
|
106
|
+
psql = escapeLiteral(sql);
|
|
126
107
|
}
|
|
127
108
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
109
|
+
if (psql != null) {
|
|
110
|
+
sql_step.hash = psql.sql;
|
|
111
|
+
}
|
|
112
|
+
sql_step.dbc = dbc_hash;
|
|
131
113
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
function queryCallback(err, results, fields) {
|
|
135
|
-
var currentCtx = TraceContextManager.getCurrentContext();
|
|
114
|
+
var els = new EscapeLiteralSQL(sql);
|
|
115
|
+
els.process();
|
|
136
116
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
117
|
+
// Context 활성 상태를 배열로 관리 (여러 쿼리 동시 처리)
|
|
118
|
+
if (!ctx.active_queries) {
|
|
119
|
+
ctx.active_queries = [];
|
|
120
|
+
}
|
|
121
|
+
ctx.active_queries.push({
|
|
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
|
+
}
|
|
142
147
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
// 원본 MySQL Observer의 queryCallback 함수를 참고한 결과 처리 함수
|
|
149
|
+
function queryCallback(obj, args) {
|
|
150
|
+
if (ctx == null) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
146
153
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
154
|
+
// 처리 완료된 쿼리를 목록에서 제거
|
|
155
|
+
if (sqlKey && ctx._processing_queries) {
|
|
156
|
+
ctx._processing_queries.delete(sqlKey);
|
|
157
|
+
}
|
|
151
158
|
|
|
152
|
-
|
|
153
|
-
|
|
159
|
+
// 활성 쿼리 목록에서 현재 쿼리 제거
|
|
160
|
+
if (ctx.active_queries) {
|
|
161
|
+
ctx.active_queries = ctx.active_queries.filter(q => q.queryId !== queryId);
|
|
162
|
+
}
|
|
154
163
|
|
|
155
|
-
|
|
156
|
-
handleSqlError(currentCtx, err, sqlHash);
|
|
157
|
-
}
|
|
164
|
+
TraceContextManager.resume(ctx._id);
|
|
158
165
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
166
|
+
// 에러 처리 (args[0]이 에러 객체)
|
|
167
|
+
if (args[0]) {
|
|
168
|
+
try {
|
|
169
|
+
if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
|
|
170
|
+
var traceDepth = conf.trace_sql_error_depth;
|
|
171
|
+
var errorStack = args[0].stack ? args[0].stack.split("\n") : [];
|
|
172
|
+
if (errorStack.length > traceDepth) {
|
|
173
|
+
errorStack = errorStack.slice(0, traceDepth + 1);
|
|
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);
|
|
167
178
|
}
|
|
168
|
-
}
|
|
169
179
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
180
|
+
// 에러 무시 설정 확인
|
|
181
|
+
var shouldAddError = false;
|
|
182
|
+
if (conf._is_trace_ignore_err_cls_contains === true && args[0].code &&
|
|
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
|
+
}
|
|
173
191
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
192
|
+
if (shouldAddError) {
|
|
193
|
+
sql_step.error = StatError.addError('mysql2-' + (args[0].code || 'UNKNOWN'),
|
|
194
|
+
args[0].message || 'mysql2 error', ctx.service_hash, TextTypes.SQL, sql_step.hash);
|
|
195
|
+
if (ctx.error.isZero()) {
|
|
196
|
+
ctx.error = sql_step.error;
|
|
197
|
+
}
|
|
177
198
|
}
|
|
178
199
|
} catch (e) {
|
|
179
|
-
Logger.printError('WHATAP-
|
|
200
|
+
Logger.printError('WHATAP-192', 'Error handling MySQL2 error', e, false);
|
|
180
201
|
}
|
|
202
|
+
}
|
|
181
203
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
204
|
+
sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
|
|
205
|
+
ctx.sql_time += sql_step.elapsed;
|
|
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);
|
|
185
232
|
}
|
|
186
233
|
|
|
187
|
-
//
|
|
188
|
-
|
|
234
|
+
// UPDATE/INSERT 쿼리 처리
|
|
235
|
+
if (args[1] != null && args[1].affectedRows != null && psql != null && psql.type === 'U') {
|
|
236
|
+
sql_step.updated = args[1].affectedRows || 0;
|
|
237
|
+
}
|
|
189
238
|
}
|
|
190
239
|
|
|
191
240
|
// Promise 기반 처리 (mysql2/promise)
|
|
@@ -194,142 +243,119 @@ var createQueryWrapper = function(isPromise = false, dbcUrl, mod) {
|
|
|
194
243
|
|
|
195
244
|
// Promise 체인 처리
|
|
196
245
|
if (result && typeof result.then === 'function') {
|
|
197
|
-
var boundCallback = createBoundQueryCallback();
|
|
198
|
-
|
|
199
246
|
return result.then(function(queryResult) {
|
|
200
247
|
// MySQL2 Promise 결과는 [rows, fields] 형태
|
|
201
248
|
var rows = Array.isArray(queryResult) ? queryResult[0] : queryResult;
|
|
202
249
|
var fields = Array.isArray(queryResult) ? queryResult[1] : null;
|
|
203
250
|
|
|
204
|
-
//
|
|
205
|
-
|
|
251
|
+
// queryCallback 형태로 변환하여 호출
|
|
252
|
+
queryCallback(null, [null, rows, fields]);
|
|
206
253
|
return queryResult;
|
|
207
254
|
}).catch(function(error) {
|
|
208
|
-
// 에러 시
|
|
209
|
-
|
|
255
|
+
// 에러 시 처리
|
|
256
|
+
queryCallback(null, [error, null]);
|
|
210
257
|
throw error;
|
|
211
258
|
});
|
|
212
259
|
}
|
|
213
260
|
return result;
|
|
214
261
|
}
|
|
215
262
|
|
|
216
|
-
// 콜백 기반 처리 (일반 mysql2)
|
|
217
|
-
var
|
|
218
|
-
var boundCallback = createBoundQueryCallback();
|
|
263
|
+
// 콜백 기반 처리 (일반 mysql2) - 원본 MysqlObserver 방식 적용
|
|
264
|
+
var lastCallback = false;
|
|
219
265
|
|
|
266
|
+
// 원본 MysqlObserver의 functionHook 방식을 모방
|
|
220
267
|
// 마지막 인자에서 함수를 찾아서 후킹
|
|
221
|
-
|
|
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
|
-
}
|
|
268
|
+
lastCallback = hookLastFunction(args, queryCallback);
|
|
241
269
|
|
|
242
|
-
|
|
270
|
+
// functionHook이 실패한 경우 _callback 속성 확인 (원본 코드 참조)
|
|
271
|
+
if (lastCallback === false && args.length > 0 && args[0]._callback) {
|
|
243
272
|
try {
|
|
273
|
+
// _callback 속성이 있는 경우 직접 후킹
|
|
244
274
|
var originalCallback = args[0]._callback;
|
|
245
275
|
args[0]._callback = function() {
|
|
246
276
|
var callbackArgs = Array.prototype.slice.call(arguments);
|
|
247
277
|
try {
|
|
248
|
-
|
|
278
|
+
queryCallback(this, callbackArgs);
|
|
249
279
|
} catch (e) {
|
|
250
280
|
Logger.printError('WHATAP-CALLBACK', 'Error in _callback hook', e, false);
|
|
251
281
|
}
|
|
282
|
+
// 원본 콜백 실행
|
|
252
283
|
if (originalCallback && typeof originalCallback === 'function') {
|
|
253
284
|
return originalCallback.apply(this, callbackArgs);
|
|
254
285
|
}
|
|
255
286
|
};
|
|
256
|
-
|
|
287
|
+
lastCallback = true;
|
|
257
288
|
} catch (e) {
|
|
258
289
|
Logger.printError('WHATAP-CALLBACK-HOOK', 'Error hooking _callback', e, false);
|
|
259
290
|
}
|
|
260
291
|
}
|
|
261
292
|
|
|
293
|
+
// 수정된 args로 원본 함수 실행
|
|
262
294
|
try {
|
|
263
295
|
return original.apply(this, args);
|
|
264
296
|
} catch (queryError) {
|
|
265
|
-
|
|
297
|
+
// 쿼리 실행 중 에러 발생 시 즉시 처리
|
|
298
|
+
queryCallback(null, [queryError, null]);
|
|
266
299
|
throw queryError;
|
|
267
300
|
}
|
|
268
301
|
};
|
|
269
302
|
};
|
|
270
303
|
};
|
|
271
304
|
|
|
272
|
-
|
|
273
|
-
|
|
305
|
+
/**
|
|
306
|
+
* 원본 MySQL Observer의 functionHook을 모방한 함수
|
|
307
|
+
* 마지막 함수 인자를 찾아서 콜백으로 후킹
|
|
308
|
+
*/
|
|
309
|
+
function hookLastFunction(args, callback) {
|
|
310
|
+
if (!args || args.length === 0) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
274
313
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
}
|
|
314
|
+
// 뒤에서부터 함수를 찾음
|
|
315
|
+
for (var i = args.length - 1; i >= 0; i--) {
|
|
316
|
+
if (typeof args[i] === 'function') {
|
|
317
|
+
var originalFunction = args[i];
|
|
289
318
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
}
|
|
319
|
+
// 함수를 래핑
|
|
320
|
+
args[i] = function() {
|
|
321
|
+
var functionArgs = Array.prototype.slice.call(arguments);
|
|
301
322
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
323
|
+
try {
|
|
324
|
+
// WHATAP 콜백 먼저 실행
|
|
325
|
+
callback(this, functionArgs);
|
|
326
|
+
} catch (e) {
|
|
327
|
+
Logger.printError('WHATAP-FUNCTION-HOOK', 'Error in function hook callback', e, false);
|
|
328
|
+
}
|
|
308
329
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
330
|
+
// 원본 함수 실행
|
|
331
|
+
if (originalFunction && typeof originalFunction === 'function') {
|
|
332
|
+
try {
|
|
333
|
+
return originalFunction.apply(this, functionArgs);
|
|
334
|
+
} catch (e) {
|
|
335
|
+
Logger.printError('WHATAP-ORIGINAL-FUNCTION', 'Error in original function', e, false);
|
|
336
|
+
throw e;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// 후킹된 함수임을 표시
|
|
342
|
+
args[i].__whatap_hooked__ = true;
|
|
343
|
+
return true;
|
|
317
344
|
}
|
|
318
|
-
} catch (e) {
|
|
319
|
-
Logger.printError('WHATAP-226', 'Error handling MySQL2 error', e, false);
|
|
320
345
|
}
|
|
346
|
+
|
|
347
|
+
return false;
|
|
321
348
|
}
|
|
322
349
|
|
|
323
|
-
|
|
324
|
-
|
|
350
|
+
/**
|
|
351
|
+
* 개선된 Connection 래퍼 - 원본 패턴 적용
|
|
352
|
+
*/
|
|
353
|
+
var createConnectionWrapper = function(isPromise = false) {
|
|
325
354
|
return function(original) {
|
|
326
355
|
return function wrappedCreateConnection() {
|
|
327
356
|
var args = Array.prototype.slice.call(arguments);
|
|
328
357
|
var ctx = TraceContextManager.getCurrentContext();
|
|
329
358
|
|
|
330
|
-
// AsyncResource 생성
|
|
331
|
-
var asyncResource = ctx ? new AsyncResource('mysql2-connection') : null;
|
|
332
|
-
|
|
333
359
|
// DB 연결 정보 구성
|
|
334
360
|
if (dbc_hash === 0 && args.length > 0) {
|
|
335
361
|
var info = (args[0] || {});
|
|
@@ -340,12 +366,18 @@ var createConnectionWrapper = function(isPromise = false, mod) {
|
|
|
340
366
|
dbc += '/';
|
|
341
367
|
dbc += info.database || '';
|
|
342
368
|
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);
|
|
343
372
|
}
|
|
344
373
|
|
|
345
|
-
|
|
374
|
+
var dbc_step = null;
|
|
375
|
+
if (ctx && !ctx.db_opening) {
|
|
346
376
|
ctx.db_opening = true;
|
|
347
377
|
ctx.footprint('MySql2 Connecting Start');
|
|
348
|
-
|
|
378
|
+
dbc_step = new DBCStep();
|
|
379
|
+
dbc_step.start_time = ctx.getElapsedTime();
|
|
380
|
+
dbc_step.hash = dbc_hash;
|
|
349
381
|
}
|
|
350
382
|
|
|
351
383
|
var result = original.apply(this, args);
|
|
@@ -358,31 +390,29 @@ var createConnectionWrapper = function(isPromise = false, mod) {
|
|
|
358
390
|
|
|
359
391
|
// Connection의 query와 execute 메서드 후킹
|
|
360
392
|
if (connection.query && !connection.query.__whatap_wrapped__) {
|
|
361
|
-
shimmer.wrap(connection, 'query', createQueryWrapper(isPromise
|
|
393
|
+
shimmer.wrap(connection, 'query', createQueryWrapper(isPromise));
|
|
362
394
|
connection.query.__whatap_wrapped__ = true;
|
|
363
395
|
}
|
|
364
396
|
if (connection.execute && !connection.execute.__whatap_wrapped__) {
|
|
365
|
-
shimmer.wrap(connection, 'execute', createQueryWrapper(isPromise
|
|
397
|
+
shimmer.wrap(connection, 'execute', createQueryWrapper(isPromise));
|
|
366
398
|
connection.execute.__whatap_wrapped__ = true;
|
|
367
399
|
}
|
|
368
400
|
|
|
369
|
-
// 에러 델리게이트 후킹
|
|
370
|
-
if (connection._protocol && connection._protocol._delegateError
|
|
371
|
-
!connection._protocol._delegateError.__whatap_wrapped__) {
|
|
401
|
+
// 에러 델리게이트 후킹 (원본 패턴)
|
|
402
|
+
if (connection._protocol && connection._protocol._delegateError) {
|
|
372
403
|
try {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
}
|
|
385
|
-
connection._protocol._delegateError.__whatap_wrapped__ = true;
|
|
404
|
+
var originalDelegateError = connection._protocol._delegateError;
|
|
405
|
+
connection._protocol._delegateError = function() {
|
|
406
|
+
var args = Array.prototype.slice.call(arguments);
|
|
407
|
+
try {
|
|
408
|
+
// WHATAP 에러 처리
|
|
409
|
+
handleProtocolError(args);
|
|
410
|
+
} catch (e) {
|
|
411
|
+
Logger.printError('WHATAP-PROTOCOL-ERROR', 'Error in protocol error handler', e, false);
|
|
412
|
+
}
|
|
413
|
+
// 원본 함수 실행
|
|
414
|
+
return originalDelegateError.apply(this, args);
|
|
415
|
+
};
|
|
386
416
|
} catch (e) {
|
|
387
417
|
Logger.printError('WHATAP-PROTOCOL-HOOK', 'Error hooking _delegateError', e, false);
|
|
388
418
|
}
|
|
@@ -391,30 +421,50 @@ var createConnectionWrapper = function(isPromise = false, mod) {
|
|
|
391
421
|
return connection;
|
|
392
422
|
}
|
|
393
423
|
|
|
394
|
-
|
|
395
|
-
|
|
424
|
+
// 프로토콜 에러 처리 함수
|
|
425
|
+
function handleProtocolError(args) {
|
|
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
|
+
}
|
|
396
441
|
|
|
397
442
|
try {
|
|
398
|
-
var
|
|
399
|
-
var errorMessage =
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
if (
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
443
|
+
var errorCode = args[0].code || 'UNKNOWN';
|
|
444
|
+
var errorMessage = ctx.error_message = args[0].message || 'mysql2 error';
|
|
445
|
+
ctx.error_class = args[0].name || args[0].constructor?.name || 'MySQLError';
|
|
446
|
+
|
|
447
|
+
if (args[0].fatal) {
|
|
448
|
+
step.error = StatError.addError('mysql2-' + errorCode, errorMessage, ctx.service_hash);
|
|
449
|
+
if (ctx.error.isZero()) {
|
|
450
|
+
ctx.error = step.error;
|
|
451
|
+
}
|
|
452
|
+
} else {
|
|
453
|
+
var shouldAddError = false;
|
|
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
|
+
}
|
|
409
461
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
errors.push(errorMessage);
|
|
462
|
+
if (shouldAddError) {
|
|
463
|
+
step.error = StatError.addError('mysql2-' + errorCode, errorMessage, ctx.service_hash, TextTypes.SQL, step.hash);
|
|
464
|
+
if (ctx.error.isZero()) {
|
|
465
|
+
ctx.error = step.error;
|
|
466
|
+
}
|
|
416
467
|
}
|
|
417
|
-
AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
|
|
418
468
|
}
|
|
419
469
|
} catch (e) {
|
|
420
470
|
Logger.printError('WHATAP-PROTOCOL-ERROR-HANDLING', 'Error processing protocol error', e, false);
|
|
@@ -423,41 +473,37 @@ var createConnectionWrapper = function(isPromise = false, mod) {
|
|
|
423
473
|
|
|
424
474
|
// Promise 기반 연결 처리
|
|
425
475
|
if (isPromise && result && typeof result.then === 'function') {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
return connection;
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
} else {
|
|
452
|
-
// 동기/콜백 기반 연결 처리
|
|
476
|
+
return result.then(function(connection) {
|
|
477
|
+
hookConnection(connection);
|
|
478
|
+
|
|
479
|
+
if (ctx && dbc_step) {
|
|
480
|
+
ctx.footprint('MySql2 Connecting Done');
|
|
481
|
+
ctx.db_opening = false;
|
|
482
|
+
dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
|
|
483
|
+
ctx.profile.push(dbc_step);
|
|
484
|
+
}
|
|
485
|
+
return connection;
|
|
486
|
+
}).catch(function(error) {
|
|
487
|
+
if (ctx && dbc_step) {
|
|
488
|
+
ctx.footprint('MySql2 Connecting Error');
|
|
489
|
+
ctx.db_opening = false;
|
|
490
|
+
dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
|
|
491
|
+
ctx.profile.push(dbc_step);
|
|
492
|
+
}
|
|
493
|
+
throw error;
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
// 동기/콜백 기반 연결 처리
|
|
497
|
+
else {
|
|
453
498
|
if (result && typeof result === 'object') {
|
|
454
499
|
hookConnection(result);
|
|
455
500
|
}
|
|
456
501
|
|
|
457
|
-
if (ctx) {
|
|
458
|
-
ctx.elapsed = Date.now() - ctx.start_time;
|
|
502
|
+
if (ctx && dbc_step) {
|
|
459
503
|
ctx.footprint('MySql2 Connecting Done');
|
|
460
504
|
ctx.db_opening = false;
|
|
505
|
+
dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
|
|
506
|
+
ctx.profile.push(dbc_step);
|
|
461
507
|
}
|
|
462
508
|
}
|
|
463
509
|
|
|
@@ -466,7 +512,10 @@ var createConnectionWrapper = function(isPromise = false, mod) {
|
|
|
466
512
|
};
|
|
467
513
|
};
|
|
468
514
|
|
|
469
|
-
|
|
515
|
+
/**
|
|
516
|
+
* 개선된 Pool 래퍼 - 원본 패턴 적용
|
|
517
|
+
*/
|
|
518
|
+
var createPoolWrapper = function(isPromise = false) {
|
|
470
519
|
return function(original) {
|
|
471
520
|
return function wrappedCreatePool() {
|
|
472
521
|
var args = Array.prototype.slice.call(arguments);
|
|
@@ -481,22 +530,28 @@ var createPoolWrapper = function(isPromise = false, mod) {
|
|
|
481
530
|
dbc += '/';
|
|
482
531
|
dbc += info.database || '';
|
|
483
532
|
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);
|
|
484
536
|
}
|
|
485
537
|
|
|
486
538
|
var pool = original.apply(this, args);
|
|
487
539
|
|
|
540
|
+
// Pool 객체 후킹
|
|
488
541
|
if (pool && !hookedInstances.has(pool)) {
|
|
489
542
|
hookedInstances.add(pool);
|
|
490
543
|
|
|
544
|
+
// Pool의 query와 execute 메서드 후킹
|
|
491
545
|
if (pool.query && !pool.query.__whatap_wrapped__) {
|
|
492
|
-
shimmer.wrap(pool, 'query', createQueryWrapper(isPromise
|
|
546
|
+
shimmer.wrap(pool, 'query', createQueryWrapper(isPromise));
|
|
493
547
|
pool.query.__whatap_wrapped__ = true;
|
|
494
548
|
}
|
|
495
549
|
if (pool.execute && !pool.execute.__whatap_wrapped__) {
|
|
496
|
-
shimmer.wrap(pool, 'execute', createQueryWrapper(isPromise
|
|
550
|
+
shimmer.wrap(pool, 'execute', createQueryWrapper(isPromise));
|
|
497
551
|
pool.execute.__whatap_wrapped__ = true;
|
|
498
552
|
}
|
|
499
553
|
|
|
554
|
+
// getConnection 메서드 후킹 - 원본 패턴 적용
|
|
500
555
|
if (pool.getConnection && !pool.getConnection.__whatap_wrapped__) {
|
|
501
556
|
shimmer.wrap(pool, 'getConnection', function(original) {
|
|
502
557
|
return function wrappedGetConnection() {
|
|
@@ -507,76 +562,42 @@ var createPoolWrapper = function(isPromise = false, mod) {
|
|
|
507
562
|
return original.apply(this, args);
|
|
508
563
|
}
|
|
509
564
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
//
|
|
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;
|
|
565
|
+
// 원본 패턴: functionHook을 사용하여 콜백 후킹
|
|
566
|
+
hookLastFunction(args, function(obj, callbackArgs) {
|
|
567
|
+
TraceContextManager.resume(ctx._id);
|
|
568
|
+
DataTextAgent.DBC.add(dbc_hash, dbc);
|
|
569
|
+
|
|
570
|
+
if (callbackArgs[0] != null) {
|
|
571
|
+
return; // 에러가 있으면 처리하지 않음
|
|
548
572
|
}
|
|
549
|
-
}
|
|
550
573
|
|
|
551
|
-
|
|
574
|
+
var conn = callbackArgs[1];
|
|
575
|
+
if (conn && !conn.__query_hook__) {
|
|
576
|
+
conn.__query_hook__ = true;
|
|
552
577
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
if (!currentCtx) {
|
|
558
|
-
TraceContextManager.resume(ctx.id);
|
|
559
|
-
currentCtx = ctx;
|
|
578
|
+
// Connection 객체의 메서드들 후킹
|
|
579
|
+
if (conn.query && !conn.query.__whatap_wrapped__) {
|
|
580
|
+
shimmer.wrap(conn, 'query', createQueryWrapper(isPromise));
|
|
581
|
+
conn.query.__whatap_wrapped__ = true;
|
|
560
582
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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
|
-
}
|
|
583
|
+
if (conn.execute && !conn.execute.__whatap_wrapped__) {
|
|
584
|
+
shimmer.wrap(conn, 'execute', createQueryWrapper(isPromise));
|
|
585
|
+
conn.execute.__whatap_wrapped__ = true;
|
|
576
586
|
}
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
var result = original.apply(this, args);
|
|
577
591
|
|
|
578
|
-
|
|
579
|
-
|
|
592
|
+
// 연결 통계 업데이트 (원본 패턴)
|
|
593
|
+
try {
|
|
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);
|
|
580
601
|
}
|
|
581
602
|
|
|
582
603
|
return result;
|
|
@@ -591,6 +612,50 @@ var createPoolWrapper = function(isPromise = false, mod) {
|
|
|
591
612
|
};
|
|
592
613
|
};
|
|
593
614
|
|
|
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
|
+
|
|
594
659
|
var checkedSql = new IntKeyMap(2000).setMax(2000);
|
|
595
660
|
var nonLiteSql = new IntKeyMap(5000).setMax(5000);
|
|
596
661
|
var date = DateUtil.yyyymmdd();
|
|
@@ -623,6 +688,8 @@ function escapeLiteral(sql) {
|
|
|
623
688
|
els.process();
|
|
624
689
|
|
|
625
690
|
var hash = HashUtil.hashFromString(els.getParsedSql());
|
|
691
|
+
DataTextAgent.SQL.add(hash, els.getParsedSql());
|
|
692
|
+
|
|
626
693
|
if (hash === sqlHash) {
|
|
627
694
|
psql = new ParsedSql(els.sqlType, hash, null);
|
|
628
695
|
nonLiteSql.put(sqlHash, psql);
|
|
@@ -633,33 +700,4 @@ function escapeLiteral(sql) {
|
|
|
633
700
|
return psql;
|
|
634
701
|
}
|
|
635
702
|
|
|
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
|
-
|
|
665
703
|
exports.Mysql2Observer = Mysql2Observer;
|