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
@@ -4,18 +4,20 @@
4
4
  * can be found in the LICENSE file.
5
5
  */
6
6
 
7
- var TraceContextManager = require('../trace/trace-context-manager');
7
+ var TraceContextManager = require('../trace/trace-context-manager'),
8
+ Logger = require('../logger');
8
9
 
10
+ var HttpStepX = require('../step/http-stepx');
9
11
  const Hexa32 = require("../util/hexa32");
10
12
  const SecurityMaster = require("../net/security-master");
11
13
  const conf = require("../conf/configure");
12
14
  const KeyGen = require("../util/keygen");
13
- const TraceHelper = require('../util/trace-helper');
15
+ const HashUtil = require("../util/hashutil");
16
+ const DataTextAgent = require("../data/datatext-agent");
14
17
  const shimmer = require('../core/shimmer');
15
- const AsyncSender = require('../udp/async_sender');
16
- const { PacketTypeEnum } = require("../../lib/udp");
17
- const Transfer = require('../util/transfer');
18
- const TraceHttpc = require('../trace/trace-httpc');
18
+ const StatError = require("../stat/stat-error");
19
+ const TextTypes = require("../lang/text-types");
20
+ const URLPatternDetector = require('../trace/serviceurl-pattern-detector').Detector;
19
21
 
20
22
  var GlobalObserver = function (agent) {
21
23
  this.agent = agent;
@@ -30,28 +32,30 @@ conf.on('transaction_status_error_enable', function (newProps) {
30
32
  GlobalObserver.prototype.inject = function (mod, moduleName) {
31
33
  var self = this;
32
34
 
35
+ ['setTimeout', 'setInterval', 'setImmediate'].forEach(function (funcName) {
36
+ self.agent.aop.before(mod, funcName, function (obj, args) {
37
+ if(args[args.length - 1] === 'whatap') { return; }
38
+
39
+ var cached_id = TraceContextManager.getCurrentId();
40
+ self.agent.aop.functionHook(args, 0, function (obj, args) {
41
+ TraceContextManager.resume(cached_id);
42
+ cached_id = null;
43
+ });
44
+ });
45
+ });
46
+
33
47
  shimmer.wrap(mod, 'fetch', function(original) {
34
48
  return async function(...args) {
35
49
  var info = args[1] ? args[1] : {};
36
50
  var ctx = TraceContextManager._asyncLocalStorage.getStore();
37
51
 
38
52
  if (ctx) {
39
- ctx.start_time = Date.now(); // start_time 설정 추가
40
-
41
- TraceHelper.interTxTraceAutoOn(ctx);
53
+ interTxTraceAutoOn(ctx);
42
54
 
43
55
  if (conf.getProperty('mtrace_enabled', false)) {
44
56
  addTraceHeaders(info, ctx);
45
57
  args[1] = info;
46
58
  }
47
-
48
- if (args[0]) {
49
- const url = new URL(args[0]);
50
- ctx.httpc_url = url.pathname + url.search;
51
- ctx.httpc_host = url.hostname;
52
- ctx.httpc_port = url.port || (url.protocol === 'https:' ? 443 : 80);
53
- ctx.active_httpc_hash = true;
54
- }
55
59
  }
56
60
 
57
61
  try {
@@ -67,100 +71,171 @@ GlobalObserver.prototype.inject = function (mod, moduleName) {
67
71
  };
68
72
 
69
73
  function addTraceHeaders(info, ctx) {
70
- // 헤더 객체 초기화
71
- if (!info.headers) {
72
- info.headers = {};
74
+ if (info.headers) {
75
+ info.headers['x-wtap-po'] = transferPOID(ctx);
76
+ } else {
77
+ info.headers = {
78
+ 'x-wtap-po': transferPOID(ctx)
79
+ };
73
80
  }
74
-
75
- info.headers['x-wtap-po'] = Transfer.POID();
76
-
77
- if (conf.stat_mtrace_enabled) {
78
- info.headers[conf._trace_mtrace_spec_key1] = Transfer.SPEC_URL(ctx);
81
+ if (conf.getProperty('stat_mtrace_enabled', false)) {
82
+ info.headers[conf._trace_mtrace_spec_key1] = transferSPEC_URL(ctx);
79
83
  }
80
-
81
- if (!ctx.mtid.isZero()) {
82
- info.headers[conf._trace_mtrace_caller_key] = Transfer.MTID_CALLERTX(ctx);
84
+ if (ctx.mtid.isZero() === false) {
85
+ info.headers[conf._trace_mtrace_caller_key] = transferMTID_CALLERTX(ctx);
83
86
  }
84
-
85
- ctx.mcallee = KeyGen.getMtid();
86
- info.headers[conf._trace_mtrace_callee_key] = Hexa32.toString32(ctx.mcallee);
87
87
  }
88
88
 
89
89
  function handleResponse(ctx, args, response) {
90
90
  if (!ctx || !args[0]) return;
91
91
 
92
- // Error handling for 4xx, 5xx responses
93
- if (!response.ok && response.status >= 400 && transaction_status_error_enable) {
94
- let error = {
95
- class: response.statusText,
96
- message: ''
97
- }
98
- interceptorError(response.status, error, ctx);
92
+ const url = new URL(args[0]);
93
+ setupContext(ctx, url);
94
+
95
+ var step = createStep(ctx, url);
96
+
97
+ if (!response.ok && transaction_status_error_enable) {
98
+ recordError(ctx, step, response);
99
99
  }
100
100
 
101
- endHttpc(ctx);
101
+ ctx.profile.push(step);
102
102
  }
103
103
 
104
104
  function handleError(ctx, args, err) {
105
105
  if (!ctx || !args[0]) return;
106
+ ctx.error_class = err.name || err.constructor?.name || 'HttpError';
107
+ ctx.error_message = err.message;
108
+
109
+ const url = new URL(args[0]);
110
+ setupContext(ctx, url);
106
111
 
107
- const networkErrorToStatusCode = {
108
- 'ECONNREFUSED': 503,
109
- 'ETIMEDOUT': 504,
110
- 'ENOTFOUND': 502,
111
- 'ECONNRESET': 503,
112
- 'EPIPE': 503,
113
- 'EHOSTUNREACH': 503,
114
- };
115
- const statusCode = networkErrorToStatusCode[err.code] || 500;
112
+ var step = createStep(ctx, url);
116
113
 
117
114
  if (transaction_status_error_enable) {
118
- let error = {
119
- class: err.code || err.name,
120
- message: err.message || ''
121
- }
122
- interceptorError(statusCode, error, ctx);
115
+ recordError(ctx, step, err);
123
116
  }
124
117
 
125
- endHttpc(ctx);
118
+ ctx.profile.push(step);
126
119
  }
127
120
 
128
- function interceptorError(statusCode, error, ctx) {
129
- if (!ctx) {
130
- return;
131
- }
132
-
133
- ctx.status = statusCode;
134
- let errors = [];
135
- let error_message = 'Request failed with status code ';
121
+ function setupContext(ctx, url) {
122
+ ctx.httpc_host = url.hostname;
123
+ ctx.httpc_url = url.pathname;
124
+ ctx.httpc_port = url.port || (url.protocol === 'https:' ? 443 : 80);
125
+ }
136
126
 
137
- if (statusCode >= 400 && !ctx.error) {
138
- ctx.error = 1;
127
+ function createStep(ctx, url) {
128
+ var step = new HttpStepX();
129
+ step.start_time = ctx.getElapsedTime();
130
+ step.url = HashUtil.hashFromString(ctx.httpc_url);
131
+ DataTextAgent.HTTPC_URL.add(step.url, ctx.httpc_url);
132
+ step.host = HashUtil.hashFromString(ctx.httpc_host);
133
+ DataTextAgent.HTTPC_HOST.add(step.host, ctx.httpc_host);
134
+ step.port = ctx.httpc_port;
135
+ return step;
136
+ }
139
137
 
140
- errors.push(error.class);
141
- if (error.message) {
142
- errors.push(error.message);
138
+ function recordError(ctx, step, response) {
139
+ if (step.error.isZero()) {
140
+ const errorMessage = ctx.error_message = response.statusText || (response.cause ? response.cause.message : '') || response.message || 'Unknown error';
141
+ step.error = StatError.addError(response.status || null, errorMessage, ctx.service_hash, TextTypes.HTTPC_URL, step.url);
142
+
143
+ if (response.status) {
144
+ if (response.status >= 400 && response.status < 500) {
145
+ ctx.error_class = `HttpClientError`;
146
+ } else if (response.status >= 500) {
147
+ ctx.error_class = `HttpServerError`;
148
+ } else {
149
+ ctx.error_class = `HttpError`;
150
+ }
143
151
  } else {
144
- errors.push(error_message + statusCode);
152
+ ctx.error_class = 'HttpError';
145
153
  }
146
154
 
147
- AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
155
+ if (ctx.error.isZero()) {
156
+ ctx.error = step.error;
157
+ ctx.statusCode = response.status || null;
158
+ ctx.statusMessage = errorMessage;
159
+ }
148
160
  }
149
161
  }
150
162
 
151
- function endHttpc(ctx) {
152
- if (ctx == null) {
153
- return;
154
- }
163
+ var transfer_poid;
164
+ function transferPOID(ctx) {
165
+ if (transfer_poid)
166
+ return transfer_poid;
167
+ transfer_poid = Hexa32.toString32(SecurityMaster.PCODE) + ','
168
+ + Hexa32.toString32(SecurityMaster.OKIND) + ',' + Hexa32.toString32(SecurityMaster.OID);
169
+ return transfer_poid;
170
+ }
171
+
172
+ function transferMTID_CALLERTX(ctx) {
173
+ if (ctx.transfer_id)
174
+ return ctx.transfer_id;
175
+ var x = Hexa32.toString32(ctx.mtid) + ',' + (ctx.mdepth + 1) + ',' + Hexa32.toString32(ctx.txid);
176
+ ctx.transfer_id = x;
177
+ return ctx.transfer_id;
178
+ }
155
179
 
156
- ctx.active_httpc_hash = false;
157
- let urls = `${ctx.httpc_host}:${ctx.httpc_port}${ctx.httpc_url}`;
158
- let httpcDatas = [urls, ctx.mcallee];
159
- ctx.elapsed = Date.now() - ctx.start_time;
160
- TraceHttpc.isSlowHttpc(ctx);
161
- AsyncSender.send_packet(PacketTypeEnum.TX_HTTPC, ctx, httpcDatas);
180
+ function transferSPEC_URL(ctx) {
181
+ if (ctx.transfer_info)
182
+ return ctx.transfer_info;
183
+ var x = conf.mtrace_spec + ',' + ctx.service_hash;
184
+ ctx.transfer_info = x;
185
+ return ctx.transfer_info;
162
186
  }
163
187
 
164
- // interTxTraceAutoOn function moved to util/trace-helper.js
188
+ var check_seq = 1;
189
+ function interTxTraceAutoOn(ctx) {
190
+ if (conf.mtrace_enabled == false || ctx.httpc_checked || ctx.mtid.isZero() === false)
191
+ return;
192
+ ctx.httpc_checked = true;
193
+ if (conf.mtrace_rate >= 100) {
194
+ ctx.mtid = KeyGen.next();
195
+ return;
196
+ }
197
+ check_seq++;
198
+ switch (Math.floor(conf.mtrace_rate / 10)) {
199
+ case 10:
200
+ ctx.mtid = KeyGen.next();
201
+ break;
202
+ case 9:
203
+ if (check_seq % 10 !== 0)
204
+ ctx.mtid = KeyGen.next();
205
+ break;
206
+ case 8:
207
+ if (check_seq % 5 !== 0)
208
+ ctx.mtid = KeyGen.next();
209
+ break;
210
+ case 7:
211
+ if (check_seq % 4 !== 0)
212
+ ctx.mtid = KeyGen.next();
213
+ break;
214
+ case 6:
215
+ if (check_seq % 3 !== 0)
216
+ ctx.mtid = KeyGen.next();
217
+ break;
218
+ case 5:
219
+ if (check_seq % 2 === 0)
220
+ ctx.mtid = KeyGen.next();
221
+ break;
222
+ case 4:
223
+ if (check_seq % 3 === 0 || check_seq % 5 === 0)
224
+ ctx.mtid = KeyGen.next();
225
+ break;
226
+ case 3:
227
+ if (check_seq % 4 === 0 || check_seq % 5 === 0)
228
+ ctx.mtid = KeyGen.next();
229
+ break;
230
+ case 2:
231
+ if (check_seq % 5 === 0)
232
+ ctx.mtid = KeyGen.next();
233
+ break;
234
+ case 1:
235
+ if (check_seq % 10 === 0)
236
+ ctx.mtid = KeyGen.next();
237
+ break;
238
+ }
239
+ }
165
240
 
166
241
  exports.GlobalObserver = GlobalObserver;
@@ -0,0 +1,336 @@
1
+ /**
2
+ * Copyright 2016 the WHATAP project authors. All rights reserved.
3
+ * Use of this source code is governed by a license that
4
+ * can be found in the LICENSE file.
5
+ */
6
+
7
+ var TraceContextManager = require('../trace/trace-context-manager'),
8
+ conf = require('../conf/configure'),
9
+ Logger = require('../logger');
10
+ const { Detector: URLPatternDetector } = require("../trace/serviceurl-pattern-detector");
11
+ const HashUtil = require("../util/hashutil");
12
+ const DataTextAgent = require("../data/datatext-agent");
13
+ const ResourceProfile = require("../util/resourceprofile");
14
+ const ProfilePack = require('../pack/profile-pack');
15
+ const TxRecord = require('../service/tx-record');
16
+ const DateUtil = require('../util/dateutil');
17
+ const SecurityMaster = require('../net/security-master');
18
+ const DataProfileAgent = require("../data/dataprofile-agent");
19
+ const MessageStep = require("../step/message-step");
20
+ const MeterService = require('../counter/meter/meter-service').MeterService;
21
+
22
+ const shimmer = require('../core/shimmer');
23
+ const StatError = require("../stat/stat-error");
24
+
25
+ var grpc_profile_enabled = conf.getProperty('grpc_profile_enabled', true);
26
+ var grpc_profile_stream_client_enabled = conf.getProperty('grpc_profile_stream_client_enabled', true);
27
+ var grpc_profile_stream_server_enabled = conf.getProperty('grpc_profile_stream_server_enabled', true);
28
+ var grpc_profile_ignore_method = conf.getProperty('grpc_profile_ignore_method', '');
29
+ var ignore_method_set = null;
30
+ conf.on('grpc_profile_enabled', function(newProperty) {
31
+ grpc_profile_enabled = newProperty;
32
+ });
33
+ conf.on('grpc_profile_stream_client_enabled', function(newProperty) {
34
+ grpc_profile_stream_client_enabled = newProperty;
35
+ });
36
+ conf.on('grpc_profile_stream_server_enabled', function(newProperty) {
37
+ grpc_profile_stream_server_enabled = newProperty;
38
+ });
39
+ conf.on('grpc_profile_ignore_method', function(newProperty) {
40
+ grpc_profile_ignore_method = newProperty;
41
+ });
42
+
43
+ var GRpcObserver = function(agent) {
44
+ this.agent = agent;
45
+ this.packages = ['@grpc/grpc-js'];
46
+ };
47
+
48
+ const types = {
49
+ unary: 'unary',
50
+ client_stream: 'clientStream',
51
+ server_stream: 'serverStream',
52
+ bidi: 'bidi'
53
+ };
54
+
55
+ GRpcObserver.prototype.inject = function(mod, moduleName) {
56
+ if (mod.__whatap_observe__) {
57
+ return;
58
+ }
59
+ mod.__whatap_observe__ = true;
60
+ Logger.initPrint("GRpcObserver");
61
+
62
+ if (grpc_profile_enabled) {
63
+ shimmer.wrap(mod.Server.prototype, 'register', wrapRegister);
64
+ shimmer.wrap(mod.Client.prototype, 'register', wrapRegister);
65
+ }
66
+ };
67
+
68
+ function checkIgnoreMethod(ignore_method, method_name){
69
+ try{
70
+ if (ignore_method) {
71
+ ignore_method_set = new Set(ignore_method.split(','));
72
+ } else {
73
+ ignore_method_set = null;
74
+ }
75
+ if (ignore_method_set && ignore_method_set.has(method_name)) {
76
+ return true;
77
+ }
78
+ }catch (e) {
79
+ Logger.printError('WHATAP-703', 'gRPC checkIgnoreMethod error: ' + e, false);
80
+ }
81
+ return false;
82
+ }
83
+
84
+ function wrapHandler(handler, methodName, type) {
85
+ return function(call, callback) {
86
+ var method_name = methodName.includes('/') ? methodName.substring(methodName.lastIndexOf('/')+1, methodName.length) : methodName
87
+ if (!grpc_profile_enabled || checkIgnoreMethod(conf.getProperty('grpc_profile_ignore_method', ''), method_name)) {
88
+ return handler.call(this, call, callback);
89
+ }
90
+
91
+ TraceContextManager._asyncLocalStorage.run(initCtx(), () => {
92
+ var ctx = TraceContextManager._asyncLocalStorage.getStore();
93
+ try {
94
+ if (!ctx) {
95
+ return handler.call(this, call, callback);
96
+ }
97
+
98
+ ctx.service_name = methodName;
99
+ ctx.service_hash = HashUtil.hashFromString(methodName);
100
+
101
+ var step_type = new MessageStep();
102
+ step_type.hash = HashUtil.hashFromString('Type');
103
+ step_type.start_time = ctx.getElapsedTime();
104
+ step_type.desc = type;
105
+ DataTextAgent.MESSAGE.add(step_type.hash, "Type");
106
+ ctx.profile.push(step_type);
107
+
108
+ var step_method = new MessageStep();
109
+ step_method.hash = HashUtil.hashFromString('Method');
110
+ step_method.start_time = ctx.getElapsedTime();
111
+ step_method.desc = method_name;
112
+ DataTextAgent.MESSAGE.add(step_method.hash, "Method");
113
+ ctx.profile.push(step_method);
114
+
115
+ if(call.request && Object.keys(call.request).length > 0){
116
+ var step_param = new MessageStep();
117
+ step_param.hash = HashUtil.hashFromString('Parameter');
118
+ step_param.start_time = ctx.getElapsedTime();
119
+ step_param.desc = JSON.stringify(Object.keys(call.request));
120
+ DataTextAgent.MESSAGE.add(step_param.hash, "Parameter");
121
+ ctx.profile.push(step_param);
122
+ }
123
+
124
+ ctx.grpc_method = method_name;
125
+
126
+ function wrappedCallback(err, response, ctx) {
127
+
128
+ if (err) {
129
+ ctx.error = err.stack;
130
+ ctx.statusCode = 500;
131
+ ctx.error_message = err.message;
132
+ ctx.error_class = err.name || err.constructor?.name || 'GrpcError';
133
+
134
+ var step_error = new MessageStep();
135
+ step_error.hash = HashUtil.hashFromString("EXCEPTION");
136
+ step_error.start_time = ctx.getElapsedTime();
137
+ step_error.desc = err.stack;
138
+ DataTextAgent.MESSAGE.add(step_error.hash, "EXCEPTION");
139
+ ctx.profile.push(step_error);
140
+ }
141
+
142
+ endTransaction(ctx);
143
+
144
+ if (typeof callback === 'function') {
145
+ return callback(err, response);
146
+ }
147
+ }
148
+
149
+ return handler.call(this, call, (err, response) => wrappedCallback(err, response, ctx));
150
+ } catch (err) {
151
+ Logger.printError('WHATAP-701', 'gRPC wrapHandler error: ' + err, false);
152
+
153
+ if(ctx.error.isZero()) {
154
+ ctx.error = StatError.addError(err.code, err.message, ctx.service_hash);
155
+ }
156
+ ctx.error_message = err.message;
157
+ ctx.error_class = err.name || err.constructor?.name || 'GrpcError';
158
+
159
+ var step_error = new MessageStep();
160
+ step_error.hash = HashUtil.hashFromString("EXCEPTION");
161
+ step_error.start_time = ctx.getElapsedTime();
162
+ step_error.desc = err.stack;
163
+ DataTextAgent.MESSAGE.add(step_error.hash, "EXCEPTION");
164
+ ctx.profile.push(step_error);
165
+
166
+ endTransaction(ctx);
167
+
168
+ return handler.call(this, call, callback);
169
+ }
170
+ });
171
+ };
172
+ }
173
+
174
+ function wrapStreamHandler(handler, methodName, type) {
175
+ return function(call, callback) {
176
+ var method_name = methodName.includes('/') ? methodName.substring(methodName.lastIndexOf('/')+1, methodName.length) : methodName
177
+ if (!grpc_profile_enabled || checkIgnoreMethod(conf.getProperty('grpc_profile_ignore_method', ''), method_name)) {
178
+ return handler.call(this, call, callback);
179
+ }
180
+
181
+ switch (type) {
182
+ case 'server_stream':
183
+ if (!grpc_profile_stream_server_enabled) {
184
+ return handler.call(this, call, callback);
185
+ }
186
+ break;
187
+ case 'client_stream':
188
+ if (!grpc_profile_stream_client_enabled) {
189
+ return handler.call(this, call, callback);
190
+ }
191
+ break;
192
+ case 'bidi':
193
+ if (!grpc_profile_stream_server_enabled || !grpc_profile_stream_client_enabled) {
194
+ return handler.call(this, call, callback);
195
+ }
196
+ break;
197
+ }
198
+
199
+ TraceContextManager._asyncLocalStorage.run(initCtx(), () => {
200
+ try {
201
+ var ctx = TraceContextManager._asyncLocalStorage.getStore();
202
+ if (!ctx) {
203
+ return handler.call(this, call, callback);
204
+ }
205
+
206
+ ctx.service_name = methodName;
207
+ ctx.service_hash = HashUtil.hashFromString(methodName);
208
+
209
+ var step_type = new MessageStep();
210
+ step_type.hash = HashUtil.hashFromString('Type');
211
+ step_type.start_time = ctx.getElapsedTime();
212
+ step_type.desc = type;
213
+ DataTextAgent.MESSAGE.add(step_type.hash, "Type");
214
+ ctx.profile.push(step_type);
215
+
216
+ var step_method = new MessageStep();
217
+ step_method.hash = HashUtil.hashFromString('Method');
218
+ step_method.start_time = ctx.getElapsedTime();
219
+ step_method.desc = method_name;
220
+ DataTextAgent.MESSAGE.add(step_method.hash, "Method");
221
+ ctx.profile.push(step_method);
222
+
223
+ ctx.grpc_method = method_name;
224
+
225
+ const startTime = Date.now();
226
+
227
+ call.on('end', () => {
228
+ const duration = Date.now() - startTime;
229
+ endTransaction(ctx);
230
+ });
231
+
232
+ call.once('cancelled', () => {
233
+ ctx.cancelled = true;
234
+ var step_cancel = new MessageStep();
235
+ step_cancel.hash = HashUtil.hashFromString("CANCELLED");
236
+ step_cancel.start_time = ctx.getElapsedTime();
237
+ step_cancel.desc = "Request cancelled";
238
+ DataTextAgent.MESSAGE.add(step_cancel.hash, "CANCELLED");
239
+ ctx.profile.push(step_cancel);
240
+ endTransaction(ctx);
241
+ });
242
+
243
+ call.on('error', (err) => {
244
+ ctx.error = err.stack;
245
+ ctx.statusCode = err.code || grpc.status.INTERNAL;
246
+
247
+ var step_error = new MessageStep();
248
+ step_error.hash = HashUtil.hashFromString("EXCEPTION");
249
+ step_error.start_time = ctx.getElapsedTime();
250
+ step_error.desc = err.stack;
251
+ DataTextAgent.MESSAGE.add(step_error.hash, "EXCEPTION");
252
+ ctx.profile.push(step_error);
253
+
254
+ endTransaction(ctx);
255
+ });
256
+
257
+ return handler.call(this, call);
258
+ } catch (e) {
259
+ Logger.printError('WHATAP-702', 'gRPC wrapStreamHandler error: ' + e, false);
260
+ return handler.call(this, call);
261
+ }
262
+ });
263
+ };
264
+ }
265
+
266
+ function wrapRegister(register) {
267
+ return function(name, handler, serialize, deserialize, type) {
268
+ if (typeof handler === 'function') {
269
+ switch (type) {
270
+ case types.client_stream:
271
+ handler = wrapStreamHandler(handler, name, 'clientStream');
272
+ break;
273
+ case types.server_stream:
274
+ handler = wrapStreamHandler(handler, name, 'serverStream');
275
+ break;
276
+ case types.bidi:
277
+ handler = wrapStreamHandler(handler, name, 'bidi');
278
+ break;
279
+ case types.unary:
280
+ default:
281
+ handler = wrapHandler(handler, name, 'unary');
282
+ }
283
+ }
284
+ return register.call(this, name, handler, serialize, deserialize, type);
285
+ };
286
+ }
287
+
288
+ function endTransaction(ctx) {
289
+ try {
290
+ var profile = new ProfilePack();
291
+ var wtx = new TxRecord();
292
+ wtx.endTime = DateUtil.currentTime();
293
+ profile.time = wtx.endTime;
294
+ wtx.elapsed = ctx.getElapsedTime();
295
+
296
+ DataTextAgent.SERVICE.add(ctx.service_hash, ctx.service_name);
297
+
298
+ wtx.seq = ctx.txid;
299
+ wtx.service = ctx.service_hash;
300
+ wtx.cpuTime = ResourceProfile.getCPUTime() - ctx.start_cpu;
301
+ wtx.malloc = ResourceProfile.getUsedHeapSize() - ctx.start_malloc;
302
+ if (wtx.malloc < 0) { wtx.malloc = 0; }
303
+ wtx.status = 2;
304
+ wtx.http_method = ctx.grpc_method;
305
+
306
+ wtx.ipaddr = ctx.remoteIp;
307
+
308
+ MeterService.add(wtx.service, wtx.elapsed,
309
+ wtx.errorLevel, ctx.mcaller_pcode, ctx.mcaller_okind, ctx.mcaller_oid);
310
+
311
+ profile.oid = SecurityMaster.OID;
312
+ profile.service = wtx;
313
+
314
+ TraceContextManager.end(ctx._id);
315
+
316
+ setTimeout(function() {
317
+ DataProfileAgent.sendProfile(ctx, profile, false);
318
+ ctx = null;
319
+ }, 100);
320
+ } catch (e) {
321
+ Logger.printError('WHATAP-615', 'Websocket end transaction error..', e, false);
322
+ TraceContextManager.end(ctx._id);
323
+ ctx = null;
324
+ }
325
+ };
326
+
327
+ function initCtx() {
328
+ const ctx = TraceContextManager.start();
329
+ if (!ctx) { return; }
330
+
331
+ ctx.start_malloc = ResourceProfile.getUsedHeapSize();
332
+ ctx.start_cpu = ResourceProfile.getCPUTime();
333
+ return ctx;
334
+ }
335
+
336
+ exports.GRpcObserver = GRpcObserver;