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,25 +5,18 @@
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
- DataTextAgent = require('../data/datatext-agent'),
12
- StatSql = require('../stat/stat-sql'),
13
- MeterSql = require('../counter/meter/meter-sql'),
14
- conf = require('../conf/configure'),
15
- IntKeyMap = require('../util/intkey-map'),
16
- EscapeLiteralSQL = require('../util/escape-literal-sql'),
17
- HashUtil = require('../util/hashutil'),
18
- StatError = require('../stat/stat-error'),
19
- TextTypes = require('../lang/text-types'),
20
- ParamSecurity = require('../util/paramsecurity'),
21
- Logger = require('../logger'),
22
- Buffer = require('buffer').Buffer,
23
- DateUtil = require('../util/dateutil'),
24
- TraceSQL = require('../trace/trace-sql');
25
- const shimmer = require('../core/shimmer');
26
- const ResultSetStep = require("../step/resultset-step");
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
+ AsyncResource = require('async_hooks').AsyncResource;
27
20
 
28
21
  var PgSqlObserver = function (agent) {
29
22
  this.agent = agent;
@@ -31,274 +24,539 @@ var PgSqlObserver = function (agent) {
31
24
  };
32
25
 
33
26
  var dbc_hash = 0;
34
- var dbc, dbc_step;
35
- PgSqlObserver.prototype.inject = function (mod, moduleName) {
36
- if (mod.__whatap_observe__) { return; }
37
- mod.__whatap_observe__ = true;
27
+ var dbc = 'postgresql://';
38
28
 
39
- Logger.initPrint("PgSqlObserver");
29
+ var hookedInstances = new WeakSet();
40
30
 
41
- var self = this;
42
- var aop = self.agent.aop;
31
+ var checkedSql = new IntKeyMap(2000).setMax(2000);
32
+ var nonLiteSql = new IntKeyMap(5000).setMax(5000);
33
+ var date = DateUtil.yyyymmdd();
43
34
 
44
- if (conf.sql_enabled === false) { return; }
35
+ function handleSqlError(ctx, err) {
36
+ if (!err) return;
45
37
 
46
- shimmer.wrap(mod.Client.prototype, 'connect', function (original) {
47
- return function () {
48
- const ctx = TraceContextManager.getCurrentContext();
49
- if (!ctx || ctx.db_opening) {
50
- return original.apply(this, arguments);
38
+ try {
39
+ var errorClass = err.code || err.name || err.constructor?.name || 'PostgreSQLError';
40
+ var errorMessage = err.message || 'postgresql error';
41
+ var errorStack = '';
42
+
43
+ if (conf.trace_sql_error_stack && conf.trace_sql_error_depth && err.stack) {
44
+ var traceDepth = conf.trace_sql_error_depth;
45
+ var stackLines = err.stack.split("\n");
46
+ if (stackLines.length > traceDepth) {
47
+ stackLines = stackLines.slice(0, traceDepth + 1);
48
+ }
49
+ errorStack = stackLines.join("\n");
50
+ }
51
+
52
+ var shouldAddError = false;
53
+ if (conf._is_trace_ignore_err_cls_contains === true && errorClass &&
54
+ errorClass.indexOf(conf.trace_ignore_err_cls_contains) < 0) {
55
+ shouldAddError = true;
56
+ } else if (conf._is_trace_ignore_err_msg_contains === true && errorMessage &&
57
+ errorMessage.indexOf(conf.trace_ignore_err_msg_contains) < 0) {
58
+ shouldAddError = true;
59
+ } else if (conf._is_trace_ignore_err_cls_contains === false &&
60
+ conf._is_trace_ignore_err_msg_contains === false) {
61
+ shouldAddError = true;
62
+ }
63
+
64
+ if (shouldAddError) {
65
+ if (!ctx.error) ctx.error = 1;
66
+ ctx.status = 500;
67
+ var errors = [errorClass];
68
+ if (errorMessage) {
69
+ errors.push(errorMessage);
70
+ }
71
+ if (errorStack || err.stack) {
72
+ errors.push(errorStack || err.stack);
51
73
  }
52
74
 
53
- ctx.pg_opening = true;
54
- ctx.footprint('PgSql Connecting Start');
75
+ AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
76
+ }
77
+ } catch (e) {
78
+ Logger.printError('WHATAP-204', 'Error handling PgSQL error', e, false);
79
+ }
80
+ }
55
81
 
56
- dbc_step = new DBCStep();
57
- dbc_step.start_time = ctx.getElapsedTime();
82
+ function setupDbcInfo(args) {
83
+ if (dbc_hash === 0 && args.length > 0) {
84
+ var info = args[0] || {};
85
+ dbc = 'postgresql://';
86
+ dbc += (info.user || '') + '@';
87
+ dbc += (info.host || '') + '/';
88
+ dbc += info.database || '';
89
+ dbc_hash = HashUtil.hashFromString(dbc);
90
+ }
91
+ }
58
92
 
59
- const finishConnect = function (param) {
60
- if (ctx) {
61
- if (dbc_hash === 0) {
62
- const info = param.connectionParameters;
63
- dbc = 'postgresql://';
64
- dbc += (info.user || '') + '@';
65
- dbc += (info.host || '') + '/';
66
- dbc += info.database || '';
67
-
68
- dbc_hash = HashUtil.hashFromString(dbc);
69
-
70
- // DataTextAgent.DBC.add(dbc_hash, dbc);
71
- // DataTextAgent.METHOD.add(dbc_hash, dbc);
72
- // DataTextAgent.ERROR.add(dbc_hash, dbc);
73
- }
74
- //
75
- // dbc_step.hash = dbc_hash;
76
- // dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
77
- //
78
- // ctx.db_opening = false;
79
- // ctx.footprint('PgSql Connecting Done');
80
- // ctx.profile.push(dbc_step);
81
- }
82
- };
93
+ function hookConnectionMethods(connection, agent) {
94
+ if (connection && !hookedInstances.has(connection)) {
95
+ hookedInstances.add(connection);
96
+ Logger.print('WHATAP-201', 'Hooking new connection object', false);
97
+
98
+ if (dbc_hash === 0 && connection.connectionParameters) {
99
+ var info = connection.connectionParameters;
100
+ dbc = 'postgresql://';
101
+ dbc += (info.user || '') + '@';
102
+ dbc += (info.host || '') + '/';
103
+ dbc += info.database || '';
104
+ dbc_hash = HashUtil.hashFromString(dbc);
105
+ }
106
+
107
+ if (connection.query && !connection.query.__whatap_wrapped__) {
108
+ shimmer.wrap(connection, 'query', createQueryWrapper(agent, dbc));
109
+ connection.query.__whatap_wrapped__ = true;
110
+ }
111
+ }
112
+ }
83
113
 
84
- try {
85
- const result = original.apply(this, arguments);
114
+ function escapeLiteral(sql) {
115
+ if (sql == null) {
116
+ sql = '';
117
+ }
86
118
 
87
- if (result && result.then) {
88
- result.then(finishConnect(this));
89
- } else {
90
- finishConnect(this);
91
- }
119
+ if (date !== DateUtil.yyyymmdd()) {
120
+ checkedSql.clear();
121
+ nonLiteSql.clear();
122
+ date = DateUtil.yyyymmdd();
123
+ }
92
124
 
93
- } catch (err) {
94
- ctx.db_opening = false;
95
- Logger.printError('PgSqlObserver', 'Connect Error', err);
96
- throw err;
97
- }
98
- };
99
- });
125
+ var sqlHash = HashUtil.hashFromString(sql);
126
+ var psql = nonLiteSql.get(sqlHash);
127
+
128
+ if (psql != null) {
129
+ return psql;
130
+ }
131
+ psql = checkedSql.get(sqlHash);
132
+
133
+ if (psql != null) {
134
+ return psql;
135
+ }
100
136
 
101
- shimmer.wrap(mod.Client.prototype, 'query', function (original) {
102
- return function () {
103
- const ctx = TraceContextManager.getCurrentContext();
104
- if (!ctx) {
137
+ var els = new EscapeLiteralSQL(sql);
138
+ els.process();
139
+
140
+ var hash = HashUtil.hashFromString(els.getParsedSql());
141
+
142
+ if (hash === sqlHash) {
143
+ psql = new ParsedSql(els.sqlType, hash, null);
144
+ nonLiteSql.put(sqlHash, psql);
145
+ } else {
146
+ psql = new ParsedSql(els.sqlType, hash, els.getParameter());
147
+ checkedSql.put(sqlHash, psql);
148
+ }
149
+ return psql;
150
+ }
151
+
152
+ var createQueryWrapper = function(agent, dbcUrl) {
153
+ return function(original) {
154
+ return function wrappedQuery() {
155
+ var args = Array.prototype.slice.call(arguments);
156
+ var ctx = TraceContextManager.getCurrentContext();
157
+
158
+ if (ctx == null || args[0] == null) {
105
159
  return original.apply(this, arguments);
106
160
  }
107
161
 
108
- if(dbc && dbc_hash){
109
- if(!dbc_step){
110
- dbc_step = new DBCStep();
111
- dbc_step.start_time = ctx.getElapsedTime();
112
- }
113
- dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
114
- dbc_step.hash = dbc_hash;
162
+ if (dbcUrl === 'postgresql://' || !dbc_hash) {
163
+ return original.apply(this, arguments);
164
+ }
115
165
 
116
- DataTextAgent.DBC.add(dbc_hash, dbc);
117
- DataTextAgent.METHOD.add(dbc_hash, dbc);
118
- DataTextAgent.ERROR.add(dbc_hash, dbc);
166
+ const asyncResource = new AsyncResource('pg-query');
167
+ const callbackResource = new AsyncResource('pg-callback');
119
168
 
120
- ctx.pg_opening = false;
121
- ctx.footprint('PgSql Connecting Done');
122
- ctx.profile.push(dbc_step);
123
- }
124
- dbc_step = null;
169
+ return asyncResource.runInAsyncScope(() => {
170
+ ctx.start_time = Date.now();
171
+ ctx.elapsed = 0;
172
+ ctx.active_sqlhash = true;
173
+ AsyncSender.send_packet(PacketTypeEnum.TX_DB_CONN, ctx, [dbcUrl]);
125
174
 
126
- var sql_step = new SqlStepX();
127
- sql_step.start_time = ctx.getElapsedTime();
128
- ctx.profile.push(sql_step);
129
- ctx.footprint('PgSql Query Start');
175
+ var sql_start_time = Date.now();
176
+ ctx.footprint('PgSql Query Start');
177
+ ctx.sql_count++;
130
178
 
131
- ctx.vvv++;
179
+ var sql = args.length > 0 ? args[0] : undefined;
180
+ var params = null;
181
+ var finalSql = '';
132
182
 
133
- let sql = arguments[0];
134
- let psql = null;
135
- if (typeof sql === 'string' && sql.length > 0) {
136
183
  try {
137
- psql = escapeLiteral(sql);
138
- } catch (e) {
139
- Logger.printError('WHATAP-191', 'PgSqlObserver escapeliteral error', e);
184
+ if (sql !== null && typeof sql === 'object') {
185
+ finalSql = sql.text || sql.query || '';
186
+ params = sql.values || null;
187
+ } else if (typeof sql === 'string') {
188
+ finalSql = sql;
189
+ params = args.length > 1 && Array.isArray(args[1]) ? args[1] : null;
190
+ }
191
+
192
+ if (typeof finalSql === 'string' && finalSql.length > 0 && params && Array.isArray(params) && params.length > 0) {
193
+ params.forEach((param, index) => {
194
+ const placeholder = '$' + (index + 1);
195
+ const value = param === null ? 'NULL' :
196
+ typeof param === 'string' ? `'${param.replace(/'/g, "''")}'` :
197
+ String(param);
198
+ finalSql = finalSql.replace(new RegExp('\\' + placeholder + '\\b', 'g'), value);
199
+ });
200
+ }
201
+ } catch (formatError) {
202
+ Logger.printError('WHATAP-205', 'Error formatting PostgreSQL query', formatError, false);
203
+ if (typeof sql === 'string') {
204
+ finalSql = sql;
205
+ } else if (sql && sql.text) {
206
+ finalSql = sql.text;
207
+ }
140
208
  }
141
- } else {
142
- sql = '';
143
- psql = escapeLiteral(sql);
144
- }
145
209
 
146
- if(psql != null) {
147
- sql_step.hash = psql.sql;
148
- }
149
- sql_step.dbc = dbc_hash;
150
-
151
- var els = new EscapeLiteralSQL(sql);
152
- els.process();
153
- ctx.active_sqlhash = sql_step.hash;
154
- ctx.active_dbc = sql_step.dbc;
155
- //ctx.active_crud = sql_step.crud;
156
-
157
- if(conf.profile_sql_param_enabled) {
158
- var params = arguments.length > 1 && Array.isArray(arguments[1]) ? arguments[1] : undefined;
159
- sql_step.setTrue(1);
160
- var crc = {value : 0};
161
- sql_step.p1 = toParamBytes(psql.param, crc);
162
- if(params != undefined) {
163
- const result = params.map((param) => {
164
- if(typeof param === 'string'){
165
- return `'${param}'`
166
- }
167
- return param
168
- }).toString()
169
- sql_step.p2 = toParamBytes(result, crc);
210
+ if (typeof finalSql !== 'string' || finalSql.length === 0) {
211
+ finalSql = '';
212
+ }
213
+
214
+ var psql = null;
215
+ if (typeof finalSql === 'string' && finalSql.length > 0) {
216
+ try {
217
+ psql = escapeLiteral(finalSql);
218
+ Logger.print('WHATAP-202', 'Processing SQL: ' + finalSql.substring(0, 200), false);
219
+ } catch (e) {
220
+ Logger.printError('WHATAP-206', 'PgSqlObserver escapeliteral error', e, false);
221
+ }
222
+ } else {
223
+ psql = escapeLiteral(finalSql);
170
224
  }
171
- sql_step.pcrc = crc.value;
172
- }
173
225
 
174
- try {
175
- const result = original.apply(this, arguments);
226
+ var sqlHash = psql ? psql.sql : 0;
227
+ ctx.active_sqlhash = sqlHash;
228
+ ctx.active_dbc = dbc_hash;
176
229
 
177
- if (result && result.then) {
178
- return result.then(res => {
179
- if(res.command && res.command === 'SELECT'){
180
- var result_step = new ResultSetStep();
181
- result_step.start_time = ctx.getElapsedTime();
182
- result_step.elapsed = 0;
183
- result_step.fetch = res.rowCount;
184
- result_step.sqlhash = psql.sql;
185
- result_step.dbc = dbc_hash;
186
- ctx.profile.push(result_step);
230
+ function queryCallback(err, results) {
231
+ var currentCtx = TraceContextManager.getCurrentContext();
232
+ if (currentCtx == null) {
233
+ return;
234
+ }
187
235
 
188
- ctx.rs_count = ctx.rs_count ? ctx.rs_count + res.rowCount : res.rowCount;
236
+ TraceContextManager.resume(currentCtx.id);
189
237
 
190
- MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
191
- StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
238
+ var sql_elapsed = Date.now() - sql_start_time;
239
+ var resultCount = 0;
192
240
 
193
- TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
241
+ if (err) {
242
+ handleSqlError(currentCtx, err);
243
+ }
244
+
245
+ if (!err && results) {
246
+ if (results.rowCount !== undefined) {
247
+ resultCount = results.rowCount;
248
+ } else if (results.rows && Array.isArray(results.rows)) {
249
+ resultCount = results.rows.length;
194
250
  }
195
- self._finishQuery(ctx, sql_step);
196
- return res;
197
- }).catch(err => {
198
- self._handleError(ctx, sql_step, err)
199
- if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
200
- var traceDepth = conf.trace_sql_error_depth;
201
-
202
- var errorStack = err.stack.split("\n");
203
- if (errorStack.length > traceDepth) {
204
- errorStack = errorStack.slice(0, traceDepth + 1);
205
- }
206
- ctx.error_message = errorStack.join("\n");
207
- sql_step.error = ctx.error = StatError.addError('pgsql -' + err.code, err.message, ctx.service_hash, TextTypes.SQL, null);
251
+ }
252
+
253
+ try {
254
+ TraceSQL.isSlowSQL(currentCtx);
255
+ if (results && results.command && results.command === 'SELECT' && psql != null && psql.type === 'S') {
256
+ TraceSQL.isTooManyRecords(resultCount, currentCtx);
208
257
  }
209
- throw err;
210
- });
211
- } else {
212
- const callback = arguments[arguments.length - 1];
213
- if (typeof callback === 'function') {
214
- arguments[arguments.length - 1] = function (err, res) {
215
- if (err) {
216
- self._handleError(ctx, sql_step, err);
217
- } else {
218
- self._finishQuery(ctx, sql_step);
258
+ } catch (e) {
259
+ Logger.printError('WHATAP-207', 'Error in TraceSQL processing', e, false);
260
+ }
261
+
262
+ currentCtx.elapsed = sql_elapsed;
263
+ currentCtx.active_sqlhash = false;
264
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, currentCtx, [dbcUrl, finalSql, String(resultCount)]);
265
+
266
+ currentCtx.footprint('PgSql Query Done');
267
+ }
268
+
269
+ var callbackWrapped = false;
270
+ var hasCallback = false;
271
+
272
+ for (var i = args.length - 1; i >= 0; i--) {
273
+ if (typeof args[i] === 'function') {
274
+ hasCallback = true;
275
+ var originalCallback = args[i];
276
+ args[i] = callbackResource.bind(function() {
277
+ var callbackArgs = Array.prototype.slice.call(arguments);
278
+ try {
279
+ queryCallback(callbackArgs[0], callbackArgs[1]);
280
+ } catch (e) {
281
+ Logger.printError('WHATAP-208', 'Error in callback hook', e, false);
219
282
  }
220
- return callback.apply(this, arguments);
221
- };
283
+ if (originalCallback && typeof originalCallback === 'function') {
284
+ return originalCallback.apply(this, callbackArgs);
285
+ }
286
+ });
287
+ callbackWrapped = true;
288
+ break;
289
+ }
290
+ }
291
+
292
+ try {
293
+ const result = original.apply(this, args);
294
+
295
+ // Promise 기반 처리
296
+ if (!hasCallback && result && typeof result.then === 'function') {
297
+ return result.then(function(res) {
298
+ queryCallback(null, res);
299
+ return res;
300
+ }).catch(function(err) {
301
+ queryCallback(err, null);
302
+ throw err;
303
+ });
222
304
  }
305
+
223
306
  return result;
307
+ } catch (queryError) {
308
+ queryCallback(queryError, null);
309
+ throw queryError;
224
310
  }
225
- } catch (err) {
226
- self._handleError(ctx, sql_step, err);
227
- throw err;
228
- }
311
+ });
229
312
  };
230
- });
313
+ };
231
314
  };
232
315
 
233
- PgSqlObserver.prototype._finishQuery = function (ctx, sql_step) {
234
- sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
235
- TraceSQL.isSlowSQL(ctx);
316
+ var wrapPoolConnect = function(agent) {
317
+ return function(original) {
318
+ return function wrappedPoolConnect() {
319
+ var args = Array.prototype.slice.call(arguments);
320
+ var ctx = TraceContextManager.getCurrentContext();
236
321
 
237
- MeterSql.add(sql_step.hash, sql_step.elapsed, false);
238
- StatSql.addSqlTime(ctx, ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, false, 0);
239
- };
322
+ if (ctx == null) {
323
+ return original.apply(this, args);
324
+ }
325
+
326
+ // AsyncResource로 connect 컨텍스트 유지
327
+ const connectionResource = new AsyncResource('pg-pool-connect');
328
+ return connectionResource.runInAsyncScope(() => {
329
+ ctx.start_time = Date.now();
330
+
331
+ // 콜백 래핑
332
+ for (var i = args.length - 1; i >= 0; i--) {
333
+ if (typeof args[i] === 'function') {
334
+ var originalCallback = args[i];
335
+ // AsyncResource로 콜백 바인딩
336
+ args[i] = connectionResource.bind(function() {
337
+ var callbackArgs = Array.prototype.slice.call(arguments);
338
+ var err = callbackArgs[0];
339
+ var client = callbackArgs[1];
340
+
341
+ TraceContextManager.resume(ctx.id);
342
+ ctx.elapsed = Date.now() - ctx.start_time;
343
+
344
+ if (!err && client && !client.__query_hook__) {
345
+ client.__query_hook__ = true;
346
+ hookConnectionMethods(client, agent);
347
+ }
348
+
349
+ return originalCallback.apply(this, callbackArgs);
350
+ });
351
+ break;
352
+ }
353
+ }
354
+
355
+ var result = original.apply(this, args);
240
356
 
241
- PgSqlObserver.prototype._handleError = function (ctx, sql_step, err) {
242
- ctx.error_message = err.message;
243
- ctx.error_class = err.name || err.constructor?.name || 'PgsqlError';
244
- sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
245
- TraceSQL.isSlowSQL(ctx);
357
+ // Promise 기반 처리
358
+ if (result && typeof result.then === 'function' && args.every(arg => typeof arg !== 'function')) {
359
+ return result.then(connectionResource.bind(function(client) {
360
+ TraceContextManager.resume(ctx.id);
361
+ ctx.elapsed = Date.now() - ctx.start_time;
246
362
 
247
- MeterSql.add(sql_step.hash, sql_step.elapsed, false);
248
- StatSql.addSqlTime(ctx, ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, true, 0);
363
+ if (client && !client.__query_hook__) {
364
+ client.__query_hook__ = true;
365
+ hookConnectionMethods(client, agent);
366
+ }
367
+
368
+ return client;
369
+ }));
370
+ }
371
+
372
+ return result;
373
+ });
374
+ };
375
+ };
249
376
  };
250
377
 
251
- var toParamBytes = function(p, crc) {
252
- if(p == null || p.length === 0) {
253
- return null;
378
+ PgSqlObserver.prototype.inject = function (mod, moduleName) {
379
+ if (mod.__whatap_observe__) {
380
+ return;
254
381
  }
255
- try{
256
- return ParamSecurity.encrypt(Buffer.from(p), crc);
257
- } catch(e) {
258
- return null;
382
+ mod.__whatap_observe__ = true;
383
+ Logger.initPrint("PgSqlObserver");
384
+
385
+ if (conf.sql_enabled === false) {
386
+ return;
259
387
  }
260
- };
261
388
 
262
- var checkedSql = new IntKeyMap(2000).setMax(2000);
263
- var nonLiteSql = new IntKeyMap(5000).setMax(5000);
264
- var date = DateUtil.yyyymmdd();
389
+ var self = this;
265
390
 
266
- function escapeLiteral(sql) {
267
- if(sql == null) { sql = ''; }
391
+ // Client 생성자 래핑
392
+ if (mod.Client && !mod.Client.__whatap_wrapped__) {
393
+ shimmer.wrap(mod, 'Client', function(original) {
394
+ return function wrappedClient() {
395
+ var args = Array.prototype.slice.call(arguments);
396
+ var ctx = TraceContextManager.getCurrentContext();
397
+
398
+ setupDbcInfo(args);
399
+
400
+ // if (ctx && !ctx.db_opening) {
401
+ // ctx.db_opening = true;
402
+ // ctx.footprint('PgSql Connecting Start');
403
+ // ctx.start_time = Date.now();
404
+ // }
405
+
406
+ var client = new (Function.prototype.bind.apply(original, [null].concat(args)));
407
+
408
+ hookConnectionMethods(client, self.agent);
409
+
410
+ if (client && client.connect && !client.connect.__whatap_wrapped__) {
411
+ shimmer.wrap(client, 'connect', function(original) {
412
+ return function wrappedConnect() {
413
+ var args = Array.prototype.slice.call(arguments);
414
+ var ctx = TraceContextManager.getCurrentContext();
415
+
416
+ if (ctx) {
417
+ ctx.start_time = Date.now();
418
+ ctx.footprint('PgSql Connecting Start');
419
+ ctx.db_opening = true;
420
+
421
+ // 콜백 래핑
422
+ for (var i = args.length - 1; i >= 0; i--) {
423
+ if (typeof args[i] === 'function') {
424
+ var originalCallback = args[i];
425
+ args[i] = function() {
426
+ var callbackArgs = Array.prototype.slice.call(arguments);
427
+ var err = callbackArgs[0];
428
+
429
+ if (err) {
430
+ handleSqlError(ctx, err);
431
+ }
432
+
433
+ ctx.db_opening = false;
434
+ ctx.footprint('PgSql Connecting Done');
435
+ TraceContextManager.resume(ctx.id);
436
+
437
+ return originalCallback.apply(this, callbackArgs);
438
+ };
439
+ break;
440
+ }
441
+ }
442
+ }
268
443
 
269
- if(date !== DateUtil.yyyymmdd()) {
270
- checkedSql.clear();
271
- nonLiteSql.clear();
272
- date = DateUtil.yyyymmdd();
273
- Logger.print('WHATAP-SQL-CLEAR', 'PgSqlObserver CLEAR OK!!!!!!!!!!!!!!!!', false);
274
- }
444
+ var result = original.apply(this, args);
445
+
446
+ // Promise 기반 처리
447
+ if (result && typeof result.then === 'function') {
448
+ return result.then(function() {
449
+ if (ctx) {
450
+ ctx.db_opening = false;
451
+ ctx.footprint('PgSql Connecting Done');
452
+ }
453
+ return result;
454
+ }).catch(function(err) {
455
+ if (ctx) {
456
+ ctx.db_opening = false;
457
+ ctx.footprint('PgSql Connecting Error');
458
+ handleSqlError(ctx, err);
459
+ }
460
+ throw err;
461
+ });
462
+ }
275
463
 
276
- var sqlHash = HashUtil.hashFromString(sql);
277
- var psql = nonLiteSql.get(sqlHash);
464
+ return result;
465
+ };
466
+ });
467
+ client.connect.__whatap_wrapped__ = true;
468
+ }
278
469
 
279
- if(psql != null) {
280
- return psql;
470
+ if (ctx) {
471
+ ctx.elapsed = Date.now() - ctx.start_time;
472
+ ctx.footprint('PgSql Client Created');
473
+ ctx.db_opening = false;
474
+ }
475
+
476
+ return client;
477
+ };
478
+ });
479
+ mod.Client.__whatap_wrapped__ = true;
281
480
  }
282
- psql = checkedSql.get(sqlHash);
283
481
 
284
- if(psql != null) {
285
- return psql;
482
+ if (mod.Pool && !mod.Pool.__whatap_wrapped__) {
483
+ shimmer.wrap(mod, 'Pool', function(original) {
484
+ return function wrappedPool() {
485
+ var args = Array.prototype.slice.call(arguments);
486
+ setupDbcInfo(args);
487
+
488
+ var pool = new (Function.prototype.bind.apply(original, [null].concat(args)));
489
+
490
+ if (pool && !hookedInstances.has(pool)) {
491
+ hookedInstances.add(pool);
492
+
493
+ // Pool의 query 메서드 후킹
494
+ if (pool.query && !pool.query.__whatap_wrapped__) {
495
+ shimmer.wrap(pool, 'query', createQueryWrapper(self.agent, dbc));
496
+ pool.query.__whatap_wrapped__ = true;
497
+ }
498
+
499
+ // Pool의 connect 메서드는 수동 연결 시에만 후킹
500
+ // pool.query() 내부에서 자동으로 연결을 가져올 때는 후킹하지 않음
501
+ if (pool.connect && !pool.connect.__whatap_wrapped__) {
502
+ shimmer.wrap(pool, 'connect', function(original) {
503
+ return function wrappedConnect() {
504
+ var args = Array.prototype.slice.call(arguments);
505
+ var result = original.apply(this, args);
506
+
507
+ // Promise 기반 처리 - 연결 객체에만 후킹 (query는 후킹하지 않음)
508
+ if (result && typeof result.then === 'function') {
509
+ return result.then(function(client) {
510
+ // client에는 query 후킹하지 않음 (pool.query()에서 이미 추적됨)
511
+ return client;
512
+ });
513
+ } else if (result && typeof result === 'object') {
514
+ // 동기적으로 반환된 client에도 query 후킹하지 않음
515
+ return result;
516
+ }
517
+
518
+ return result;
519
+ };
520
+ });
521
+ pool.connect.__whatap_wrapped__ = true;
522
+ }
523
+ }
524
+
525
+ return pool;
526
+ };
527
+ });
528
+ mod.Pool.__whatap_wrapped__ = true;
286
529
  }
287
530
 
288
- var els = new EscapeLiteralSQL(sql);
289
- els.process();
531
+ if (mod.Client && mod.Client.prototype && mod.Client.prototype.query && !mod.Client.prototype.query.__whatap_wrapped__) {
532
+ shimmer.wrap(mod.Client.prototype, 'query', function(original) {
533
+ return function wrappedQuery() {
534
+ var args = Array.prototype.slice.call(arguments);
535
+ var ctx = TraceContextManager.getCurrentContext();
290
536
 
291
- var hash = HashUtil.hashFromString(els.getParsedSql());
292
- DataTextAgent.SQL.add(hash, els.getParsedSql());
537
+ if (ctx == null || args[0] == null) {
538
+ return original.apply(this, arguments);
539
+ }
293
540
 
294
- if(hash === sqlHash) {
295
- psql = new ParsedSql(els.sqlType, hash, null);
296
- nonLiteSql.put(sqlHash, psql);
297
- } else {
298
- psql = new ParsedSql(els.sqlType, hash, els.getParameter());
299
- checkedSql.put(sqlHash, psql);
541
+ if (dbc_hash === 0 && this.connectionParameters) {
542
+ var info = this.connectionParameters;
543
+ dbc = 'postgresql://';
544
+ dbc += (info.user || '') + '@';
545
+ dbc += (info.host || '') + '/';
546
+ dbc += info.database || '';
547
+ dbc_hash = HashUtil.hashFromString(dbc);
548
+ }
549
+
550
+ return createQueryWrapper(self.agent, dbc)(original).apply(this, args);
551
+ };
552
+ });
553
+ mod.Client.prototype.query.__whatap_wrapped__ = true;
300
554
  }
301
- return psql;
302
- }
555
+
556
+ if (mod.Pool && mod.Pool.prototype && mod.Pool.prototype.query && !mod.Pool.prototype.query.__whatap_wrapped__) {
557
+ shimmer.wrap(mod.Pool.prototype, 'query', createQueryWrapper(self.agent, dbc));
558
+ mod.Pool.prototype.query.__whatap_wrapped__ = true;
559
+ }
560
+ };
303
561
 
304
562
  exports.PgSqlObserver = PgSqlObserver;