whatap 1.0.2 → 1.0.3-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/README.md +77 -33
  2. package/agent/darwin/arm64/whatap_nodejs +0 -0
  3. package/agent/linux/amd64/whatap_nodejs +0 -0
  4. package/agent/linux/arm64/whatap_nodejs +0 -0
  5. package/build.txt +4 -0
  6. package/lib/conf/config-default.js +3 -10
  7. package/lib/conf/configure.js +349 -369
  8. package/lib/conf/license.js +1 -1
  9. package/lib/control/packagectr-helper.js +3 -34
  10. package/lib/core/agent.js +888 -176
  11. package/lib/core/interceptor.js +6 -6
  12. package/lib/core/shimmer.js +36 -82
  13. package/lib/counter/counter-manager.js +8 -79
  14. package/lib/counter/task/activetransaction.js +17 -68
  15. package/lib/io/data-inputx.js +3 -13
  16. package/lib/io/data-outputx.js +206 -268
  17. package/lib/logger.js +6 -6
  18. package/lib/net/security-master.js +20 -139
  19. package/lib/observers/apollo-server-observer.js +27 -33
  20. package/lib/observers/global-observer.js +80 -155
  21. package/lib/observers/http-observer.js +236 -666
  22. package/lib/observers/ioredis-observer.js +294 -0
  23. package/lib/observers/maria-observer.js +362 -204
  24. package/lib/observers/mongodb-observer.js +226 -169
  25. package/lib/observers/mongoose-observer.js +323 -518
  26. package/lib/observers/mssql-observer.js +418 -177
  27. package/lib/observers/mysql-observer.js +449 -342
  28. package/lib/observers/mysql2-observer.js +358 -396
  29. package/lib/observers/oracle-observer.js +384 -559
  30. package/lib/observers/pgsql-observer.js +489 -231
  31. package/lib/observers/prisma-observer.js +92 -303
  32. package/lib/observers/process-observer.js +35 -79
  33. package/lib/observers/redis-observer.js +331 -166
  34. package/lib/observers/socket.io-observer.js +187 -226
  35. package/lib/observers/websocket-observer.js +301 -175
  36. package/lib/pack/counter-pack.js +0 -3
  37. package/lib/pack/log-sink-pack.js +52 -14
  38. package/lib/pack/tagcount-pack.js +4 -4
  39. package/lib/{counter/task → system}/gc-action.js +74 -27
  40. package/lib/trace/trace-context-manager.js +25 -113
  41. package/lib/trace/trace-context.js +7 -21
  42. package/lib/trace/trace-httpc.js +11 -17
  43. package/lib/trace/trace-sql.js +21 -29
  44. package/lib/udp/async_sender.js +119 -0
  45. package/lib/udp/index.js +17 -0
  46. package/lib/udp/packet_enum.js +52 -0
  47. package/lib/udp/packet_queue.js +69 -0
  48. package/lib/udp/packet_type_enum.js +33 -0
  49. package/lib/udp/param_def.js +72 -0
  50. package/lib/udp/udp_session.js +336 -0
  51. package/lib/util/escape-literal-sql.js +5 -5
  52. package/lib/util/hashutil.js +18 -18
  53. package/lib/util/keygen.js +3 -0
  54. package/lib/util/linkedset.js +2 -1
  55. package/lib/util/nodeutil.js +1 -2
  56. package/lib/util/sql-util.js +178 -0
  57. package/lib/util/trace-helper.js +91 -0
  58. package/lib/util/transfer.js +58 -0
  59. package/lib/value/map-value.js +2 -3
  60. package/package.json +5 -9
  61. package/whatap.conf +4 -1
  62. package/lib/conf/conf-sys-mon.js +0 -101
  63. package/lib/control/cmd-config.js +0 -24
  64. package/lib/control/control-handler.js +0 -367
  65. package/lib/core/request-agent.js +0 -27
  66. package/lib/counter/meter/meter-activex.js +0 -67
  67. package/lib/counter/meter/meter-httpc.js +0 -57
  68. package/lib/counter/meter/meter-resource.js +0 -9
  69. package/lib/counter/meter/meter-service.js +0 -168
  70. package/lib/counter/meter/meter-socket.io.js +0 -51
  71. package/lib/counter/meter/meter-sql.js +0 -71
  72. package/lib/counter/meter/meter-users.js +0 -58
  73. package/lib/counter/meter.js +0 -183
  74. package/lib/counter/task/agentinfo.js +0 -107
  75. package/lib/counter/task/gcstat.js +0 -34
  76. package/lib/counter/task/heapmem.js +0 -25
  77. package/lib/counter/task/httpc.js +0 -76
  78. package/lib/counter/task/metering-info.js +0 -125
  79. package/lib/counter/task/proc-cpu.js +0 -29
  80. package/lib/counter/task/realtimeuser.js +0 -31
  81. package/lib/counter/task/res/systemECSTask.js +0 -39
  82. package/lib/counter/task/res/systemKubeTask.js +0 -53
  83. package/lib/counter/task/res/util/awsEcsClientThread.js +0 -218
  84. package/lib/counter/task/res/util/linuxProcStatUtil.js +0 -14
  85. package/lib/counter/task/res-sys-cpu.js +0 -62
  86. package/lib/counter/task/service.js +0 -202
  87. package/lib/counter/task/socketio.js +0 -30
  88. package/lib/counter/task/sql.js +0 -105
  89. package/lib/counter/task/systemperf.js +0 -43
  90. package/lib/data/datapack-sender.js +0 -289
  91. package/lib/data/dataprofile-agent.js +0 -162
  92. package/lib/data/datatext-agent.js +0 -135
  93. package/lib/data/event-level.js +0 -15
  94. package/lib/data/test.js +0 -49
  95. package/lib/data/zipprofile.js +0 -197
  96. package/lib/env/constants.js +0 -21
  97. package/lib/error/error-handler.js +0 -437
  98. package/lib/kube/kube-client.js +0 -144
  99. package/lib/lang/text-types.js +0 -58
  100. package/lib/logsink/line-log-util.js +0 -87
  101. package/lib/logsink/line-log.js +0 -12
  102. package/lib/logsink/log-sender.js +0 -78
  103. package/lib/logsink/log-tracer.js +0 -40
  104. package/lib/logsink/sender-util.js +0 -56
  105. package/lib/logsink/zip/zip-send.js +0 -177
  106. package/lib/net/netflag.js +0 -55
  107. package/lib/net/receiver.js +0 -66
  108. package/lib/net/sender.js +0 -141
  109. package/lib/net/tcp-return.js +0 -18
  110. package/lib/net/tcp-session.js +0 -286
  111. package/lib/net/tcpreq-client-proxy.js +0 -70
  112. package/lib/net/tcprequest-mgr.js +0 -58
  113. package/lib/observers/cluster-observer.js +0 -22
  114. package/lib/observers/express-observer.js +0 -215
  115. package/lib/observers/file-observer.js +0 -184
  116. package/lib/observers/grpc-observer.js +0 -336
  117. package/lib/observers/memcached-observer.js +0 -56
  118. package/lib/observers/mongo-observer.js +0 -317
  119. package/lib/observers/net-observer.js +0 -77
  120. package/lib/observers/promise-observer.js +0 -31
  121. package/lib/observers/schedule-observer.js +0 -67
  122. package/lib/observers/stream-observer.js +0 -19
  123. package/lib/observers/thrift-observer.js +0 -197
  124. package/lib/pack/activestack-pack.js +0 -55
  125. package/lib/pack/apenum.js +0 -8
  126. package/lib/pack/errorsnap-pack.js +0 -69
  127. package/lib/pack/event-pack.js +0 -54
  128. package/lib/pack/hitmap-pack.js +0 -63
  129. package/lib/pack/hitmap-pack1.js +0 -152
  130. package/lib/pack/netstat.js +0 -15
  131. package/lib/pack/otype.js +0 -7
  132. package/lib/pack/profile-pack.js +0 -49
  133. package/lib/pack/realtimeuser-pack.js +0 -41
  134. package/lib/pack/stat-general-pack.js +0 -96
  135. package/lib/pack/staterror-pack.js +0 -120
  136. package/lib/pack/stathttpc-pack.js +0 -66
  137. package/lib/pack/stathttpc-rec.js +0 -78
  138. package/lib/pack/statremote-pack.js +0 -46
  139. package/lib/pack/statservice-pack.js +0 -63
  140. package/lib/pack/statservice-pack1.js +0 -88
  141. package/lib/pack/statservice-rec.js +0 -292
  142. package/lib/pack/statservice-rec_dep.js +0 -151
  143. package/lib/pack/statsql-pack.js +0 -69
  144. package/lib/pack/statsql-rec.js +0 -100
  145. package/lib/pack/statuseragent-pack.js +0 -44
  146. package/lib/pack/tagctr.js +0 -15
  147. package/lib/pack/text-pack.js +0 -50
  148. package/lib/pack/time-count.js +0 -25
  149. package/lib/pack/websocket.js +0 -15
  150. package/lib/pack/zip-pack.js +0 -70
  151. package/lib/pii/pii-item.js +0 -31
  152. package/lib/pii/pii-mask.js +0 -174
  153. package/lib/plugin/plugin-loadermanager.js +0 -57
  154. package/lib/plugin/plugin.js +0 -75
  155. package/lib/service/tx-record.js +0 -332
  156. package/lib/stat/stat-error.js +0 -116
  157. package/lib/stat/stat-httpc.js +0 -98
  158. package/lib/stat/stat-remote-ip.js +0 -46
  159. package/lib/stat/stat-remote-ipurl.js +0 -88
  160. package/lib/stat/stat-sql.js +0 -113
  161. package/lib/stat/stat-tranx.js +0 -58
  162. package/lib/stat/stat-tx-caller.js +0 -160
  163. package/lib/stat/stat-tx-domain.js +0 -111
  164. package/lib/stat/stat-tx-referer.js +0 -112
  165. package/lib/stat/stat-useragent.js +0 -48
  166. package/lib/stat/timingsender.js +0 -76
  167. package/lib/step/activestack-step.js +0 -38
  168. package/lib/step/dbc-step.js +0 -36
  169. package/lib/step/http-stepx.js +0 -67
  170. package/lib/step/message-step.js +0 -40
  171. package/lib/step/method-stepx.js +0 -45
  172. package/lib/step/resultset-step.js +0 -40
  173. package/lib/step/securemsg-step.js +0 -44
  174. package/lib/step/socket-step.js +0 -46
  175. package/lib/step/sql-stepx.js +0 -68
  176. package/lib/step/sqlxtype.js +0 -16
  177. package/lib/step/step.js +0 -66
  178. package/lib/step/stepenum.js +0 -54
  179. package/lib/topology/link.js +0 -63
  180. package/lib/topology/nodeinfo.js +0 -123
  181. package/lib/topology/status-detector.js +0 -111
  182. package/lib/util/anylist.js +0 -103
  183. package/lib/util/cardinality/hyperloglog.js +0 -106
  184. package/lib/util/cardinality/murmurhash.js +0 -31
  185. package/lib/util/cardinality/registerset.js +0 -75
  186. package/lib/util/errordata.js +0 -21
  187. package/lib/util/iputil_x.js +0 -527
  188. package/lib/util/kube-util.js +0 -73
  189. package/lib/util/paramsecurity.js +0 -80
  190. package/lib/util/pre-process.js +0 -13
  191. package/lib/util/process-seq.js +0 -166
  192. package/lib/util/property-util.js +0 -36
  193. package/lib/util/request-queue.js +0 -70
  194. package/lib/util/requestdouble-queue.js +0 -72
  195. package/lib/util/resourceprofile.js +0 -157
  196. package/lib/util/stop-watch.js +0 -30
  197. package/lib/util/system-util.js +0 -10
  198. package/lib/util/userid-util.js +0 -57
@@ -5,258 +5,277 @@
5
5
  */
6
6
 
7
7
  var TraceContextManager = require('../trace/trace-context-manager'),
8
- ParsedSql = require('../trace/parsed-sql'),
9
- SqlStepX = require('../step/sql-stepx'),
10
- DBCStep = require('../step/dbc-step'),
11
- ResultSetStep = require('../step/resultset-step'),
12
- DataTextAgent = require('../data/datatext-agent'),
13
- StatSql = require('../stat/stat-sql'),
14
- MeterSql = require('../counter/meter/meter-sql'),
15
- conf = require('../conf/configure'),
16
- IntKeyMap = require('../util/intkey-map'),
17
- EscapeLiteralSQL = require('../util/escape-literal-sql'),
18
- HashUtil = require('../util/hashutil'),
19
- StatError = require('../stat/stat-error'),
20
- TextTypes = require('../lang/text-types'),
21
- ParamSecurity = require('../util/paramsecurity'),
22
- Logger = require('../logger'),
23
- DateUtil = require('../util/dateutil'),
24
- Buffer = require('buffer').Buffer,
25
- shimmer = require('../core/shimmer'),
26
- TraceSQL = require('../trace/trace-sql');
8
+ ParsedSql = require('../trace/parsed-sql'),
9
+ conf = require('../conf/configure'),
10
+ IntKeyMap = require('../util/intkey-map'),
11
+ EscapeLiteralSQL = require('../util/escape-literal-sql'),
12
+ HashUtil = require('../util/hashutil'),
13
+ Logger = require('../logger'),
14
+ DateUtil = require('../util/dateutil'),
15
+ AsyncSender = require('../udp/async_sender'),
16
+ PacketTypeEnum = require('../udp/packet_type_enum'),
17
+ shimmer = require('../core/shimmer'),
18
+ TraceSQL = require('../trace/trace-sql');
19
+ const { AsyncResource } = require('async_hooks');
27
20
 
28
21
  var MariaObserver = function (agent) {
29
22
  this.agent = agent;
30
23
  this.packages = ['mariadb'];
31
24
  };
32
25
 
33
- var dbc, dbc_hash;
34
- MariaObserver.prototype.inject = function (mod, moduleName) {
35
- if (mod.__whatap_observe__) { return; }
36
- mod.__whatap_observe__ = true;
37
-
38
- Logger.initPrint("MariaObserver");
39
-
40
- if (!conf.sql_enabled) return;
41
-
42
- // shimmer.wrap(mod, 'createConnection', wrapCreateConnection(mod));
43
- shimmer.wrap(mod, 'createConnection', wrapConnection(mod));
44
- shimmer.wrap(mod, 'createPool', wrapCreatePool(mod));
45
- };
46
-
47
- var _finishQuery = function (ctx, sql_step) {
48
- sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
49
- TraceSQL.isSlowSQL(ctx);
50
-
51
- MeterSql.add(sql_step.hash, sql_step.elapsed, false);
52
- StatSql.addSqlTime(ctx, ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, false, 0);
53
- };
54
-
55
- var _handleError = function (ctx, sql_step, err) {
56
- sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
57
- TraceSQL.isSlowSQL(ctx);
58
-
59
- MeterSql.add(sql_step.hash, sql_step.elapsed, false);
60
- StatSql.addSqlTime(ctx, ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, true, 0);
61
- };
26
+ var dbc_hash = 0;
27
+ var dbc = 'mariadb://';
62
28
 
63
- function wrapCreateConnection(agent) {
64
- return function (original) {
65
- return async function (...args) {
66
- const ctx = TraceContextManager.getCurrentContext();
67
- if (!ctx || ctx.db_opening) return original.apply(this, args);
29
+ // 후킹된 객체 추적
30
+ var hookedInstances = new WeakSet();
68
31
 
69
- ctx.db_opening = true;
70
- ctx.footprint('Maria Connecting Start');
32
+ function handleSqlError(ctx, err, sqlHash) {
33
+ if (!err) return;
71
34
 
72
- // const dbc_step = new DBCStep();
73
- // dbc_step.start_time = ctx.getElapsedTime();
35
+ try {
36
+ var errorClass = err.code || err.name || err.constructor?.name || 'MariaDBError';
37
+ var errorMessage = err.message || err.sqlMessage || 'mariadb error';
38
+ var errorStack = '';
39
+
40
+ if (conf.trace_sql_error_stack && conf.trace_sql_error_depth && err.stack) {
41
+ var traceDepth = conf.trace_sql_error_depth;
42
+ var stackLines = err.stack.split("\n");
43
+ if (stackLines.length > traceDepth) {
44
+ stackLines = stackLines.slice(0, traceDepth + 1);
45
+ }
46
+ errorStack = stackLines.join("\n");
47
+ ctx.error_message = errorStack;
48
+ }
74
49
 
75
- const connection = await original.apply(this, args);
76
- wrapConnectionMethods(connection, agent);
50
+ var shouldAddError = false;
51
+ if (conf._is_trace_ignore_err_cls_contains === true && errorClass &&
52
+ errorClass.indexOf(conf.trace_ignore_err_cls_contains) < 0) {
53
+ shouldAddError = true;
54
+ } else if (conf._is_trace_ignore_err_msg_contains === true && errorMessage &&
55
+ errorMessage.indexOf(conf.trace_ignore_err_msg_contains) < 0) {
56
+ shouldAddError = true;
57
+ } else if (conf._is_trace_ignore_err_cls_contains === false &&
58
+ conf._is_trace_ignore_err_msg_contains === false) {
59
+ shouldAddError = true;
60
+ }
77
61
 
78
- ctx.footprint('Maria Connecting Done');
79
- ctx.db_opening = false;
62
+ if (shouldAddError) {
63
+ if(!ctx.error) ctx.error = 1;
64
+ ctx.status = 500;
65
+ ctx.error_message = errorMessage;
66
+ ctx.errClass = errorClass;
67
+ ctx.errMessage = errorMessage;
80
68
 
81
- getDBCHash(args);
82
- // dbc_step.hash = dbc_hash;
83
- // ctx.profile.push(dbc_step);
69
+ var errors = [errorClass];
70
+ if (errorMessage) {
71
+ errors.push(errorMessage);
72
+ }
73
+ if (errorStack || err.stack) {
74
+ errors.push(errorStack || err.stack);
75
+ }
76
+ AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
77
+ }
78
+ } catch (e) {
79
+ Logger.printError('WHATAP-214', 'Error handling MariaDB error', e, false);
80
+ }
81
+ }
84
82
 
85
- return connection;
86
- };
87
- };
83
+ function setupDbcInfo(args) {
84
+ if (dbc_hash === 0 && args.length > 0) {
85
+ var info = args[0] || {};
86
+ dbc = 'mariadb://';
87
+ dbc += info.user || '';
88
+ dbc += "@";
89
+ dbc += info.host || '';
90
+ dbc += '/';
91
+ dbc += info.database || '';
92
+ dbc_hash = HashUtil.hashFromString(dbc);
93
+ }
88
94
  }
89
95
 
90
- function wrapConnection(agent) {
91
- return function (original) {
92
- return function (...args) {
93
- const connectionPromise = original.apply(this, args);
94
- getDBCHash(args);
96
+ function hookConnectionMethods(connection, agent) {
97
+ if (connection && !hookedInstances.has(connection)) {
98
+ hookedInstances.add(connection);
99
+ Logger.print('WHATAP-MARIADB-CONNECTION', 'Hooking new connection object', false);
95
100
 
96
- return connectionPromise.then(connection => {
97
- shimmer.wrap(connection, 'query', createQueryHook(agent));
98
- return connection;
99
- });
101
+ // query와 execute 메서드 후킹
102
+ if (connection.query && !connection.query.__whatap_wrapped__) {
103
+ shimmer.wrap(connection, 'query', createQueryWrapper(agent, dbc));
104
+ connection.query.__whatap_wrapped__ = true;
105
+ }
106
+ if (connection.execute && !connection.execute.__whatap_wrapped__) {
107
+ shimmer.wrap(connection, 'execute', createQueryWrapper(agent, dbc));
108
+ connection.execute.__whatap_wrapped__ = true;
100
109
  }
101
110
  }
102
111
  }
103
112
 
104
- function wrapCreatePool(agent) {
105
- return function (original) {
106
- return function (...args) {
107
- const pool = original.apply(this, args);
108
- getDBCHash(args);
109
-
110
- shimmer.wrap(pool, 'getConnection', (originalGetConnection) => {
111
- return async function (...connArgs) {
112
- const connection = await originalGetConnection.apply(this, connArgs);
113
- wrapConnectionMethods(connection, agent);
114
- return connection;
115
- };
116
- });
113
+ // 쿼리 래핑 함수
114
+ var createQueryWrapper = function(agent, dbcUrl) {
115
+ return function(original) {
116
+ return function wrappedQuery() {
117
+ var args = Array.prototype.slice.call(arguments);
118
+ var ctx = TraceContextManager.getCurrentContext();
117
119
 
118
- return pool;
119
- };
120
- };
121
- }
122
-
123
- function wrapConnectionMethods(connection, agent) {
124
- shimmer.wrap(connection, 'query', createQueryHook(agent));
125
- shimmer.wrap(connection, 'execute', createQueryHook(agent));
126
- }
120
+ if (ctx == null || args[0] == null) {
121
+ return original.apply(this, arguments);
122
+ }
127
123
 
128
- function createQueryHook(agent) {
129
- return function (original) {
130
- return function (...args) {
131
- const ctx = TraceContextManager.getCurrentContext();
132
- if (!ctx || !args[0]) return original.apply(this, args);
124
+ if (args[0].sql == null && typeof args[0] != 'string') {
125
+ return original.apply(this, arguments);
126
+ }
133
127
 
134
- ctx.db_opening = true;
135
- ctx.footprint('Maria Connecting Start');
128
+ // AsyncResource 생성
129
+ const asyncResource = new AsyncResource('mariadb-query');
136
130
 
137
- const dbc_step = new DBCStep();
138
- ctx.footprint('Maria Connecting Done');
139
- ctx.db_opening = false;
131
+ return asyncResource.runInAsyncScope(() => {
132
+ // HTTP Observer 패턴을 따라 시작 시간 설정
133
+ ctx.start_time = Date.now();
140
134
 
141
- dbc_step.hash = dbc_hash;
142
- ctx.profile.push(dbc_step);
135
+ // DB 연결 패킷 전송 (elapsed = 0으로 설정)
136
+ ctx.elapsed = 0;
137
+ AsyncSender.send_packet(PacketTypeEnum.TX_DB_CONN, ctx, [dbcUrl]);
143
138
 
144
- const sql_step = new SqlStepX();
145
- sql_step.start_time = ctx.getElapsedTime();
146
- ctx.profile.push(sql_step);
139
+ // SQL 시작 시간 기록
140
+ var sql_start_time = Date.now();
141
+ ctx.footprint('MariaDB Query Start');
142
+ ctx.sql_count++;
147
143
 
148
- ctx.footprint('Maria Query Start');
149
- ctx.sql_count++;
144
+ var sql = args.length > 0 ? args[0] : undefined;
145
+ var params = args.length > 1 && Array.isArray(args[1]) ? args[1] : undefined;
150
146
 
151
- var sql = args.length > 0 ? args[0] : undefined,
152
- psql = null;
147
+ if (typeof sql !== 'string') {
148
+ sql = args[0].sql || undefined;
149
+ if (args[0].values && Array.isArray(args[0].values)) {
150
+ params = args[0].values;
151
+ }
152
+ }
153
153
 
154
- if(typeof sql !== 'string') {
155
- sql = args[0].sql || undefined;
156
- }
154
+ // 최종 SQL 생성 (파라미터 바인딩)
155
+ var finalSql = sql || '';
156
+ if (typeof sql === 'string' && sql.length > 0 && params && params.length > 0) {
157
+ try {
158
+ // MariaDB는 mysql2 스타일의 파라미터 바인딩 지원
159
+ const result = params.map((param) => {
160
+ if(typeof param === 'string'){
161
+ return `'${param.replace(/'/g, "''")}'` // SQL injection 방지
162
+ }
163
+ return param
164
+ }).join(', ');
165
+
166
+ // 간단한 파라미터 치환 (? 기준)
167
+ var paramIndex = 0;
168
+ finalSql = sql.replace(/\?/g, function() {
169
+ if (paramIndex < params.length) {
170
+ var param = params[paramIndex++];
171
+ if (typeof param === 'string') {
172
+ return `'${param.replace(/'/g, "''")}'`;
173
+ }
174
+ return param;
175
+ }
176
+ return '?';
177
+ });
178
+ } catch (e) {
179
+ Logger.printError('WHATAP-SQL-BINDING', 'Error binding parameters', e, false);
180
+ finalSql = sql; // 바인딩 실패 시 원본 사용
181
+ }
182
+ }
157
183
 
158
- if (typeof sql === 'string' && sql.length > 0) {
159
- try {
160
- psql = escapeLiteral(sql);
161
- } catch (e) {
162
- Logger.printError('WHATAP-191', 'MariaObserver escapeliteral error', e);
184
+ var psql = null;
185
+ if (typeof finalSql === 'string' && finalSql.length > 0) {
186
+ try {
187
+ psql = escapeLiteral(finalSql);
188
+ // Logger.print('WHATAP-SQL-DEBUG', 'Processing SQL: ' + finalSql.substring(0, 200), false);
189
+ } catch (e) {
190
+ Logger.printError('WHATAP-215', 'MariaObserver escapeliteral error', e, false);
191
+ }
192
+ } else {
193
+ finalSql = '';
194
+ psql = escapeLiteral(finalSql);
163
195
  }
164
- } else {
165
- sql = '';
166
- psql = escapeLiteral(sql);
167
- }
168
196
 
169
- if(psql != null) {
170
- sql_step.hash = psql.sql;
171
- // sql_step.crud = psql.type.charCodeAt(0);
172
- }
197
+ var sqlHash = psql ? psql.sql : 0;
198
+ ctx.active_sqlhash = true;
173
199
 
174
- var els = new EscapeLiteralSQL(sql);
175
- els.process();
200
+ const result = original.apply(this, args);
176
201
 
177
- ctx.active_sqlhash = sql_step.hash;
202
+ // Promise 기반 처리
203
+ if (result && typeof result.then === 'function') {
204
+ return result.then(res => {
205
+ var currentCtx = TraceContextManager.getCurrentContext();
206
+ if (currentCtx == null) {
207
+ currentCtx = ctx;
208
+ }
178
209
 
179
- if(conf.profile_sql_param_enabled) {
180
- var params = args.length > 1 && Array.isArray(args[1]) ? args[1] : undefined;
181
- sql_step.setTrue(1);
182
- var crc = {value : 0};
183
- sql_step.p1 = toParamBytes(psql.param, crc);
184
- if(params != undefined) {
185
- const result = params.map((param) => {
186
- if(typeof param === 'string'){
187
- return `'${param}'`
210
+ var sql_elapsed = Date.now() - sql_start_time;
211
+ var resultCount = 0;
212
+
213
+ // 결과 개수 계산
214
+ if (res) {
215
+ if (Array.isArray(res)) {
216
+ resultCount = res.length;
217
+ } else if (res.affectedRows !== undefined) {
218
+ resultCount = res.affectedRows;
219
+ } else if (res.changedRows !== undefined) {
220
+ resultCount = res.changedRows;
221
+ }
188
222
  }
189
- return param
190
- }).toString()
191
- sql_step.p2 = toParamBytes(result, crc);
192
- }
193
- sql_step.pcrc = crc.value;
194
- }
195
223
 
196
- const result = original.apply(this, args);
224
+ // TraceSQL 처리
225
+ try {
226
+ TraceSQL.isSlowSQL(currentCtx);
227
+
228
+ // ResultSet 처리 (SELECT 쿼리)
229
+ if (res && psql && psql.type === 'S') {
230
+ TraceSQL.isTooManyRecords(resultCount, currentCtx);
231
+ }
232
+ } catch (e) {
233
+ Logger.printError('WHATAP-TRACESQL', 'Error in TraceSQL processing', e, false);
234
+ }
197
235
 
198
- return result.then(res => {
199
- if (Array.isArray(res) && psql && psql.type === "S") {
200
- var result_step = new ResultSetStep();
201
- result_step.start_time = ctx.getElapsedTime();
202
- result_step.elapsed = 0;
203
- result_step.fetch = res.length;
204
- result_step.sqlhash = psql.sql;
205
- result_step.dbc = dbc_hash;
206
- ctx.profile.push(result_step);
236
+ currentCtx.elapsed = sql_elapsed;
237
+ currentCtx.active_sqlhash = false;
238
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, currentCtx, [dbcUrl, finalSql, String(resultCount)]);
207
239
 
208
- ctx.rs_count = ctx.rs_count ? ctx.rs_count + res.length : res.length;
240
+ currentCtx.footprint('MariaDB Query Done');
241
+ return res;
242
+ }).catch(err => {
243
+ var currentCtx = TraceContextManager.getCurrentContext();
244
+ if (currentCtx == null) {
245
+ currentCtx = ctx;
246
+ }
209
247
 
210
- MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
211
- StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
248
+ var sql_elapsed = Date.now() - sql_start_time;
212
249
 
213
- TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
214
- }
215
- var isSelectQuery = psql && psql.type === "S"? true : false;
216
- _finishQuery(ctx, sql_step, isSelectQuery, res);
217
- return res;
218
- }).catch(err => {
219
- _handleError(ctx, sql_step, err)
220
- if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
221
- var traceDepth = conf.trace_sql_error_depth;
222
-
223
- var errorStack = err.stack.split("\n");
224
- if (errorStack.length > traceDepth) {
225
- errorStack = errorStack.slice(0, traceDepth + 1);
226
- }
227
- ctx.error_message = errorStack.join("\n");
228
- sql_step.error = ctx.error = StatError.addError('pgsql -' + err.code, err.message, ctx.service_hash, TextTypes.SQL, null);
250
+ // 에러 처리
251
+ handleSqlError(currentCtx, err, sqlHash);
252
+
253
+ currentCtx.elapsed = sql_elapsed;
254
+ currentCtx.active_sqlhash = false;
255
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, currentCtx, [dbcUrl, finalSql, '0']);
256
+
257
+ currentCtx.footprint('MariaDB Query Error');
258
+ throw err;
259
+ });
229
260
  }
230
- throw err;
261
+
262
+ return result;
231
263
  });
232
264
  };
233
265
  };
234
- }
235
-
236
- var toParamBytes = function (p, crc) {
237
- if (p == null || p.length === 0) {
238
- return null;
239
- }
240
- try {
241
- return ParamSecurity.encrypt(Buffer.from(p, 'utf8'), crc);
242
- } catch (e) {
243
- return null;
244
- }
245
266
  };
246
267
 
247
- function getDBCHash(args) {
248
- dbc = `mariadb://${(args[0] || {}).user || ''}@${(args[0] || {}).host || ''}/${(args[0] || {}).database || ''}`;
249
- dbc_hash = HashUtil.hashFromString(dbc);
250
- }
251
-
268
+ // 유틸리티 함수들
252
269
  var checkedSql = new IntKeyMap(2000).setMax(2000);
253
270
  var nonLiteSql = new IntKeyMap(5000).setMax(5000);
254
271
  var date = DateUtil.yyyymmdd();
255
272
 
256
273
  function escapeLiteral(sql) {
257
- if(sql == null) { sql = ''; }
274
+ if (sql == null) {
275
+ sql = '';
276
+ }
258
277
 
259
- if(date !== DateUtil.yyyymmdd()) {
278
+ if (date !== DateUtil.yyyymmdd()) {
260
279
  checkedSql.clear();
261
280
  nonLiteSql.clear();
262
281
  date = DateUtil.yyyymmdd();
@@ -266,12 +285,12 @@ function escapeLiteral(sql) {
266
285
  var sqlHash = HashUtil.hashFromString(sql);
267
286
  var psql = nonLiteSql.get(sqlHash);
268
287
 
269
- if(psql != null) {
288
+ if (psql != null) {
270
289
  return psql;
271
290
  }
272
291
  psql = checkedSql.get(sqlHash);
273
292
 
274
- if(psql != null) {
293
+ if (psql != null) {
275
294
  return psql;
276
295
  }
277
296
 
@@ -279,9 +298,7 @@ function escapeLiteral(sql) {
279
298
  els.process();
280
299
 
281
300
  var hash = HashUtil.hashFromString(els.getParsedSql());
282
- DataTextAgent.SQL.add(hash, els.getParsedSql());
283
-
284
- if(hash === sqlHash) {
301
+ if (hash === sqlHash) {
285
302
  psql = new ParsedSql(els.sqlType, hash, null);
286
303
  nonLiteSql.put(sqlHash, psql);
287
304
  } else {
@@ -291,4 +308,145 @@ function escapeLiteral(sql) {
291
308
  return psql;
292
309
  }
293
310
 
311
+ // 메인 inject 함수
312
+ MariaObserver.prototype.inject = function (mod, moduleName) {
313
+ if (mod.__whatap_observe__) {
314
+ return;
315
+ }
316
+ mod.__whatap_observe__ = true;
317
+ Logger.initPrint("MariaObserver");
318
+
319
+ if (conf.sql_enabled === false) {
320
+ return;
321
+ }
322
+
323
+ var self = this;
324
+
325
+ // createConnection 래핑
326
+ if (mod.createConnection && !mod.createConnection.__whatap_wrapped__) {
327
+ shimmer.wrap(mod, 'createConnection', function(original) {
328
+ return function wrappedCreateConnection() {
329
+ var args = Array.prototype.slice.call(arguments);
330
+ var ctx = TraceContextManager.getCurrentContext();
331
+
332
+ setupDbcInfo(args);
333
+
334
+ if (ctx && !ctx.db_opening) {
335
+ ctx.db_opening = true;
336
+ ctx.footprint('MariaDB Connecting Start');
337
+ ctx.start_time = Date.now();
338
+ }
339
+
340
+ const result = original.apply(this, args);
341
+
342
+ // Promise 기반 처리
343
+ if (result && typeof result.then === 'function') {
344
+ return result.then(connection => {
345
+ hookConnectionMethods(connection, self.agent);
346
+
347
+ if (ctx) {
348
+ ctx.elapsed = Date.now() - ctx.start_time;
349
+ ctx.footprint('MariaDB Connecting Done');
350
+ ctx.db_opening = false;
351
+ }
352
+ return connection;
353
+ }).catch(error => {
354
+ if (ctx) {
355
+ ctx.elapsed = Date.now() - ctx.start_time;
356
+ ctx.footprint('MariaDB Connecting Error');
357
+ ctx.db_opening = false;
358
+ handleSqlError(ctx, error, 0);
359
+ }
360
+ throw error;
361
+ });
362
+ } else {
363
+ // 동기 처리
364
+ hookConnectionMethods(result, self.agent);
365
+
366
+ if (ctx) {
367
+ ctx.elapsed = Date.now() - ctx.start_time;
368
+ ctx.footprint('MariaDB Connecting Done');
369
+ ctx.db_opening = false;
370
+ }
371
+ }
372
+
373
+ return result;
374
+ };
375
+ });
376
+ mod.createConnection.__whatap_wrapped__ = true;
377
+ }
378
+
379
+ // createPool 래핑
380
+ if (mod.createPool && !mod.createPool.__whatap_wrapped__) {
381
+ shimmer.wrap(mod, 'createPool', function(original) {
382
+ return function wrappedCreatePool() {
383
+ var args = Array.prototype.slice.call(arguments);
384
+ setupDbcInfo(args);
385
+
386
+ var pool = original.apply(this, args);
387
+
388
+ if (pool && !hookedInstances.has(pool)) {
389
+ hookedInstances.add(pool);
390
+
391
+ // Pool의 직접 쿼리 메서드들 후킹
392
+ if (pool.query && !pool.query.__whatap_wrapped__) {
393
+ shimmer.wrap(pool, 'query', createQueryWrapper(self.agent, dbc));
394
+ pool.query.__whatap_wrapped__ = true;
395
+ }
396
+ if (pool.execute && !pool.execute.__whatap_wrapped__) {
397
+ shimmer.wrap(pool, 'execute', createQueryWrapper(self.agent, dbc));
398
+ pool.execute.__whatap_wrapped__ = true;
399
+ }
400
+
401
+ // getConnection 메서드 후킹
402
+ if (pool.getConnection && !pool.getConnection.__whatap_wrapped__) {
403
+ shimmer.wrap(pool, 'getConnection', function(original) {
404
+ return function wrappedGetConnection() {
405
+ var args = Array.prototype.slice.call(arguments);
406
+ var ctx = TraceContextManager.getCurrentContext();
407
+
408
+ if (ctx == null) {
409
+ return original.apply(this, args);
410
+ }
411
+
412
+ const connectionResource = new AsyncResource('mariadb-getConnection');
413
+
414
+ return connectionResource.runInAsyncScope(() => {
415
+ ctx.start_time = Date.now();
416
+
417
+ const result = original.apply(this, args);
418
+
419
+ // Promise 기반 처리
420
+ if (result && typeof result.then === 'function') {
421
+ return result.then(connectionResource.bind(function(connection) {
422
+ TraceContextManager.resume(ctx.id);
423
+ ctx.elapsed = Date.now() - ctx.start_time;
424
+
425
+ if (connection && !connection.__query_hook__) {
426
+ connection.__query_hook__ = true;
427
+ hookConnectionMethods(connection, self.agent);
428
+ }
429
+
430
+ return connection;
431
+ }));
432
+ }
433
+
434
+ // 동기 처리
435
+ hookConnectionMethods(result, self.agent);
436
+ ctx.elapsed = Date.now() - ctx.start_time;
437
+ return result;
438
+ });
439
+ };
440
+ });
441
+ pool.getConnection.__whatap_wrapped__ = true;
442
+ }
443
+ }
444
+
445
+ return pool;
446
+ };
447
+ });
448
+ mod.createPool.__whatap_wrapped__ = true;
449
+ }
450
+ };
451
+
294
452
  exports.MariaObserver = MariaObserver;