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.
Files changed (198) hide show
  1. package/README.md +32 -78
  2. package/lib/conf/conf-sys-mon.js +101 -0
  3. package/lib/conf/config-default.js +10 -3
  4. package/lib/conf/configure.js +369 -349
  5. package/lib/conf/license.js +1 -1
  6. package/lib/control/cmd-config.js +24 -0
  7. package/lib/control/control-handler.js +367 -0
  8. package/lib/control/packagectr-helper.js +34 -3
  9. package/lib/core/agent.js +176 -882
  10. package/lib/core/interceptor.js +6 -6
  11. package/lib/core/request-agent.js +27 -0
  12. package/lib/core/shimmer.js +82 -36
  13. package/lib/counter/counter-manager.js +79 -8
  14. package/lib/counter/meter/meter-activex.js +67 -0
  15. package/lib/counter/meter/meter-httpc.js +57 -0
  16. package/lib/counter/meter/meter-resource.js +9 -0
  17. package/lib/counter/meter/meter-service.js +168 -0
  18. package/lib/counter/meter/meter-socket.io.js +51 -0
  19. package/lib/counter/meter/meter-sql.js +71 -0
  20. package/lib/counter/meter/meter-users.js +58 -0
  21. package/lib/counter/meter.js +183 -0
  22. package/lib/counter/task/activetransaction.js +68 -17
  23. package/lib/counter/task/agentinfo.js +107 -0
  24. package/lib/{system → counter/task}/gc-action.js +27 -74
  25. package/lib/counter/task/gcstat.js +34 -0
  26. package/lib/counter/task/heapmem.js +25 -0
  27. package/lib/counter/task/httpc.js +76 -0
  28. package/lib/counter/task/metering-info.js +125 -0
  29. package/lib/counter/task/proc-cpu.js +29 -0
  30. package/lib/counter/task/realtimeuser.js +31 -0
  31. package/lib/counter/task/res/systemECSTask.js +39 -0
  32. package/lib/counter/task/res/systemKubeTask.js +53 -0
  33. package/lib/counter/task/res/util/awsEcsClientThread.js +218 -0
  34. package/lib/counter/task/res/util/linuxProcStatUtil.js +14 -0
  35. package/lib/counter/task/res-sys-cpu.js +62 -0
  36. package/lib/counter/task/service.js +202 -0
  37. package/lib/counter/task/socketio.js +30 -0
  38. package/lib/counter/task/sql.js +105 -0
  39. package/lib/counter/task/systemperf.js +43 -0
  40. package/lib/data/datapack-sender.js +289 -0
  41. package/lib/data/dataprofile-agent.js +162 -0
  42. package/lib/data/datatext-agent.js +135 -0
  43. package/lib/data/event-level.js +15 -0
  44. package/lib/data/test.js +49 -0
  45. package/lib/data/zipprofile.js +197 -0
  46. package/lib/env/constants.js +21 -0
  47. package/lib/error/error-handler.js +437 -0
  48. package/lib/io/data-inputx.js +13 -3
  49. package/lib/io/data-outputx.js +268 -206
  50. package/lib/kube/kube-client.js +144 -0
  51. package/lib/lang/text-types.js +58 -0
  52. package/lib/logger.js +6 -6
  53. package/lib/logsink/line-log-util.js +87 -0
  54. package/lib/logsink/line-log.js +12 -0
  55. package/lib/logsink/log-sender.js +78 -0
  56. package/lib/logsink/log-tracer.js +40 -0
  57. package/lib/logsink/sender-util.js +56 -0
  58. package/lib/logsink/zip/zip-send.js +177 -0
  59. package/lib/net/netflag.js +55 -0
  60. package/lib/net/receiver.js +66 -0
  61. package/lib/net/security-master.js +139 -20
  62. package/lib/net/sender.js +141 -0
  63. package/lib/net/tcp-return.js +18 -0
  64. package/lib/net/tcp-session.js +286 -0
  65. package/lib/net/tcpreq-client-proxy.js +70 -0
  66. package/lib/net/tcprequest-mgr.js +58 -0
  67. package/lib/observers/apollo-server-observer.js +33 -27
  68. package/lib/observers/cluster-observer.js +22 -0
  69. package/lib/observers/express-observer.js +215 -0
  70. package/lib/observers/file-observer.js +184 -0
  71. package/lib/observers/global-observer.js +155 -80
  72. package/lib/observers/grpc-observer.js +336 -0
  73. package/lib/observers/http-observer.js +666 -236
  74. package/lib/observers/maria-observer.js +204 -362
  75. package/lib/observers/memcached-observer.js +56 -0
  76. package/lib/observers/mongo-observer.js +317 -0
  77. package/lib/observers/mongodb-observer.js +169 -226
  78. package/lib/observers/mongoose-observer.js +518 -323
  79. package/lib/observers/mssql-observer.js +177 -418
  80. package/lib/observers/mysql-observer.js +342 -449
  81. package/lib/observers/mysql2-observer.js +396 -358
  82. package/lib/observers/net-observer.js +77 -0
  83. package/lib/observers/oracle-observer.js +559 -384
  84. package/lib/observers/pgsql-observer.js +231 -489
  85. package/lib/observers/prisma-observer.js +303 -92
  86. package/lib/observers/process-observer.js +79 -35
  87. package/lib/observers/promise-observer.js +31 -0
  88. package/lib/observers/redis-observer.js +166 -331
  89. package/lib/observers/schedule-observer.js +67 -0
  90. package/lib/observers/socket.io-observer.js +226 -187
  91. package/lib/observers/stream-observer.js +19 -0
  92. package/lib/observers/thrift-observer.js +197 -0
  93. package/lib/observers/websocket-observer.js +175 -301
  94. package/lib/pack/activestack-pack.js +55 -0
  95. package/lib/pack/apenum.js +8 -0
  96. package/lib/pack/counter-pack.js +3 -0
  97. package/lib/pack/errorsnap-pack.js +69 -0
  98. package/lib/pack/event-pack.js +54 -0
  99. package/lib/pack/hitmap-pack.js +63 -0
  100. package/lib/pack/hitmap-pack1.js +152 -0
  101. package/lib/pack/log-sink-pack.js +14 -52
  102. package/lib/pack/netstat.js +15 -0
  103. package/lib/pack/otype.js +7 -0
  104. package/lib/pack/profile-pack.js +49 -0
  105. package/lib/pack/realtimeuser-pack.js +41 -0
  106. package/lib/pack/stat-general-pack.js +96 -0
  107. package/lib/pack/staterror-pack.js +120 -0
  108. package/lib/pack/stathttpc-pack.js +66 -0
  109. package/lib/pack/stathttpc-rec.js +78 -0
  110. package/lib/pack/statremote-pack.js +46 -0
  111. package/lib/pack/statservice-pack.js +63 -0
  112. package/lib/pack/statservice-pack1.js +88 -0
  113. package/lib/pack/statservice-rec.js +292 -0
  114. package/lib/pack/statservice-rec_dep.js +151 -0
  115. package/lib/pack/statsql-pack.js +69 -0
  116. package/lib/pack/statsql-rec.js +100 -0
  117. package/lib/pack/statuseragent-pack.js +44 -0
  118. package/lib/pack/tagcount-pack.js +4 -4
  119. package/lib/pack/tagctr.js +15 -0
  120. package/lib/pack/text-pack.js +50 -0
  121. package/lib/pack/time-count.js +25 -0
  122. package/lib/pack/websocket.js +15 -0
  123. package/lib/pack/zip-pack.js +70 -0
  124. package/lib/pii/pii-item.js +31 -0
  125. package/lib/pii/pii-mask.js +174 -0
  126. package/lib/plugin/plugin-loadermanager.js +57 -0
  127. package/lib/plugin/plugin.js +75 -0
  128. package/lib/service/tx-record.js +332 -0
  129. package/lib/stat/stat-error.js +116 -0
  130. package/lib/stat/stat-httpc.js +98 -0
  131. package/lib/stat/stat-remote-ip.js +46 -0
  132. package/lib/stat/stat-remote-ipurl.js +88 -0
  133. package/lib/stat/stat-sql.js +113 -0
  134. package/lib/stat/stat-tranx.js +58 -0
  135. package/lib/stat/stat-tx-caller.js +160 -0
  136. package/lib/stat/stat-tx-domain.js +111 -0
  137. package/lib/stat/stat-tx-referer.js +112 -0
  138. package/lib/stat/stat-useragent.js +48 -0
  139. package/lib/stat/timingsender.js +76 -0
  140. package/lib/step/activestack-step.js +38 -0
  141. package/lib/step/dbc-step.js +36 -0
  142. package/lib/step/http-stepx.js +67 -0
  143. package/lib/step/message-step.js +40 -0
  144. package/lib/step/method-stepx.js +45 -0
  145. package/lib/step/resultset-step.js +40 -0
  146. package/lib/step/securemsg-step.js +44 -0
  147. package/lib/step/socket-step.js +46 -0
  148. package/lib/step/sql-stepx.js +68 -0
  149. package/lib/step/sqlxtype.js +16 -0
  150. package/lib/step/step.js +66 -0
  151. package/lib/step/stepenum.js +54 -0
  152. package/lib/topology/link.js +63 -0
  153. package/lib/topology/nodeinfo.js +123 -0
  154. package/lib/topology/status-detector.js +111 -0
  155. package/lib/trace/trace-context-manager.js +113 -25
  156. package/lib/trace/trace-context.js +21 -7
  157. package/lib/trace/trace-httpc.js +17 -11
  158. package/lib/trace/trace-sql.js +29 -21
  159. package/lib/util/anylist.js +103 -0
  160. package/lib/util/cardinality/hyperloglog.js +106 -0
  161. package/lib/util/cardinality/murmurhash.js +31 -0
  162. package/lib/util/cardinality/registerset.js +75 -0
  163. package/lib/util/errordata.js +21 -0
  164. package/lib/util/escape-literal-sql.js +5 -5
  165. package/lib/util/hashutil.js +18 -18
  166. package/lib/util/iputil_x.js +527 -0
  167. package/lib/util/keygen.js +0 -3
  168. package/lib/util/kube-util.js +73 -0
  169. package/lib/util/linkedset.js +1 -2
  170. package/lib/util/nodeutil.js +2 -1
  171. package/lib/util/paramsecurity.js +80 -0
  172. package/lib/util/pre-process.js +13 -0
  173. package/lib/util/process-seq.js +166 -0
  174. package/lib/util/property-util.js +36 -0
  175. package/lib/util/request-queue.js +70 -0
  176. package/lib/util/requestdouble-queue.js +72 -0
  177. package/lib/util/resourceprofile.js +157 -0
  178. package/lib/util/stop-watch.js +30 -0
  179. package/lib/util/system-util.js +10 -0
  180. package/lib/util/userid-util.js +57 -0
  181. package/lib/value/map-value.js +3 -2
  182. package/package.json +9 -4
  183. package/whatap.conf +1 -4
  184. package/agent/darwin/arm64/whatap_nodejs +0 -0
  185. package/agent/linux/amd64/whatap_nodejs +0 -0
  186. package/agent/linux/arm64/whatap_nodejs +0 -0
  187. package/build.txt +0 -4
  188. package/lib/observers/ioredis-observer.js +0 -294
  189. package/lib/udp/async_sender.js +0 -119
  190. package/lib/udp/index.js +0 -17
  191. package/lib/udp/packet_enum.js +0 -52
  192. package/lib/udp/packet_queue.js +0 -69
  193. package/lib/udp/packet_type_enum.js +0 -33
  194. package/lib/udp/param_def.js +0 -72
  195. package/lib/udp/udp_session.js +0 -336
  196. package/lib/util/sql-util.js +0 -178
  197. package/lib/util/trace-helper.js +0 -91
  198. package/lib/util/transfer.js +0 -58
@@ -6,394 +6,420 @@
6
6
 
7
7
  var TraceContextManager = require('../trace/trace-context-manager'),
8
8
  ParsedSql = require('../trace/parsed-sql'),
9
- conf = require('../conf/configure'),
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'),
10
15
  IntKeyMap = require('../util/intkey-map'),
11
16
  EscapeLiteralSQL = require('../util/escape-literal-sql'),
12
17
  HashUtil = require('../util/hashutil'),
18
+ StatError = require('../stat/stat-error'),
19
+ TextTypes = require('../lang/text-types'),
20
+ ParamSecurity = require('../util/paramsecurity'),
13
21
  Logger = require('../logger'),
22
+ conf = require('../conf/configure'),
14
23
  DateUtil = require('../util/dateutil'),
15
- AsyncSender = require('../udp/async_sender'),
16
- PacketTypeEnum = require('../udp/packet_type_enum'),
17
- shimmer = require('../core/shimmer'),
24
+ Buffer = require('buffer').Buffer,
18
25
  TraceSQL = require('../trace/trace-sql');
19
- const { AsyncResource } = require('async_hooks');
20
26
 
21
27
  var MysqlObserver = function (agent) {
22
28
  this.agent = agent;
23
29
  this.packages = ['mysql'];
24
30
  };
25
31
 
26
- // 전역 dbc 정보
27
- var dbc_hash = 0;
28
- var dbc = 'mysql://';
32
+ var queryHook = function (dbc, agent) {
33
+
34
+ return function (obj, args) {
35
+ var ctx = TraceContextManager.getCurrentContext();
29
36
 
30
- // 후킹된 객체 추적
31
- var hookedInstances = new WeakSet();
37
+ if (ctx == null || args[0] == null) {
38
+ return;
39
+ }
40
+ if (args[0].sql == null && typeof args[0] != 'string') {
41
+ return;
42
+ }
32
43
 
33
- // 공통 함수들
34
- function handleSqlError(ctx, err, sqlHash) {
35
- if (!err) return;
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)
36
49
 
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);
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++;
61
+
62
+ var sql = args.length > 0 ? args[0] : undefined,
63
+ psql = null;
64
+
65
+ if (typeof sql !== 'string') {
66
+ sql = args[0].sql || undefined;
67
+ }
68
+
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);
47
74
  }
48
- errorStack = stackLines.join("\n");
75
+ } else {
76
+ sql = '';
77
+ psql = escapeLiteral(sql);
49
78
  }
50
79
 
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;
80
+ if (psql != null) {
81
+ sql_step.hash = psql.sql;
82
+ // sql_step.crud = psql.type.charCodeAt(0);
61
83
  }
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;
62
111
 
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);
112
+ function queryCallback(obj, args) {
113
+ if (ctx == null) {
114
+ return;
69
115
  }
70
- if (errorStack || err.stack) {
71
- errors.push(errorStack || err.stack);
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
+ }
72
163
  }
73
164
 
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
- }
165
+ sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
166
+ ctx.sql_time += sql_step.elapsed;
80
167
 
81
- function handleProtocolError(ctx, error) {
82
- if (!error) return;
168
+ TraceSQL.isSlowSQL(ctx);
83
169
 
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
- }
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);
189
+
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();
193
+
194
+ MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
195
+ StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
96
196
 
97
- if (shouldAddError) {
98
- ctx.error = 1;
99
- ctx.status = 500;
100
- var errors = [errorClass];
101
- if (errorMessage) {
102
- errors.push(errorMessage);
197
+ TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
103
198
  }
104
- if (error.stack) {
105
- errors.push(error.stack);
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);
106
203
  }
204
+ }
205
+
206
+ lastCallback = agent.aop.functionHook(args, -1, queryCallback);
107
207
 
108
- AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
208
+ if (lastCallback === false && args.length > 0 && args[0]._callback) {
209
+ agent.aop.both(args[0], '_callback', queryCallback);
109
210
  }
110
- } catch (e) {
111
- Logger.printError('WHATAP-PROTOCOL-ERROR-HANDLING', 'Error processing protocol error', e, false);
112
211
  }
113
- }
212
+ };
114
213
 
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);
214
+ var toParamBytes = function (p, crc) {
215
+ if (p == null || p.length === 0) {
216
+ return null;
125
217
  }
126
- }
127
-
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);
218
+ try {
219
+ return ParamSecurity.encrypt(Buffer.from(p, 'utf8'), crc);
220
+ } catch (e) {
221
+ return null;
222
+ }
223
+ };
132
224
 
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;
137
- }
138
- if (connection.execute && !connection.execute.__whatap_wrapped__) {
139
- shimmer.wrap(connection, 'execute', createQueryWrapper(agent, dbc, mysqlModule));
140
- connection.execute.__whatap_wrapped__ = true;
225
+ var errorDelegate = function () {
226
+ return function (obj, args) {
227
+ var ctx = TraceContextManager.getCurrentContext();
228
+ if (ctx == null) {
229
+ return;
141
230
  }
142
231
 
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();
232
+ var laststep = ctx.profile.getLastSteps(1);
233
+ if (laststep == null || laststep.length === 0) {
234
+ return;
235
+ }
151
236
 
152
- if (ctx && args[0]) {
153
- handleSqlError(ctx, args[0], 0);
154
- }
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);
241
+ }
155
242
 
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);
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
+ }
162
266
  }
267
+ } catch (e) {
163
268
  }
164
269
  }
165
- }
270
+ };
166
271
 
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();
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;
173
280
 
174
- if (ctx == null || args[0] == null) {
175
- return original.apply(this, arguments);
176
- }
281
+ if (conf.sql_enabled === false) {
282
+ return;
283
+ }
177
284
 
178
- if (args[0].sql == null && typeof args[0] != 'string') {
179
- return original.apply(this, arguments);
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;
292
+ }
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
+ }
180
316
  }
181
317
 
182
- // AsyncResource 생성
183
- const asyncResource = new AsyncResource('mysql-query');
184
- const callbackResource = new AsyncResource('mysql-callback');
185
-
186
- return asyncResource.runInAsyncScope(() => {
187
- ctx.start_time = Date.now();
188
-
189
- // DB 연결 패킷 전송 (elapsed = 0으로 설정)
190
- ctx.elapsed = 0;
191
- AsyncSender.send_packet(PacketTypeEnum.TX_DB_CONN, ctx, [dbcUrl]);
192
-
193
- // SQL 시작 시간 기록
194
- var sql_start_time = Date.now();
195
- ctx.footprint('MySql Query Start');
318
+ aop.both(ret, 'query', queryHook(dbc, self.agent), function (obj, args, ret, lctx) {
319
+ var ctx = lctx.context;
320
+ TraceContextManager.resume(ctx);
321
+ });
196
322
 
197
- var sql = args.length > 0 ? args[0] : undefined;
198
- var values = args.length > 1 ? args[1] : undefined;
199
- var finalSql = '';
323
+ aop.both(ret, 'execute', queryHook(dbc, self.agent), function (obj, args, ret, lctx) {
324
+ var ctx = lctx.context;
325
+ TraceContextManager.resume(ctx);
326
+ });
200
327
 
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
- }
328
+ aop.after(ret._protocol, '_delegateError', errorDelegate());
229
329
 
230
- if (typeof finalSql !== 'string' || finalSql.length === 0) {
231
- finalSql = '';
232
- }
330
+ var ctx = lctx.context,
331
+ step = lctx.step;
233
332
 
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
- }
333
+ if (ctx == null || step == null) {
334
+ return;
335
+ }
245
336
 
246
- var sqlHash = psql ? psql.sql : 0;
247
- ctx.active_sqlhash = true;
337
+ TraceContextManager.resume(ctx);
338
+ ctx.footprint('MySql Connecting Done');
339
+ ctx.db_opening = false;
340
+ step.hash = dbc_hash;
248
341
 
249
- // 쿼리 콜백 처리 함수 - AsyncResource로 바인딩
250
- function queryCallback(err, results) {
251
- // AsyncResource 스코프 내에서 실행되므로 컨텍스트 유지됨
342
+ aop.both(ret, 'connect', function (obj, args) {
252
343
  var ctx = TraceContextManager.getCurrentContext();
253
344
  if (ctx == null) {
254
345
  return;
255
346
  }
256
347
 
257
- TraceContextManager.resume(ctx.id);
258
-
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
- }
348
+ ctx.footprint('MySql Connecting Start');
266
349
 
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;
275
- }
276
- }
350
+ ctx.db_opening = true;
277
351
 
278
- try {
279
- TraceSQL.isSlowSQL(ctx);
280
- if (!err && results && psql && psql.type === 'S') {
281
- TraceSQL.isTooManyRecords(resultCount, ctx);
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;
282
357
  }
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');
358
+ TraceContextManager.resume(ctx._id);
359
+ });
360
+ ctx.profile.push(step);
292
361
  }
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;
362
+ ,
363
+ function (obj, args, ret, lctx) {
364
+ if (lctx.context) {
365
+ ctx.db_opening = false;
366
+ ctx.footprint('MySql Connecting Done');
314
367
  }
315
368
  }
369
+ );
370
+ });
316
371
 
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
- }
337
372
 
338
- try {
339
- return original.apply(this, args);
340
- } catch (queryError) {
341
- queryCallback(queryError, null);
342
- throw queryError;
343
- }
344
- });
345
- };
346
- };
347
- };
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
+ }
348
385
 
349
- // getConnection 래핑 함수
350
- var wrapGetConnection = function(agent, mysqlModule) {
351
- return function(original) {
352
- return function wrappedGetConnection() {
353
- var args = Array.prototype.slice.call(arguments);
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) {
354
392
  var ctx = TraceContextManager.getCurrentContext();
355
-
356
393
  if (ctx == null) {
357
- return original.apply(this, args);
394
+ return;
358
395
  }
359
396
 
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
- }
388
- }
397
+ aop.functionHook(args, -1, function (obj, args) {
398
+ TraceContextManager.resume(ctx._id);
399
+ DataTextAgent.DBC.add(dbc_hash, dbc);
389
400
 
390
- return original.apply(this, args);
401
+ if (args[0] != null) {
402
+ return;
403
+ }
404
+ var conn = args[1];
405
+ if (conn.__query_hook__) {
406
+ return;
407
+ }
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));
391
412
  });
392
- };
393
- };
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
+ });
394
421
  };
395
422
 
396
- // 유틸리티 함수들
397
423
  var checkedSql = new IntKeyMap(2000).setMax(2000);
398
424
  var nonLiteSql = new IntKeyMap(5000).setMax(5000);
399
425
  var date = DateUtil.yyyymmdd();
@@ -426,6 +452,8 @@ function escapeLiteral(sql) {
426
452
  els.process();
427
453
 
428
454
  var hash = HashUtil.hashFromString(els.getParsedSql());
455
+ DataTextAgent.SQL.add(hash, els.getParsedSql());
456
+
429
457
  if (hash === sqlHash) {
430
458
  psql = new ParsedSql(els.sqlType, hash, null);
431
459
  nonLiteSql.put(sqlHash, psql);
@@ -436,139 +464,4 @@ function escapeLiteral(sql) {
436
464
  return psql;
437
465
  }
438
466
 
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
-
574
467
  exports.MysqlObserver = MysqlObserver;