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