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,28 +4,36 @@
4
4
  * can be found in the LICENSE file.
5
5
  */
6
6
 
7
+
7
8
  var TraceContextManager = require('../trace/trace-context-manager'),
9
+ SocketStep = require('../step/socket-step'),
8
10
  conf = require('../conf/configure'),
9
11
  IPUtil = require('../util/iputil'),
10
12
  Logger = require('../logger');
11
13
  const {Detector: URLPatternDetector} = require("../trace/serviceurl-pattern-detector");
12
- const DateUtil = require('../util/dateutil');
13
- const SecurityMaster = require('../net/security-master');
14
+ const HashUtil = require("../util/hashutil");
15
+ const DataTextAgent = require("../data/datatext-agent");
16
+ const ResourceProfile = require("../util/resourceprofile");
17
+ const ProfilePack = require('../pack/profile-pack');
18
+ const TxRecord = require('../service/tx-record');
19
+ const DateUtil = require('../util/dateutil');
20
+ const SecurityMaster = require('../net/security-master');
21
+ const DataProfileAgent = require('../data/dataprofile-agent');
22
+ const MessageStep = require("../step/message-step");
23
+ const MeterUsers = require("../counter/meter/meter-users");
24
+ const MeterService = require('../counter/meter/meter-service').MeterService;
14
25
  const shimmer = require('../core/shimmer');
15
- const HashUtil = require('../util/hashutil');
16
- const AsyncSender = require('../udp/async_sender');
17
- const PacketTypeEnum = require('../udp/packet_type_enum');
18
- const os = require('os');
19
26
 
20
27
  // 설정을 객체로 통합하여 관리 (참조 성능 향상)
21
28
  var config = {
22
29
  trace_background_socket_enabled: conf.getProperty('trace_background_socket_enabled', true),
23
30
  trace_sampling_enabled: conf.getProperty('trace_sampling_enabled', true),
24
31
  trace_sampling_tps: conf.getProperty('trace_sampling_tps', 1000),
25
- resource_sampling_rate: 0.1 // 리소스 프로파일링을 10%만 수행
32
+ resource_sampling_rate: 0.1,
33
+ profile_batch_size: 20,
34
+ flush_interval: 500
26
35
  };
27
36
 
28
- // 설정 변경 감지를 단일 리스너로 통합
29
37
  conf.on('trace_background_socket_enabled', function (newProps) {
30
38
  config.trace_background_socket_enabled = newProps;
31
39
  });
@@ -60,89 +68,50 @@ var WebsocketObserver = function(agent){
60
68
  }
61
69
  };
62
70
 
63
- // IP 주소 캐싱 (성능 최적화)
64
- this.ipCache = new Map();
65
- };
66
-
67
- // IP 주소 처리 최적화 함수
68
- WebsocketObserver.prototype.getProcessedIp = function(address) {
69
- if (!address) return null;
70
-
71
- if (this.ipCache.has(address)) {
72
- return this.ipCache.get(address);
73
- }
74
-
75
- let host = address;
76
- if (address.includes(':')) {
77
- host = address.substring(address.lastIndexOf(':') + 1);
78
- }
71
+ this.profileBuffer = [];
79
72
 
80
- host = IPUtil.checkIp4(host);
81
- const ipInt = IPUtil.stringToInt(host);
82
- const ipBytes = Buffer.from(IPUtil.stringToBytes(host));
73
+ // 주기적 플러시 설정
74
+ setInterval(() => this.flushProfileBuffer(), config.flush_interval);
83
75
 
84
- const result = { host, ipInt, ipBytes };
85
- this.ipCache.set(address, result);
86
-
87
- // 캐시 크기 관리 (메모리 누수 방지)
88
- if (this.ipCache.size > 10000) {
89
- // 오래된 항목부터 20% 제거
90
- const keysToDelete = Array.from(this.ipCache.keys()).slice(0, Math.floor(this.ipCache.size * 0.2));
91
- keysToDelete.forEach(key => this.ipCache.delete(key));
92
- }
93
-
94
- return result;
76
+ this.ipCache = new Map();
95
77
  };
96
78
 
97
- // 에러 처리 함수
98
- function handleWebSocketError(ctx, err) {
99
- if (!err) return null;
100
-
101
- try {
102
- var errorClass = err.code || err.name || err.constructor?.name || 'WebSocketError';
103
- var errorMessage = err.message || 'websocket error';
104
- var errorStack = '';
105
-
106
- if (conf.trace_sql_error_stack && conf.trace_sql_error_depth && err.stack) {
107
- var traceDepth = conf.trace_sql_error_depth;
108
- var stackLines = err.stack.split("\n");
109
- if (stackLines.length > traceDepth) {
110
- stackLines = stackLines.slice(0, traceDepth + 1);
111
- }
112
- errorStack = stackLines.join("\n");
79
+ WebsocketObserver.prototype.flushProfileBuffer = function() {
80
+ if (this.profileBuffer.length === 0) return;
81
+
82
+ const now = Date.now();
83
+ // 100ms 이상 지난 항목만 처리
84
+ const bufferToFlush = this.profileBuffer.filter(item => (now - item.timestamp) >= 100);
85
+ if (bufferToFlush.length === 0) return;
86
+
87
+ // 최대 batch_size까지만 처리
88
+ let batchToProcess;
89
+ if(bufferToFlush.length > config.profile_batch_size)
90
+ batchToProcess = bufferToFlush.slice(0, config.profile_batch_size);
91
+ else
92
+ batchToProcess = bufferToFlush;
93
+
94
+ // 버퍼에서 처리할 항목 제거
95
+ this.profileBuffer = this.profileBuffer.filter(item =>
96
+ !batchToProcess.some(batch => batch.ctx._id === item.ctx._id)
97
+ );
98
+
99
+ // 배치 처리
100
+ batchToProcess.forEach(item => {
101
+ try {
102
+ DataProfileAgent.sendProfile(item.ctx, item.profile, false);
103
+ // 컨텍스트 정리 (메모리 누수 방지)
104
+ item.ctx = null;
105
+ } catch (e) {
106
+ Logger.printError('WHATAP-615', 'Websocket buffer flush error', e, false);
113
107
  }
108
+ });
114
109
 
115
- var shouldAddError = false;
116
- if (conf._is_trace_ignore_err_cls_contains === true && errorClass &&
117
- errorClass.indexOf(conf.trace_ignore_err_cls_contains) < 0) {
118
- shouldAddError = true;
119
- } else if (conf._is_trace_ignore_err_msg_contains === true && errorMessage &&
120
- errorMessage.indexOf(conf.trace_ignore_err_msg_contains) < 0) {
121
- shouldAddError = true;
122
- } else if (conf._is_trace_ignore_err_cls_contains === false &&
123
- conf._is_trace_ignore_err_msg_contains === false) {
124
- shouldAddError = true;
125
- }
126
-
127
- if (shouldAddError) {
128
- if (!ctx.error) ctx.error = 1;
129
- ctx.status = 500;
130
- var errors = [errorClass];
131
- if (errorMessage) {
132
- errors.push(errorMessage);
133
- }
134
- if (errorStack || err.stack) {
135
- errors.push(errorStack || err.stack);
136
- }
137
-
138
- AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
139
- return errorClass; // 에러 정보 반환
140
- }
141
- } catch (e) {
142
- Logger.printError('WHATAP-218', 'Error handling WebSocket error', e, false);
110
+ if (this.profileBuffer.length > 1000) {
111
+ // 가장 오래된 항목부터 제거
112
+ this.profileBuffer = this.profileBuffer.slice(-500);
143
113
  }
144
- return null;
145
- }
114
+ };
146
115
 
147
116
  WebsocketObserver.prototype.inject = function (mod, moduleName) {
148
117
  if (mod.__whatap_observe__) {
@@ -152,267 +121,172 @@ WebsocketObserver.prototype.inject = function (mod, moduleName) {
152
121
  Logger.initPrint("WebsocketObserver");
153
122
 
154
123
  var self = this;
124
+ var aop = self.agent.aop;
155
125
 
156
- // 1. WebSocketServer 래핑 - request 이벤트 후킹
157
- if (mod.server && mod.server.prototype) {
158
- const wrapWebSocketServer = function(target) {
159
- if (!target || typeof target.on !== 'function') {
160
- return false;
161
- }
162
-
163
- shimmer.wrap(target, 'on', function (original) {
164
- return function (event, listener) {
165
- if (event === 'request') {
166
- return original.call(this, event, function (request) {
167
- // WebSocketConnection의 메시지 전송 메서드들 래핑
168
- request.on('requestAccepted', function(connection) {
169
- self.__wrapWebSocketConnection(connection, request);
170
- });
171
- return listener.apply(this, [request]);
172
- });
173
- }
174
- return original.call(this, event, listener);
175
- };
176
- });
177
-
178
- return true;
179
- };
126
+ // IP 주소 변환 캐시 (동일한 IP 변환 작업 최소화)
127
+ const ipCache = new Map();
180
128
 
181
- wrapWebSocketServer(mod.server.prototype);
182
- }
129
+ function getProcessedIp(remoteAddress) {
130
+ if (!remoteAddress) return null;
183
131
 
184
- // 2. WebSocketConnection 직접 래핑 (구버전 호환성)
185
- if (mod.connection && mod.connection.prototype) {
186
- self.__wrapWebSocketConnection(mod.connection.prototype, null);
187
- }
188
- };
132
+ if (ipCache.has(remoteAddress)) {
133
+ return ipCache.get(remoteAddress);
134
+ }
189
135
 
190
- WebsocketObserver.prototype.__wrapWebSocketConnection = function(connection, request) {
191
- var self = this;
136
+ let host = remoteAddress;
137
+ if (remoteAddress.includes(':')) {
138
+ host = remoteAddress.substring(remoteAddress.lastIndexOf(':') + 1);
139
+ }
192
140
 
193
- if (!connection) return;
141
+ host = IPUtil.checkIp4(host);
142
+ const ipInt = IPUtil.stringToInt(host);
143
+ const ipBytes = Buffer.from(IPUtil.stringToBytes(host));
194
144
 
195
- // send, sendUTF, sendBytes 메서드들 래핑
196
- const methods = ['send', 'sendUTF', 'sendBytes'];
145
+ const result = { host, ipInt, ipBytes };
146
+ ipCache.set(remoteAddress, result);
197
147
 
198
- methods.forEach(function(methodName) {
199
- if (typeof connection[methodName] === 'function' && !connection[methodName].__whatap_wrapped__) {
200
- const originalMethod = connection[methodName];
148
+ return result;
149
+ }
201
150
 
202
- connection[methodName] = function(data) {
203
- if (!config.trace_background_socket_enabled) {
204
- return originalMethod.apply(this, arguments);
205
- }
151
+ aop.after(mod.server.prototype, 'on', function (obj, args, ret, lctx){
152
+ aop.after(mod.connection.prototype, ['send', 'sendUTF', 'sendBytes'], function (obj, args, ret, lctx) {
153
+ if(!config.trace_background_socket_enabled) {
154
+ return;
155
+ }
206
156
 
207
- // 빠른 샘플링 체크 (조기 반환으로 성능 향상)
208
- if (config.trace_sampling_enabled && !self.socketCounter.checkSampling()) {
209
- return originalMethod.apply(this, arguments);
157
+ if(config.trace_sampling_enabled) {
158
+ if(!self.socketCounter.checkSampling()) {
159
+ MeterService.add(0, 1, 0, 0, 0, 0);
160
+ return;
210
161
  }
162
+ }
211
163
 
212
- // 브로드캐스트 감지 시도
213
- const emitType = self.__detectBroadcastType();
164
+ var protocol = obj.protocol;
165
+ var remoteAddress = obj.remoteAddress;
214
166
 
215
- const remoteAddress = this.remoteAddress ||
216
- (this.socket ? this.socket.remoteAddress : null) ||
217
- (request && request.remoteAddress ? request.remoteAddress : null);
167
+ // 리소스 프로파일링 최적화 - 모든 트랜잭션 대신 샘플링
168
+ const shouldProfileResource = Math.random() < config.resource_sampling_rate; // 10%만 리소스 프로파일링
218
169
 
219
- TraceContextManager._asyncLocalStorage.run(self.__initCtx(this, data, emitType, {
220
- remoteAddress: remoteAddress,
221
- protocol: this.protocol,
222
- request: request
223
- }), () => {
170
+ TraceContextManager._asyncLocalStorage.run(initCtx(obj, args, shouldProfileResource), () => {
171
+ try {
224
172
  var ctx = TraceContextManager._asyncLocalStorage.getStore();
225
173
  if (!ctx) {
226
- return originalMethod.apply(this, arguments);
174
+ return;
227
175
  }
228
176
 
229
- try {
230
- ctx.service_name = "/websocket";
231
- let hostname = '';
232
-
233
- let datas = [
234
- hostname,
235
- ctx.service_name,
236
- ctx.remoteIp || 0,
237
- ctx.userAgentString || '',
238
- ctx.referer || '',
239
- String(ctx.userid || 0),
240
- String(false), // isStaticContents
241
- emitType
242
- ];
243
-
244
- AsyncSender.send_packet(PacketTypeEnum.TX_START, ctx, datas);
245
-
246
- self.__sendWebSocketMessage(ctx, emitType, data, methodName);
247
- self.__sendWebSocketEvent(ctx, this, data, emitType, remoteAddress);
248
- self.__endTransaction(null, ctx);
249
-
250
- return originalMethod.apply(this, arguments);
251
- } catch (e) {
252
- Logger.printError('WHATAP-219', 'websocket send transaction error', e, false);
253
- var errorInfo = handleWebSocketError(ctx, e);
254
- self.__endTransaction(errorInfo, ctx);
255
- return originalMethod.apply(this, arguments);
256
- }
257
- });
258
- };
177
+ // IP 주소 처리 최적화
178
+ const ipInfo = getProcessedIp(remoteAddress);
179
+ if (!ipInfo) return;
180
+
181
+ // 불필요한 footprint 로깅 제거 (메모리 사용 감소)
182
+ ctx.socket_connecting = true;
259
183
 
260
- connection[methodName].__whatap_wrapped__ = true;
184
+ // 프로토콜 정보 수집 최적화
185
+ if (protocol) {
186
+ var protocol_hash = HashUtil.hashFromString('Protocol');
187
+ var step = new MessageStep();
188
+ step.hash = protocol_hash;
189
+ step.start_time = ctx.getElapsedTime();
190
+ step.desc = protocol;
261
191
 
262
- // 속성 복사
263
- Object.defineProperties(connection[methodName], {
264
- length: { value: originalMethod.length },
265
- name: { value: originalMethod.name }
192
+ DataTextAgent.MESSAGE.add(protocol_hash, 'Protocol');
193
+ ctx.profile.add(step);
194
+ }
195
+
196
+ // 소켓 단계 기록 (메모리 사용 최적화)
197
+ var step = new SocketStep();
198
+ step.start_time = ctx.getElapsedTime();
199
+ step.ipaddr = ipInfo.ipBytes;
200
+
201
+ ctx.socket_connecting = false;
202
+ step.elapsed = ctx.getElapsedTime() - step.start_time;
203
+ ctx.profile.push(step);
204
+
205
+ // 트랜잭션 종료 처리 최적화
206
+ self.__endTransaction(null, ctx);
207
+ return;
208
+ } catch (e) {
209
+ Logger.printError('WHATAP-616', 'Websocket transaction error', e, false);
210
+ return;
211
+ }
266
212
  });
267
- }
213
+ });
268
214
  });
269
215
  };
270
216
 
271
- WebsocketObserver.prototype.__detectBroadcastType = function() {
217
+ WebsocketObserver.prototype.__endTransaction = function(error, ctx) {
272
218
  try {
273
- // 스택 트레이스를 통해 브로드캐스트 패턴 감지
274
- const stack = (new Error()).stack;
275
- if (stack) {
276
- // connections.forEach, broadcast 패턴 감지
277
- if (stack.includes('connections.forEach') ||
278
- stack.includes('connections.map') ||
279
- stack.includes('broadcast') ||
280
- stack.includes('sendToAll')) {
281
- return 'broadcast';
282
- }
219
+ var profile = new ProfilePack();
220
+ var wtx = new TxRecord();
221
+ wtx.endTime = DateUtil.currentTime();
222
+ profile.time = wtx.endTime;
223
+ wtx.elapsed = ctx.getElapsedTime();
224
+
225
+ DataTextAgent.SERVICE.add(ctx.service_hash, ctx.service_name);
226
+
227
+ wtx.seq = ctx.txid;
228
+ wtx.service = ctx.service_hash;
229
+
230
+ // CPU와 메모리 측정은 샘플링된 트랜잭션에만 수행
231
+ if (ctx.shouldProfileResource) {
232
+ wtx.cpuTime = ResourceProfile.getCPUTime() - ctx.start_cpu;
233
+ wtx.malloc = ResourceProfile.getUsedHeapSize() - ctx.start_malloc;
234
+ if(wtx.malloc < 0) { wtx.malloc = 0; }
235
+ } else {
236
+ wtx.cpuTime = 0;
237
+ wtx.malloc = 0;
283
238
  }
284
- return 'websocket';
285
- } catch (e) {
286
- return 'websocket';
287
- }
288
- };
289
239
 
290
- WebsocketObserver.prototype.__sendWebSocketMessage = function(ctx, emitType, data, methodName) {
291
- try {
292
- var title, message;
293
-
294
- switch (emitType) {
295
- case 'broadcast':
296
- title = "Broadcast";
297
- message = `Broadcast to all connections using ${methodName}`;
298
- break;
299
- default:
300
- title = "WebSocket";
301
- message = `Direct WebSocket message using ${methodName}`;
302
- }
240
+ wtx.status = 2;
241
+ wtx.ipaddr = ctx.remoteIp;
303
242
 
304
- AsyncSender.send_packet(PacketTypeEnum.TX_MSG, ctx, [title, '', message]);
305
- } catch (e) {
306
- Logger.printError('WHATAP-220', 'WebSocket send message error', e, false);
307
- throw e;
308
- }
309
- };
243
+ MeterService.add(0, wtx.elapsed, 0, SecurityMaster.PCODE, SecurityMaster.OKIND, SecurityMaster.OID);
310
244
 
311
- WebsocketObserver.prototype.__sendWebSocketEvent = function(ctx, connection, data, emitType, remoteAddress) {
312
- try {
313
- var start_time = Date.now();
314
- ctx.start_time = start_time;
315
- ctx.elapsed = 0;
316
- ctx.error = 0;
317
-
318
- // IP 주소 및 포트 처리
319
- var ipString = "0.0.0.0";
320
- var port = 0;
321
-
322
- if (remoteAddress) {
323
- const ipInfo = this.getProcessedIp(remoteAddress);
324
- if (ipInfo) {
325
- ipString = ipInfo.host;
326
- ctx.remoteIp = ipInfo.ipInt;
327
-
328
- // 포트 정보 추출
329
- if (connection.socket && connection.socket.remotePort) {
330
- port = Number(connection.socket.remotePort);
331
- } else if (remoteAddress.includes(':')) {
332
- const parts = remoteAddress.split(':');
333
- if (parts.length > 1) {
334
- const portStr = parts[parts.length - 1];
335
- const parsedPort = parseInt(portStr, 10);
336
- if (!isNaN(parsedPort)) {
337
- port = parsedPort;
338
- }
339
- }
340
- }
341
- }
342
- }
343
-
344
- var elapsed = Date.now() - start_time;
345
- ctx.elapsed = elapsed;
346
-
347
- let datas = [ipString, port, elapsed, 0];
348
- AsyncSender.send_packet(PacketTypeEnum.TX_WEB_SOCKET, ctx, datas);
349
- } catch (e) {
350
- Logger.printError('WHATAP-221', 'WebSocket send event error', e, false);
351
- throw e;
352
- }
353
- };
245
+ profile.oid = SecurityMaster.OID;
246
+ profile.service = wtx;
354
247
 
355
- WebsocketObserver.prototype.__endTransaction = function(error, ctx) {
356
- try {
357
- if (error) {
358
- TraceContextManager.end(ctx != null ? ctx.id : null);
359
- ctx = null;
360
- return;
361
- }
248
+ // 트랜잭션 컨텍스트 종료 (메모리 누수 방지)
249
+ TraceContextManager.end(ctx._id);
362
250
 
363
- if (ctx == null || TraceContextManager.isExist(ctx.id) === false)
364
- return;
365
-
366
- ctx.start_time = Date.now();
367
- let datas = [
368
- os.hostname(), // hostname
369
- ctx.service_name,
370
- 0, // mtid
371
- 0, // mdepth
372
- 0, // mcaller_txid
373
- 0, // mcaller_pcode
374
- '', // mcaller_spec
375
- String(0), // mcaller_url_hash
376
- 200
377
- ];
378
- ctx.elapsed = Date.now() - ctx.start_time;
379
- AsyncSender.send_packet(PacketTypeEnum.TX_END, ctx, datas);
380
- TraceContextManager.end(ctx.id);
251
+ // 프로파일 데이터 버퍼에 추가 (비동기 처리 최적화)
252
+ this.profileBuffer.push({
253
+ ctx: ctx,
254
+ profile: profile,
255
+ timestamp: Date.now()
256
+ });
381
257
 
382
258
  } catch (e) {
383
- Logger.printError('WHATAP-222', 'WebSocket end transaction error', e, false);
384
- TraceContextManager.end(ctx.id);
259
+ Logger.printError('WHATAP-615', 'Websocket end transaction error', e, false);
260
+ // 에러 발생 시에도 컨텍스트 정리
261
+ TraceContextManager.end(ctx._id);
385
262
  ctx = null;
386
263
  }
387
264
  };
388
265
 
389
- WebsocketObserver.prototype.__initCtx = function(connection, data, emitType, contextInfo) {
266
+ // 컨텍스트 초기화 최적화
267
+ function initCtx(socket, args, shouldProfileResource) {
390
268
  const ctx = TraceContextManager.start();
391
- if (!ctx) return null;
269
+ if (!ctx) {return;}
392
270
 
393
271
  var remote_addr;
394
- var remoteAddress = contextInfo.remoteAddress;
395
-
396
- if (remoteAddress) {
397
- if(remoteAddress && remoteAddress.includes(':')){
398
- remote_addr = remoteAddress.substring(remoteAddress.lastIndexOf(':')+1);
399
- }
272
+ const address = socket.remoteAddress;
273
+ if(address && address.includes(':')){
274
+ remote_addr = address.substring(address.lastIndexOf(':')+1);
400
275
  }
401
276
 
402
- ctx.service_name = "/websocket";
403
-
404
- if (remote_addr) {
405
- remote_addr = IPUtil.checkIp4(remote_addr);
406
- ctx.remoteIp = IPUtil.stringToInt(remote_addr);
407
- ctx.userid = ctx.remoteIp;
277
+ // 리소스 측정은 샘플링된 트랜잭션에만 수행 (CPU 사용 감소)
278
+ ctx.shouldProfileResource = shouldProfileResource;
279
+ if (shouldProfileResource) {
280
+ ctx.start_malloc = ResourceProfile.getUsedHeapSize();
281
+ ctx.start_cpu = ResourceProfile.getCPUTime();
408
282
  }
409
283
 
410
- // 기본값 설정
411
- ctx.userAgentString = '';
412
- ctx.referer = '';
413
- ctx.status = 200;
284
+ remote_addr = IPUtil.checkIp4(remote_addr);
285
+ ctx.remoteIp = IPUtil.stringToInt(remote_addr);
286
+ ctx.userid = Long.fromNumber(ctx.remoteIp);
287
+ MeterUsers.add(ctx.userid);
414
288
 
415
289
  return ctx;
416
- };
290
+ }
417
291
 
418
292
  exports.WebsocketObserver = WebsocketObserver;
@@ -0,0 +1,55 @@
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 Pack = require('./pack'),
8
+ PackEnum = require('./packenum'),
9
+ Long = require('long');
10
+
11
+ function ActiveStackPack() {
12
+ Pack.call(this);
13
+
14
+ this.seq = Long.ZERO;
15
+ this.txid = Long.ZERO;
16
+ this.service = 0;
17
+ this.elapsed = 0;
18
+
19
+ this.callstack = [];
20
+ this.callstack_hash = 0;
21
+ }
22
+
23
+ ActiveStackPack.prototype = new Pack();
24
+ ActiveStackPack.prototype.constructor = ActiveStackPack;
25
+ ActiveStackPack.prototype.getPackType = function () {
26
+ return PackEnum.ACTIVESTACK_1;
27
+ };
28
+ ActiveStackPack.prototype.write = function(dout) {
29
+ Pack.prototype.write.call(this, dout);
30
+ dout.writeByte(1);
31
+ dout.writeLong(this.seq);
32
+ dout.writeLong(this.txid);
33
+ dout.writeDecimal(this.service);
34
+ dout.writeDecimal(this.callstack_hash);
35
+ dout.writeIntArray(this.callstack);
36
+ dout.writeDecimal(this.elapsed);
37
+ };
38
+ ActiveStackPack.prototype.read = function(din) {
39
+
40
+ Pack.prototype.read.call(this, din);
41
+ var ver= din.readByte();
42
+ this.seq = din.readLong();
43
+ this.txid = din.readLong();
44
+ this.service = din.readDecNumber();
45
+
46
+ this.callstack_hash = din.readDecNumber();
47
+ this.callstack = din.readIntArray();
48
+ if(ver>0){
49
+ this.elapsed = din.readDecNumber();
50
+ }
51
+ return this;
52
+ };
53
+
54
+
55
+ module.exports = ActiveStackPack;
@@ -0,0 +1,8 @@
1
+ exports.AP_JAVA = 1;
2
+ exports.AP_NODE = 2;
3
+ exports.AP_PYTHON = 3;
4
+ exports.AP_PHP = 4;
5
+ exports.AP_URL_ADM = 5;
6
+ exports.BSM = 6;
7
+ exports.WEB_CHECK = 9;
8
+ exports.OTEL = 10;
@@ -11,6 +11,8 @@ var Pack = require('./pack'),
11
11
  IntMapValue = require('./../value/int-map-value'),
12
12
  IntValue = require('./../value/int-value'),
13
13
  FloatValue = require('./../value/float-value'),
14
+ NETSTAT = require('./netstat'),
15
+ WEBSOCKET = require('./websocket'),
14
16
  IntIntMap = require('../util/intint-map'),
15
17
  IntKeyLinkedMap = require('../util/intkey-linkedmap'),
16
18
  LongKeyLinkedMap = require('../util/longkey-linkedmap'),
@@ -71,6 +73,7 @@ function CounterPack() {
71
73
 
72
74
  this.db_num_active = null;
73
75
  this.db_num_idle = null;
76
+ this.netstat = null;
74
77
 
75
78
  this.host_ip = 0;
76
79
  this.proc_fd = 0;