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
@@ -5,18 +5,25 @@
5
5
  */
6
6
 
7
7
  var TraceContextManager = require('../trace/trace-context-manager'),
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;
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");
20
27
 
21
28
  var PgSqlObserver = function (agent) {
22
29
  this.agent = agent;
@@ -24,539 +31,274 @@ var PgSqlObserver = function (agent) {
24
31
  };
25
32
 
26
33
  var dbc_hash = 0;
27
- var dbc = 'postgresql://';
28
-
29
- var hookedInstances = new WeakSet();
34
+ var dbc, dbc_step;
35
+ PgSqlObserver.prototype.inject = function (mod, moduleName) {
36
+ if (mod.__whatap_observe__) { return; }
37
+ mod.__whatap_observe__ = true;
30
38
 
31
- var checkedSql = new IntKeyMap(2000).setMax(2000);
32
- var nonLiteSql = new IntKeyMap(5000).setMax(5000);
33
- var date = DateUtil.yyyymmdd();
39
+ Logger.initPrint("PgSqlObserver");
34
40
 
35
- function handleSqlError(ctx, err) {
36
- if (!err) return;
41
+ var self = this;
42
+ var aop = self.agent.aop;
37
43
 
38
- try {
39
- var errorClass = err.code || err.name || err.constructor?.name || 'PostgreSQLError';
40
- var errorMessage = err.message || 'postgresql error';
41
- var errorStack = '';
44
+ if (conf.sql_enabled === false) { return; }
42
45
 
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);
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);
73
51
  }
74
52
 
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
- }
81
-
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
- }
92
-
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
- }
113
-
114
- function escapeLiteral(sql) {
115
- if (sql == null) {
116
- sql = '';
117
- }
118
-
119
- if (date !== DateUtil.yyyymmdd()) {
120
- checkedSql.clear();
121
- nonLiteSql.clear();
122
- date = DateUtil.yyyymmdd();
123
- }
124
-
125
- var sqlHash = HashUtil.hashFromString(sql);
126
- var psql = nonLiteSql.get(sqlHash);
53
+ ctx.pg_opening = true;
54
+ ctx.footprint('PgSql Connecting Start');
127
55
 
128
- if (psql != null) {
129
- return psql;
130
- }
131
- psql = checkedSql.get(sqlHash);
132
-
133
- if (psql != null) {
134
- return psql;
135
- }
56
+ dbc_step = new DBCStep();
57
+ dbc_step.start_time = ctx.getElapsedTime();
136
58
 
137
- var els = new EscapeLiteralSQL(sql);
138
- els.process();
139
-
140
- var hash = HashUtil.hashFromString(els.getParsedSql());
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
+ };
141
83
 
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
- }
84
+ try {
85
+ const result = original.apply(this, arguments);
151
86
 
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();
87
+ if (result && result.then) {
88
+ result.then(finishConnect(this));
89
+ } else {
90
+ finishConnect(this);
91
+ }
157
92
 
158
- if (ctx == null || args[0] == null) {
159
- return original.apply(this, arguments);
93
+ } catch (err) {
94
+ ctx.db_opening = false;
95
+ Logger.printError('PgSqlObserver', 'Connect Error', err);
96
+ throw err;
160
97
  }
98
+ };
99
+ });
161
100
 
162
- if (dbcUrl === 'postgresql://' || !dbc_hash) {
101
+ shimmer.wrap(mod.Client.prototype, 'query', function (original) {
102
+ return function () {
103
+ const ctx = TraceContextManager.getCurrentContext();
104
+ if (!ctx) {
163
105
  return original.apply(this, arguments);
164
106
  }
165
107
 
166
- const asyncResource = new AsyncResource('pg-query');
167
- const callbackResource = new AsyncResource('pg-callback');
168
-
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]);
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;
174
115
 
175
- var sql_start_time = Date.now();
176
- ctx.footprint('PgSql Query Start');
177
- ctx.sql_count++;
116
+ DataTextAgent.DBC.add(dbc_hash, dbc);
117
+ DataTextAgent.METHOD.add(dbc_hash, dbc);
118
+ DataTextAgent.ERROR.add(dbc_hash, dbc);
178
119
 
179
- var sql = args.length > 0 ? args[0] : undefined;
180
- var params = null;
181
- var finalSql = '';
120
+ ctx.pg_opening = false;
121
+ ctx.footprint('PgSql Connecting Done');
122
+ ctx.profile.push(dbc_step);
123
+ }
124
+ dbc_step = null;
182
125
 
183
- try {
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
- }
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');
191
130
 
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
- }
208
- }
131
+ ctx.vvv++;
209
132
 
210
- if (typeof finalSql !== 'string' || finalSql.length === 0) {
211
- finalSql = '';
133
+ let sql = arguments[0];
134
+ let psql = null;
135
+ if (typeof sql === 'string' && sql.length > 0) {
136
+ try {
137
+ psql = escapeLiteral(sql);
138
+ } catch (e) {
139
+ Logger.printError('WHATAP-191', 'PgSqlObserver escapeliteral error', e);
212
140
  }
141
+ } else {
142
+ sql = '';
143
+ psql = escapeLiteral(sql);
144
+ }
213
145
 
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);
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);
224
170
  }
171
+ sql_step.pcrc = crc.value;
172
+ }
225
173
 
226
- var sqlHash = psql ? psql.sql : 0;
227
- ctx.active_sqlhash = sqlHash;
228
- ctx.active_dbc = dbc_hash;
229
-
230
- function queryCallback(err, results) {
231
- var currentCtx = TraceContextManager.getCurrentContext();
232
- if (currentCtx == null) {
233
- return;
234
- }
235
-
236
- TraceContextManager.resume(currentCtx.id);
174
+ try {
175
+ const result = original.apply(this, arguments);
237
176
 
238
- var sql_elapsed = Date.now() - sql_start_time;
239
- var resultCount = 0;
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);
240
187
 
241
- if (err) {
242
- handleSqlError(currentCtx, err);
243
- }
188
+ ctx.rs_count = ctx.rs_count ? ctx.rs_count + res.rowCount : res.rowCount;
244
189
 
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;
250
- }
251
- }
190
+ MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
191
+ StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
252
192
 
253
- try {
254
- TraceSQL.isSlowSQL(currentCtx);
255
- if (results && results.command && results.command === 'SELECT' && psql != null && psql.type === 'S') {
256
- TraceSQL.isTooManyRecords(resultCount, currentCtx);
193
+ TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
257
194
  }
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);
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);
282
205
  }
283
- if (originalCallback && typeof originalCallback === 'function') {
284
- return originalCallback.apply(this, callbackArgs);
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);
208
+ }
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);
285
219
  }
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
- });
220
+ return callback.apply(this, arguments);
221
+ };
304
222
  }
305
-
306
223
  return result;
307
- } catch (queryError) {
308
- queryCallback(queryError, null);
309
- throw queryError;
310
224
  }
311
- });
225
+ } catch (err) {
226
+ self._handleError(ctx, sql_step, err);
227
+ throw err;
228
+ }
312
229
  };
313
- };
230
+ });
314
231
  };
315
232
 
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();
321
-
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);
233
+ PgSqlObserver.prototype._finishQuery = function (ctx, sql_step) {
234
+ sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
235
+ TraceSQL.isSlowSQL(ctx);
356
236
 
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;
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
+ };
362
240
 
363
- if (client && !client.__query_hook__) {
364
- client.__query_hook__ = true;
365
- hookConnectionMethods(client, agent);
366
- }
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);
367
246
 
368
- return client;
369
- }));
370
- }
371
-
372
- return result;
373
- });
374
- };
375
- };
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);
376
249
  };
377
250
 
378
- PgSqlObserver.prototype.inject = function (mod, moduleName) {
379
- if (mod.__whatap_observe__) {
380
- return;
251
+ var toParamBytes = function(p, crc) {
252
+ if(p == null || p.length === 0) {
253
+ return null;
381
254
  }
382
- mod.__whatap_observe__ = true;
383
- Logger.initPrint("PgSqlObserver");
384
-
385
- if (conf.sql_enabled === false) {
386
- return;
255
+ try{
256
+ return ParamSecurity.encrypt(Buffer.from(p), crc);
257
+ } catch(e) {
258
+ return null;
387
259
  }
260
+ };
388
261
 
389
- var self = this;
390
-
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
- }
443
-
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
- }
463
-
464
- return result;
465
- };
466
- });
467
- client.connect.__whatap_wrapped__ = true;
468
- }
262
+ var checkedSql = new IntKeyMap(2000).setMax(2000);
263
+ var nonLiteSql = new IntKeyMap(5000).setMax(5000);
264
+ var date = DateUtil.yyyymmdd();
469
265
 
470
- if (ctx) {
471
- ctx.elapsed = Date.now() - ctx.start_time;
472
- ctx.footprint('PgSql Client Created');
473
- ctx.db_opening = false;
474
- }
266
+ function escapeLiteral(sql) {
267
+ if(sql == null) { sql = ''; }
475
268
 
476
- return client;
477
- };
478
- });
479
- mod.Client.__whatap_wrapped__ = true;
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);
480
274
  }
481
275
 
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
- }
276
+ var sqlHash = HashUtil.hashFromString(sql);
277
+ var psql = nonLiteSql.get(sqlHash);
524
278
 
525
- return pool;
526
- };
527
- });
528
- mod.Pool.__whatap_wrapped__ = true;
279
+ if(psql != null) {
280
+ return psql;
529
281
  }
282
+ psql = checkedSql.get(sqlHash);
530
283
 
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();
536
-
537
- if (ctx == null || args[0] == null) {
538
- return original.apply(this, arguments);
539
- }
284
+ if(psql != null) {
285
+ return psql;
286
+ }
540
287
 
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
- }
288
+ var els = new EscapeLiteralSQL(sql);
289
+ els.process();
549
290
 
550
- return createQueryWrapper(self.agent, dbc)(original).apply(this, args);
551
- };
552
- });
553
- mod.Client.prototype.query.__whatap_wrapped__ = true;
554
- }
291
+ var hash = HashUtil.hashFromString(els.getParsedSql());
292
+ DataTextAgent.SQL.add(hash, els.getParsedSql());
555
293
 
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;
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);
559
300
  }
560
- };
301
+ return psql;
302
+ }
561
303
 
562
304
  exports.PgSqlObserver = PgSqlObserver;