whatap 1.0.2 → 1.0.3-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/README.md +77 -33
  2. package/agent/darwin/arm64/whatap_nodejs +0 -0
  3. package/agent/linux/amd64/whatap_nodejs +0 -0
  4. package/agent/linux/arm64/whatap_nodejs +0 -0
  5. package/build.txt +4 -0
  6. package/lib/conf/config-default.js +3 -10
  7. package/lib/conf/configure.js +349 -369
  8. package/lib/conf/license.js +1 -1
  9. package/lib/control/packagectr-helper.js +3 -34
  10. package/lib/core/agent.js +888 -176
  11. package/lib/core/interceptor.js +6 -6
  12. package/lib/core/shimmer.js +36 -82
  13. package/lib/counter/counter-manager.js +8 -79
  14. package/lib/counter/task/activetransaction.js +17 -68
  15. package/lib/io/data-inputx.js +3 -13
  16. package/lib/io/data-outputx.js +206 -268
  17. package/lib/logger.js +6 -6
  18. package/lib/net/security-master.js +20 -139
  19. package/lib/observers/apollo-server-observer.js +27 -33
  20. package/lib/observers/global-observer.js +80 -155
  21. package/lib/observers/http-observer.js +236 -666
  22. package/lib/observers/ioredis-observer.js +294 -0
  23. package/lib/observers/maria-observer.js +362 -204
  24. package/lib/observers/mongodb-observer.js +226 -169
  25. package/lib/observers/mongoose-observer.js +323 -518
  26. package/lib/observers/mssql-observer.js +418 -177
  27. package/lib/observers/mysql-observer.js +449 -342
  28. package/lib/observers/mysql2-observer.js +358 -396
  29. package/lib/observers/oracle-observer.js +384 -559
  30. package/lib/observers/pgsql-observer.js +489 -231
  31. package/lib/observers/prisma-observer.js +92 -303
  32. package/lib/observers/process-observer.js +35 -79
  33. package/lib/observers/redis-observer.js +331 -166
  34. package/lib/observers/socket.io-observer.js +187 -226
  35. package/lib/observers/websocket-observer.js +301 -175
  36. package/lib/pack/counter-pack.js +0 -3
  37. package/lib/pack/log-sink-pack.js +52 -14
  38. package/lib/pack/tagcount-pack.js +4 -4
  39. package/lib/{counter/task → system}/gc-action.js +74 -27
  40. package/lib/trace/trace-context-manager.js +25 -113
  41. package/lib/trace/trace-context.js +7 -21
  42. package/lib/trace/trace-httpc.js +11 -17
  43. package/lib/trace/trace-sql.js +21 -29
  44. package/lib/udp/async_sender.js +119 -0
  45. package/lib/udp/index.js +17 -0
  46. package/lib/udp/packet_enum.js +52 -0
  47. package/lib/udp/packet_queue.js +69 -0
  48. package/lib/udp/packet_type_enum.js +33 -0
  49. package/lib/udp/param_def.js +72 -0
  50. package/lib/udp/udp_session.js +336 -0
  51. package/lib/util/escape-literal-sql.js +5 -5
  52. package/lib/util/hashutil.js +18 -18
  53. package/lib/util/keygen.js +3 -0
  54. package/lib/util/linkedset.js +2 -1
  55. package/lib/util/nodeutil.js +1 -2
  56. package/lib/util/sql-util.js +178 -0
  57. package/lib/util/trace-helper.js +91 -0
  58. package/lib/util/transfer.js +58 -0
  59. package/lib/value/map-value.js +2 -3
  60. package/package.json +5 -9
  61. package/whatap.conf +4 -1
  62. package/lib/conf/conf-sys-mon.js +0 -101
  63. package/lib/control/cmd-config.js +0 -24
  64. package/lib/control/control-handler.js +0 -367
  65. package/lib/core/request-agent.js +0 -27
  66. package/lib/counter/meter/meter-activex.js +0 -67
  67. package/lib/counter/meter/meter-httpc.js +0 -57
  68. package/lib/counter/meter/meter-resource.js +0 -9
  69. package/lib/counter/meter/meter-service.js +0 -168
  70. package/lib/counter/meter/meter-socket.io.js +0 -51
  71. package/lib/counter/meter/meter-sql.js +0 -71
  72. package/lib/counter/meter/meter-users.js +0 -58
  73. package/lib/counter/meter.js +0 -183
  74. package/lib/counter/task/agentinfo.js +0 -107
  75. package/lib/counter/task/gcstat.js +0 -34
  76. package/lib/counter/task/heapmem.js +0 -25
  77. package/lib/counter/task/httpc.js +0 -76
  78. package/lib/counter/task/metering-info.js +0 -125
  79. package/lib/counter/task/proc-cpu.js +0 -29
  80. package/lib/counter/task/realtimeuser.js +0 -31
  81. package/lib/counter/task/res/systemECSTask.js +0 -39
  82. package/lib/counter/task/res/systemKubeTask.js +0 -53
  83. package/lib/counter/task/res/util/awsEcsClientThread.js +0 -218
  84. package/lib/counter/task/res/util/linuxProcStatUtil.js +0 -14
  85. package/lib/counter/task/res-sys-cpu.js +0 -62
  86. package/lib/counter/task/service.js +0 -202
  87. package/lib/counter/task/socketio.js +0 -30
  88. package/lib/counter/task/sql.js +0 -105
  89. package/lib/counter/task/systemperf.js +0 -43
  90. package/lib/data/datapack-sender.js +0 -289
  91. package/lib/data/dataprofile-agent.js +0 -162
  92. package/lib/data/datatext-agent.js +0 -135
  93. package/lib/data/event-level.js +0 -15
  94. package/lib/data/test.js +0 -49
  95. package/lib/data/zipprofile.js +0 -197
  96. package/lib/env/constants.js +0 -21
  97. package/lib/error/error-handler.js +0 -437
  98. package/lib/kube/kube-client.js +0 -144
  99. package/lib/lang/text-types.js +0 -58
  100. package/lib/logsink/line-log-util.js +0 -87
  101. package/lib/logsink/line-log.js +0 -12
  102. package/lib/logsink/log-sender.js +0 -78
  103. package/lib/logsink/log-tracer.js +0 -40
  104. package/lib/logsink/sender-util.js +0 -56
  105. package/lib/logsink/zip/zip-send.js +0 -177
  106. package/lib/net/netflag.js +0 -55
  107. package/lib/net/receiver.js +0 -66
  108. package/lib/net/sender.js +0 -141
  109. package/lib/net/tcp-return.js +0 -18
  110. package/lib/net/tcp-session.js +0 -286
  111. package/lib/net/tcpreq-client-proxy.js +0 -70
  112. package/lib/net/tcprequest-mgr.js +0 -58
  113. package/lib/observers/cluster-observer.js +0 -22
  114. package/lib/observers/express-observer.js +0 -215
  115. package/lib/observers/file-observer.js +0 -184
  116. package/lib/observers/grpc-observer.js +0 -336
  117. package/lib/observers/memcached-observer.js +0 -56
  118. package/lib/observers/mongo-observer.js +0 -317
  119. package/lib/observers/net-observer.js +0 -77
  120. package/lib/observers/promise-observer.js +0 -31
  121. package/lib/observers/schedule-observer.js +0 -67
  122. package/lib/observers/stream-observer.js +0 -19
  123. package/lib/observers/thrift-observer.js +0 -197
  124. package/lib/pack/activestack-pack.js +0 -55
  125. package/lib/pack/apenum.js +0 -8
  126. package/lib/pack/errorsnap-pack.js +0 -69
  127. package/lib/pack/event-pack.js +0 -54
  128. package/lib/pack/hitmap-pack.js +0 -63
  129. package/lib/pack/hitmap-pack1.js +0 -152
  130. package/lib/pack/netstat.js +0 -15
  131. package/lib/pack/otype.js +0 -7
  132. package/lib/pack/profile-pack.js +0 -49
  133. package/lib/pack/realtimeuser-pack.js +0 -41
  134. package/lib/pack/stat-general-pack.js +0 -96
  135. package/lib/pack/staterror-pack.js +0 -120
  136. package/lib/pack/stathttpc-pack.js +0 -66
  137. package/lib/pack/stathttpc-rec.js +0 -78
  138. package/lib/pack/statremote-pack.js +0 -46
  139. package/lib/pack/statservice-pack.js +0 -63
  140. package/lib/pack/statservice-pack1.js +0 -88
  141. package/lib/pack/statservice-rec.js +0 -292
  142. package/lib/pack/statservice-rec_dep.js +0 -151
  143. package/lib/pack/statsql-pack.js +0 -69
  144. package/lib/pack/statsql-rec.js +0 -100
  145. package/lib/pack/statuseragent-pack.js +0 -44
  146. package/lib/pack/tagctr.js +0 -15
  147. package/lib/pack/text-pack.js +0 -50
  148. package/lib/pack/time-count.js +0 -25
  149. package/lib/pack/websocket.js +0 -15
  150. package/lib/pack/zip-pack.js +0 -70
  151. package/lib/pii/pii-item.js +0 -31
  152. package/lib/pii/pii-mask.js +0 -174
  153. package/lib/plugin/plugin-loadermanager.js +0 -57
  154. package/lib/plugin/plugin.js +0 -75
  155. package/lib/service/tx-record.js +0 -332
  156. package/lib/stat/stat-error.js +0 -116
  157. package/lib/stat/stat-httpc.js +0 -98
  158. package/lib/stat/stat-remote-ip.js +0 -46
  159. package/lib/stat/stat-remote-ipurl.js +0 -88
  160. package/lib/stat/stat-sql.js +0 -113
  161. package/lib/stat/stat-tranx.js +0 -58
  162. package/lib/stat/stat-tx-caller.js +0 -160
  163. package/lib/stat/stat-tx-domain.js +0 -111
  164. package/lib/stat/stat-tx-referer.js +0 -112
  165. package/lib/stat/stat-useragent.js +0 -48
  166. package/lib/stat/timingsender.js +0 -76
  167. package/lib/step/activestack-step.js +0 -38
  168. package/lib/step/dbc-step.js +0 -36
  169. package/lib/step/http-stepx.js +0 -67
  170. package/lib/step/message-step.js +0 -40
  171. package/lib/step/method-stepx.js +0 -45
  172. package/lib/step/resultset-step.js +0 -40
  173. package/lib/step/securemsg-step.js +0 -44
  174. package/lib/step/socket-step.js +0 -46
  175. package/lib/step/sql-stepx.js +0 -68
  176. package/lib/step/sqlxtype.js +0 -16
  177. package/lib/step/step.js +0 -66
  178. package/lib/step/stepenum.js +0 -54
  179. package/lib/topology/link.js +0 -63
  180. package/lib/topology/nodeinfo.js +0 -123
  181. package/lib/topology/status-detector.js +0 -111
  182. package/lib/util/anylist.js +0 -103
  183. package/lib/util/cardinality/hyperloglog.js +0 -106
  184. package/lib/util/cardinality/murmurhash.js +0 -31
  185. package/lib/util/cardinality/registerset.js +0 -75
  186. package/lib/util/errordata.js +0 -21
  187. package/lib/util/iputil_x.js +0 -527
  188. package/lib/util/kube-util.js +0 -73
  189. package/lib/util/paramsecurity.js +0 -80
  190. package/lib/util/pre-process.js +0 -13
  191. package/lib/util/process-seq.js +0 -166
  192. package/lib/util/property-util.js +0 -36
  193. package/lib/util/request-queue.js +0 -70
  194. package/lib/util/requestdouble-queue.js +0 -72
  195. package/lib/util/resourceprofile.js +0 -157
  196. package/lib/util/stop-watch.js +0 -30
  197. package/lib/util/system-util.js +0 -10
  198. package/lib/util/userid-util.js +0 -57
@@ -4,36 +4,28 @@
4
4
  * can be found in the LICENSE file.
5
5
  */
6
6
 
7
-
8
7
  var TraceContextManager = require('../trace/trace-context-manager'),
9
- SocketStep = require('../step/socket-step'),
10
8
  conf = require('../conf/configure'),
11
9
  IPUtil = require('../util/iputil'),
12
10
  Logger = require('../logger');
13
11
  const {Detector: URLPatternDetector} = require("../trace/serviceurl-pattern-detector");
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;
12
+ const DateUtil = require('../util/dateutil');
13
+ const SecurityMaster = require('../net/security-master');
25
14
  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');
26
19
 
27
20
  // 설정을 객체로 통합하여 관리 (참조 성능 향상)
28
21
  var config = {
29
22
  trace_background_socket_enabled: conf.getProperty('trace_background_socket_enabled', true),
30
23
  trace_sampling_enabled: conf.getProperty('trace_sampling_enabled', true),
31
24
  trace_sampling_tps: conf.getProperty('trace_sampling_tps', 1000),
32
- resource_sampling_rate: 0.1,
33
- profile_batch_size: 20,
34
- flush_interval: 500
25
+ resource_sampling_rate: 0.1 // 리소스 프로파일링을 10%만 수행
35
26
  };
36
27
 
28
+ // 설정 변경 감지를 단일 리스너로 통합
37
29
  conf.on('trace_background_socket_enabled', function (newProps) {
38
30
  config.trace_background_socket_enabled = newProps;
39
31
  });
@@ -68,50 +60,89 @@ var WebsocketObserver = function(agent){
68
60
  }
69
61
  };
70
62
 
71
- this.profileBuffer = [];
63
+ // IP 주소 캐싱 (성능 최적화)
64
+ this.ipCache = new Map();
65
+ };
72
66
 
73
- // 주기적 플러시 설정
74
- setInterval(() => this.flushProfileBuffer(), config.flush_interval);
67
+ // IP 주소 처리 최적화 함수
68
+ WebsocketObserver.prototype.getProcessedIp = function(address) {
69
+ if (!address) return null;
75
70
 
76
- this.ipCache = new Map();
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
+ }
79
+
80
+ host = IPUtil.checkIp4(host);
81
+ const ipInt = IPUtil.stringToInt(host);
82
+ const ipBytes = Buffer.from(IPUtil.stringToBytes(host));
83
+
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;
77
95
  };
78
96
 
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);
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");
113
+ }
114
+
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;
107
125
  }
108
- });
109
126
 
110
- if (this.profileBuffer.length > 1000) {
111
- // 가장 오래된 항목부터 제거
112
- this.profileBuffer = this.profileBuffer.slice(-500);
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);
113
143
  }
114
- };
144
+ return null;
145
+ }
115
146
 
116
147
  WebsocketObserver.prototype.inject = function (mod, moduleName) {
117
148
  if (mod.__whatap_observe__) {
@@ -121,172 +152,267 @@ WebsocketObserver.prototype.inject = function (mod, moduleName) {
121
152
  Logger.initPrint("WebsocketObserver");
122
153
 
123
154
  var self = this;
124
- var aop = self.agent.aop;
125
155
 
126
- // IP 주소 변환 캐시 (동일한 IP 변환 작업 최소화)
127
- const ipCache = new Map();
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
+ });
128
177
 
129
- function getProcessedIp(remoteAddress) {
130
- if (!remoteAddress) return null;
178
+ return true;
179
+ };
131
180
 
132
- if (ipCache.has(remoteAddress)) {
133
- return ipCache.get(remoteAddress);
134
- }
181
+ wrapWebSocketServer(mod.server.prototype);
182
+ }
135
183
 
136
- let host = remoteAddress;
137
- if (remoteAddress.includes(':')) {
138
- host = remoteAddress.substring(remoteAddress.lastIndexOf(':') + 1);
139
- }
184
+ // 2. WebSocketConnection 직접 래핑 (구버전 호환성)
185
+ if (mod.connection && mod.connection.prototype) {
186
+ self.__wrapWebSocketConnection(mod.connection.prototype, null);
187
+ }
188
+ };
140
189
 
141
- host = IPUtil.checkIp4(host);
142
- const ipInt = IPUtil.stringToInt(host);
143
- const ipBytes = Buffer.from(IPUtil.stringToBytes(host));
190
+ WebsocketObserver.prototype.__wrapWebSocketConnection = function(connection, request) {
191
+ var self = this;
144
192
 
145
- const result = { host, ipInt, ipBytes };
146
- ipCache.set(remoteAddress, result);
193
+ if (!connection) return;
147
194
 
148
- return result;
149
- }
195
+ // send, sendUTF, sendBytes 메서드들 래핑
196
+ const methods = ['send', 'sendUTF', 'sendBytes'];
150
197
 
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
- }
198
+ methods.forEach(function(methodName) {
199
+ if (typeof connection[methodName] === 'function' && !connection[methodName].__whatap_wrapped__) {
200
+ const originalMethod = connection[methodName];
156
201
 
157
- if(config.trace_sampling_enabled) {
158
- if(!self.socketCounter.checkSampling()) {
159
- MeterService.add(0, 1, 0, 0, 0, 0);
160
- return;
202
+ connection[methodName] = function(data) {
203
+ if (!config.trace_background_socket_enabled) {
204
+ return originalMethod.apply(this, arguments);
205
+ }
206
+
207
+ // 빠른 샘플링 체크 (조기 반환으로 성능 향상)
208
+ if (config.trace_sampling_enabled && !self.socketCounter.checkSampling()) {
209
+ return originalMethod.apply(this, arguments);
161
210
  }
162
- }
163
211
 
164
- var protocol = obj.protocol;
165
- var remoteAddress = obj.remoteAddress;
212
+ // 브로드캐스트 감지 시도
213
+ const emitType = self.__detectBroadcastType();
166
214
 
167
- // 리소스 프로파일링 최적화 - 모든 트랜잭션 대신 샘플링
168
- const shouldProfileResource = Math.random() < config.resource_sampling_rate; // 10%만 리소스 프로파일링
215
+ const remoteAddress = this.remoteAddress ||
216
+ (this.socket ? this.socket.remoteAddress : null) ||
217
+ (request && request.remoteAddress ? request.remoteAddress : null);
169
218
 
170
- TraceContextManager._asyncLocalStorage.run(initCtx(obj, args, shouldProfileResource), () => {
171
- try {
219
+ TraceContextManager._asyncLocalStorage.run(self.__initCtx(this, data, emitType, {
220
+ remoteAddress: remoteAddress,
221
+ protocol: this.protocol,
222
+ request: request
223
+ }), () => {
172
224
  var ctx = TraceContextManager._asyncLocalStorage.getStore();
173
225
  if (!ctx) {
174
- return;
226
+ return originalMethod.apply(this, arguments);
175
227
  }
176
228
 
177
- // IP 주소 처리 최적화
178
- const ipInfo = getProcessedIp(remoteAddress);
179
- if (!ipInfo) return;
180
-
181
- // 불필요한 footprint 로깅 제거 (메모리 사용 감소)
182
- ctx.socket_connecting = true;
183
-
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;
191
-
192
- DataTextAgent.MESSAGE.add(protocol_hash, 'Protocol');
193
- ctx.profile.add(step);
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);
194
256
  }
257
+ });
258
+ };
195
259
 
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
- }
260
+ connection[methodName].__whatap_wrapped__ = true;
261
+
262
+ // 속성 복사
263
+ Object.defineProperties(connection[methodName], {
264
+ length: { value: originalMethod.length },
265
+ name: { value: originalMethod.name }
212
266
  });
213
- });
267
+ }
214
268
  });
215
269
  };
216
270
 
217
- WebsocketObserver.prototype.__endTransaction = function(error, ctx) {
271
+ WebsocketObserver.prototype.__detectBroadcastType = function() {
218
272
  try {
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;
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
+ }
238
283
  }
284
+ return 'websocket';
285
+ } catch (e) {
286
+ return 'websocket';
287
+ }
288
+ };
239
289
 
240
- wtx.status = 2;
241
- wtx.ipaddr = ctx.remoteIp;
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
+ }
242
303
 
243
- MeterService.add(0, wtx.elapsed, 0, SecurityMaster.PCODE, SecurityMaster.OKIND, SecurityMaster.OID);
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
+ };
244
310
 
245
- profile.oid = SecurityMaster.OID;
246
- profile.service = wtx;
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
+ };
247
354
 
248
- // 트랜잭션 컨텍스트 종료 (메모리 누수 방지)
249
- TraceContextManager.end(ctx._id);
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
+ }
250
362
 
251
- // 프로파일 데이터 버퍼에 추가 (비동기 처리 최적화)
252
- this.profileBuffer.push({
253
- ctx: ctx,
254
- profile: profile,
255
- timestamp: Date.now()
256
- });
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);
257
381
 
258
382
  } catch (e) {
259
- Logger.printError('WHATAP-615', 'Websocket end transaction error', e, false);
260
- // 에러 발생 시에도 컨텍스트 정리
261
- TraceContextManager.end(ctx._id);
383
+ Logger.printError('WHATAP-222', 'WebSocket end transaction error', e, false);
384
+ TraceContextManager.end(ctx.id);
262
385
  ctx = null;
263
386
  }
264
387
  };
265
388
 
266
- // 컨텍스트 초기화 최적화
267
- function initCtx(socket, args, shouldProfileResource) {
389
+ WebsocketObserver.prototype.__initCtx = function(connection, data, emitType, contextInfo) {
268
390
  const ctx = TraceContextManager.start();
269
- if (!ctx) {return;}
391
+ if (!ctx) return null;
270
392
 
271
393
  var remote_addr;
272
- const address = socket.remoteAddress;
273
- if(address && address.includes(':')){
274
- remote_addr = address.substring(address.lastIndexOf(':')+1);
394
+ var remoteAddress = contextInfo.remoteAddress;
395
+
396
+ if (remoteAddress) {
397
+ if(remoteAddress && remoteAddress.includes(':')){
398
+ remote_addr = remoteAddress.substring(remoteAddress.lastIndexOf(':')+1);
399
+ }
275
400
  }
276
401
 
277
- // 리소스 측정은 샘플링된 트랜잭션에만 수행 (CPU 사용 감소)
278
- ctx.shouldProfileResource = shouldProfileResource;
279
- if (shouldProfileResource) {
280
- ctx.start_malloc = ResourceProfile.getUsedHeapSize();
281
- ctx.start_cpu = ResourceProfile.getCPUTime();
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;
282
408
  }
283
409
 
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);
410
+ // 기본값 설정
411
+ ctx.userAgentString = '';
412
+ ctx.referer = '';
413
+ ctx.status = 200;
288
414
 
289
415
  return ctx;
290
- }
416
+ };
291
417
 
292
418
  exports.WebsocketObserver = WebsocketObserver;
@@ -11,8 +11,6 @@ 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'),
16
14
  IntIntMap = require('../util/intint-map'),
17
15
  IntKeyLinkedMap = require('../util/intkey-linkedmap'),
18
16
  LongKeyLinkedMap = require('../util/longkey-linkedmap'),
@@ -73,7 +71,6 @@ function CounterPack() {
73
71
 
74
72
  this.db_num_active = null;
75
73
  this.db_num_idle = null;
76
- this.netstat = null;
77
74
 
78
75
  this.host_ip = 0;
79
76
  this.proc_fd = 0;