whatap 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/README.md +32 -78
  2. package/lib/conf/conf-sys-mon.js +101 -0
  3. package/lib/conf/config-default.js +10 -3
  4. package/lib/conf/configure.js +369 -349
  5. package/lib/conf/license.js +1 -1
  6. package/lib/control/cmd-config.js +24 -0
  7. package/lib/control/control-handler.js +367 -0
  8. package/lib/control/packagectr-helper.js +34 -3
  9. package/lib/core/agent.js +176 -882
  10. package/lib/core/interceptor.js +6 -6
  11. package/lib/core/request-agent.js +27 -0
  12. package/lib/core/shimmer.js +82 -36
  13. package/lib/counter/counter-manager.js +79 -8
  14. package/lib/counter/meter/meter-activex.js +67 -0
  15. package/lib/counter/meter/meter-httpc.js +57 -0
  16. package/lib/counter/meter/meter-resource.js +9 -0
  17. package/lib/counter/meter/meter-service.js +168 -0
  18. package/lib/counter/meter/meter-socket.io.js +51 -0
  19. package/lib/counter/meter/meter-sql.js +71 -0
  20. package/lib/counter/meter/meter-users.js +58 -0
  21. package/lib/counter/meter.js +183 -0
  22. package/lib/counter/task/activetransaction.js +68 -17
  23. package/lib/counter/task/agentinfo.js +107 -0
  24. package/lib/{system → counter/task}/gc-action.js +27 -74
  25. package/lib/counter/task/gcstat.js +34 -0
  26. package/lib/counter/task/heapmem.js +25 -0
  27. package/lib/counter/task/httpc.js +76 -0
  28. package/lib/counter/task/metering-info.js +125 -0
  29. package/lib/counter/task/proc-cpu.js +29 -0
  30. package/lib/counter/task/realtimeuser.js +31 -0
  31. package/lib/counter/task/res/systemECSTask.js +39 -0
  32. package/lib/counter/task/res/systemKubeTask.js +53 -0
  33. package/lib/counter/task/res/util/awsEcsClientThread.js +218 -0
  34. package/lib/counter/task/res/util/linuxProcStatUtil.js +14 -0
  35. package/lib/counter/task/res-sys-cpu.js +62 -0
  36. package/lib/counter/task/service.js +202 -0
  37. package/lib/counter/task/socketio.js +30 -0
  38. package/lib/counter/task/sql.js +105 -0
  39. package/lib/counter/task/systemperf.js +43 -0
  40. package/lib/data/datapack-sender.js +289 -0
  41. package/lib/data/dataprofile-agent.js +162 -0
  42. package/lib/data/datatext-agent.js +135 -0
  43. package/lib/data/event-level.js +15 -0
  44. package/lib/data/test.js +49 -0
  45. package/lib/data/zipprofile.js +197 -0
  46. package/lib/env/constants.js +21 -0
  47. package/lib/error/error-handler.js +437 -0
  48. package/lib/io/data-inputx.js +13 -3
  49. package/lib/io/data-outputx.js +268 -206
  50. package/lib/kube/kube-client.js +144 -0
  51. package/lib/lang/text-types.js +58 -0
  52. package/lib/logger.js +6 -6
  53. package/lib/logsink/line-log-util.js +87 -0
  54. package/lib/logsink/line-log.js +12 -0
  55. package/lib/logsink/log-sender.js +78 -0
  56. package/lib/logsink/log-tracer.js +40 -0
  57. package/lib/logsink/sender-util.js +56 -0
  58. package/lib/logsink/zip/zip-send.js +177 -0
  59. package/lib/net/netflag.js +55 -0
  60. package/lib/net/receiver.js +66 -0
  61. package/lib/net/security-master.js +139 -20
  62. package/lib/net/sender.js +141 -0
  63. package/lib/net/tcp-return.js +18 -0
  64. package/lib/net/tcp-session.js +286 -0
  65. package/lib/net/tcpreq-client-proxy.js +70 -0
  66. package/lib/net/tcprequest-mgr.js +58 -0
  67. package/lib/observers/apollo-server-observer.js +33 -27
  68. package/lib/observers/cluster-observer.js +22 -0
  69. package/lib/observers/express-observer.js +215 -0
  70. package/lib/observers/file-observer.js +184 -0
  71. package/lib/observers/global-observer.js +155 -80
  72. package/lib/observers/grpc-observer.js +336 -0
  73. package/lib/observers/http-observer.js +666 -236
  74. package/lib/observers/maria-observer.js +204 -362
  75. package/lib/observers/memcached-observer.js +56 -0
  76. package/lib/observers/mongo-observer.js +317 -0
  77. package/lib/observers/mongodb-observer.js +169 -226
  78. package/lib/observers/mongoose-observer.js +518 -323
  79. package/lib/observers/mssql-observer.js +177 -418
  80. package/lib/observers/mysql-observer.js +342 -449
  81. package/lib/observers/mysql2-observer.js +396 -358
  82. package/lib/observers/net-observer.js +77 -0
  83. package/lib/observers/oracle-observer.js +559 -384
  84. package/lib/observers/pgsql-observer.js +231 -489
  85. package/lib/observers/prisma-observer.js +303 -92
  86. package/lib/observers/process-observer.js +79 -35
  87. package/lib/observers/promise-observer.js +31 -0
  88. package/lib/observers/redis-observer.js +166 -331
  89. package/lib/observers/schedule-observer.js +67 -0
  90. package/lib/observers/socket.io-observer.js +226 -187
  91. package/lib/observers/stream-observer.js +19 -0
  92. package/lib/observers/thrift-observer.js +197 -0
  93. package/lib/observers/websocket-observer.js +175 -301
  94. package/lib/pack/activestack-pack.js +55 -0
  95. package/lib/pack/apenum.js +8 -0
  96. package/lib/pack/counter-pack.js +3 -0
  97. package/lib/pack/errorsnap-pack.js +69 -0
  98. package/lib/pack/event-pack.js +54 -0
  99. package/lib/pack/hitmap-pack.js +63 -0
  100. package/lib/pack/hitmap-pack1.js +152 -0
  101. package/lib/pack/log-sink-pack.js +14 -52
  102. package/lib/pack/netstat.js +15 -0
  103. package/lib/pack/otype.js +7 -0
  104. package/lib/pack/profile-pack.js +49 -0
  105. package/lib/pack/realtimeuser-pack.js +41 -0
  106. package/lib/pack/stat-general-pack.js +96 -0
  107. package/lib/pack/staterror-pack.js +120 -0
  108. package/lib/pack/stathttpc-pack.js +66 -0
  109. package/lib/pack/stathttpc-rec.js +78 -0
  110. package/lib/pack/statremote-pack.js +46 -0
  111. package/lib/pack/statservice-pack.js +63 -0
  112. package/lib/pack/statservice-pack1.js +88 -0
  113. package/lib/pack/statservice-rec.js +292 -0
  114. package/lib/pack/statservice-rec_dep.js +151 -0
  115. package/lib/pack/statsql-pack.js +69 -0
  116. package/lib/pack/statsql-rec.js +100 -0
  117. package/lib/pack/statuseragent-pack.js +44 -0
  118. package/lib/pack/tagcount-pack.js +4 -4
  119. package/lib/pack/tagctr.js +15 -0
  120. package/lib/pack/text-pack.js +50 -0
  121. package/lib/pack/time-count.js +25 -0
  122. package/lib/pack/websocket.js +15 -0
  123. package/lib/pack/zip-pack.js +70 -0
  124. package/lib/pii/pii-item.js +31 -0
  125. package/lib/pii/pii-mask.js +174 -0
  126. package/lib/plugin/plugin-loadermanager.js +57 -0
  127. package/lib/plugin/plugin.js +75 -0
  128. package/lib/service/tx-record.js +332 -0
  129. package/lib/stat/stat-error.js +116 -0
  130. package/lib/stat/stat-httpc.js +98 -0
  131. package/lib/stat/stat-remote-ip.js +46 -0
  132. package/lib/stat/stat-remote-ipurl.js +88 -0
  133. package/lib/stat/stat-sql.js +113 -0
  134. package/lib/stat/stat-tranx.js +58 -0
  135. package/lib/stat/stat-tx-caller.js +160 -0
  136. package/lib/stat/stat-tx-domain.js +111 -0
  137. package/lib/stat/stat-tx-referer.js +112 -0
  138. package/lib/stat/stat-useragent.js +48 -0
  139. package/lib/stat/timingsender.js +76 -0
  140. package/lib/step/activestack-step.js +38 -0
  141. package/lib/step/dbc-step.js +36 -0
  142. package/lib/step/http-stepx.js +67 -0
  143. package/lib/step/message-step.js +40 -0
  144. package/lib/step/method-stepx.js +45 -0
  145. package/lib/step/resultset-step.js +40 -0
  146. package/lib/step/securemsg-step.js +44 -0
  147. package/lib/step/socket-step.js +46 -0
  148. package/lib/step/sql-stepx.js +68 -0
  149. package/lib/step/sqlxtype.js +16 -0
  150. package/lib/step/step.js +66 -0
  151. package/lib/step/stepenum.js +54 -0
  152. package/lib/topology/link.js +63 -0
  153. package/lib/topology/nodeinfo.js +123 -0
  154. package/lib/topology/status-detector.js +111 -0
  155. package/lib/trace/trace-context-manager.js +113 -25
  156. package/lib/trace/trace-context.js +21 -7
  157. package/lib/trace/trace-httpc.js +17 -11
  158. package/lib/trace/trace-sql.js +29 -21
  159. package/lib/util/anylist.js +103 -0
  160. package/lib/util/cardinality/hyperloglog.js +106 -0
  161. package/lib/util/cardinality/murmurhash.js +31 -0
  162. package/lib/util/cardinality/registerset.js +75 -0
  163. package/lib/util/errordata.js +21 -0
  164. package/lib/util/escape-literal-sql.js +5 -5
  165. package/lib/util/hashutil.js +18 -18
  166. package/lib/util/iputil_x.js +527 -0
  167. package/lib/util/keygen.js +0 -3
  168. package/lib/util/kube-util.js +73 -0
  169. package/lib/util/linkedset.js +1 -2
  170. package/lib/util/nodeutil.js +2 -1
  171. package/lib/util/paramsecurity.js +80 -0
  172. package/lib/util/pre-process.js +13 -0
  173. package/lib/util/process-seq.js +166 -0
  174. package/lib/util/property-util.js +36 -0
  175. package/lib/util/request-queue.js +70 -0
  176. package/lib/util/requestdouble-queue.js +72 -0
  177. package/lib/util/resourceprofile.js +157 -0
  178. package/lib/util/stop-watch.js +30 -0
  179. package/lib/util/system-util.js +10 -0
  180. package/lib/util/userid-util.js +57 -0
  181. package/lib/value/map-value.js +3 -2
  182. package/package.json +9 -4
  183. package/whatap.conf +1 -4
  184. package/agent/darwin/arm64/whatap_nodejs +0 -0
  185. package/agent/linux/amd64/whatap_nodejs +0 -0
  186. package/agent/linux/arm64/whatap_nodejs +0 -0
  187. package/build.txt +0 -4
  188. package/lib/observers/ioredis-observer.js +0 -294
  189. package/lib/udp/async_sender.js +0 -119
  190. package/lib/udp/index.js +0 -17
  191. package/lib/udp/packet_enum.js +0 -52
  192. package/lib/udp/packet_queue.js +0 -69
  193. package/lib/udp/packet_type_enum.js +0 -33
  194. package/lib/udp/param_def.js +0 -72
  195. package/lib/udp/udp_session.js +0 -336
  196. package/lib/util/sql-util.js +0 -178
  197. package/lib/util/trace-helper.js +0 -91
  198. package/lib/util/transfer.js +0 -58
@@ -5,21 +5,32 @@
5
5
  */
6
6
 
7
7
  var TraceContextManager = require('../trace/trace-context-manager'),
8
+ SocketStep = require('../step/socket-step'),
8
9
  conf = require('../conf/configure'),
9
10
  IPUtil = require('../util/iputil'),
10
11
  Logger = require('../logger');
11
12
  const {Detector: URLPatternDetector} = require("../trace/serviceurl-pattern-detector");
13
+ const DataTextAgent = require("../data/datatext-agent");
14
+ const ResourceProfile = require("../util/resourceprofile");
15
+ const ProfilePack = require('../pack/profile-pack');
16
+ const TxRecord = require('../service/tx-record');
17
+ const DateUtil = require('../util/dateutil');
18
+ const SecurityMaster = require('../net/security-master');
19
+ const DataProfileAgent = require('../data/dataprofile-agent');
20
+ const MeterUsers = require("../counter/meter/meter-users");
21
+ const MeterService = require('../counter/meter/meter-service').MeterService;
12
22
  const shimmer = require('../core/shimmer');
13
- const AsyncSender = require('../udp/async_sender');
14
- const PacketTypeEnum = require('../udp/packet_type_enum');
15
- const os = require('os');
23
+ const MessageStep = require('../step/message-step');
24
+ const HashUtil = require('../util/hashutil');
16
25
 
17
26
  // 설정을 객체로 통합하여 관리 (참조 성능 향상)
18
27
  var config = {
19
28
  trace_background_socket_enabled: conf.getProperty('trace_background_socket_enabled', true),
20
29
  trace_sampling_enabled: conf.getProperty('trace_sampling_enabled', true),
21
30
  trace_sampling_tps: conf.getProperty('trace_sampling_tps', 1000),
22
- resource_sampling_rate: 0.1 // 리소스 프로파일링을 10%만 수행
31
+ resource_sampling_rate: 0.1, // 리소스 프로파일링을 10%만 수행
32
+ profile_batch_size: 20, // 프로파일 배치 처리 크기
33
+ flush_interval: 500 // 프로파일 플러시 간격 (ms)
23
34
  };
24
35
 
25
36
  // 설정 변경 감지를 단일 리스너로 통합
@@ -57,10 +68,48 @@ var SocketIOObserver = function(agent){
57
68
  }
58
69
  };
59
70
 
71
+ // 프로파일 데이터 버퍼링 (배치 처리)
72
+ this.profileBuffer = [];
73
+
74
+ // 주기적 플러시 설정
75
+ setInterval(() => this.flushProfileBuffer(), config.flush_interval);
76
+
60
77
  // IP 주소 캐싱 (성능 최적화)
61
78
  this.ipCache = new Map();
62
79
  };
63
80
 
81
+ SocketIOObserver.prototype.flushProfileBuffer = function() {
82
+ if (this.profileBuffer.length === 0) return;
83
+
84
+ const now = Date.now();
85
+ // 100ms 이상 지난 항목만 처리
86
+ const bufferToFlush = this.profileBuffer.filter(item => (now - item.timestamp) >= 100);
87
+ if (bufferToFlush.length === 0) return;
88
+
89
+ // 최대 batch_size까지만 처리
90
+ let batchToProcess;
91
+ if(bufferToFlush.length > config.profile_batch_size)
92
+ batchToProcess = bufferToFlush.slice(0, config.profile_batch_size);
93
+ else
94
+ batchToProcess = bufferToFlush;
95
+
96
+ // 버퍼에서 처리할 항목 제거
97
+ this.profileBuffer = this.profileBuffer.filter(item =>
98
+ !batchToProcess.some(batch => batch.ctx._id === item.ctx._id)
99
+ );
100
+
101
+ // 배치 처리
102
+ batchToProcess.forEach(item => {
103
+ try {
104
+ DataProfileAgent.sendProfile(item.ctx, item.profile, false);
105
+ // 컨텍스트 정리 (메모리 누수 방지)
106
+ item.ctx = null;
107
+ } catch (e) {
108
+ Logger.printError('WHATAP-615', 'Socket.io buffer flush error', e, false);
109
+ }
110
+ });
111
+ };
112
+
64
113
  // IP 주소 처리 최적화 함수
65
114
  SocketIOObserver.prototype.getProcessedIp = function(address) {
66
115
  if (!address) return null;
@@ -91,56 +140,6 @@ SocketIOObserver.prototype.getProcessedIp = function(address) {
91
140
  return result;
92
141
  };
93
142
 
94
- // 에러 처리 함수
95
- function handleSocketError(ctx, err) {
96
- if (!err) return null;
97
-
98
- try {
99
- var errorClass = err.code || err.name || err.constructor?.name || 'SocketIOError';
100
- var errorMessage = err.message || 'socket.io error';
101
- var errorStack = '';
102
-
103
- if (conf.trace_sql_error_stack && conf.trace_sql_error_depth && err.stack) {
104
- var traceDepth = conf.trace_sql_error_depth;
105
- var stackLines = err.stack.split("\n");
106
- if (stackLines.length > traceDepth) {
107
- stackLines = stackLines.slice(0, traceDepth + 1);
108
- }
109
- errorStack = stackLines.join("\n");
110
- }
111
-
112
- var shouldAddError = false;
113
- if (conf._is_trace_ignore_err_cls_contains === true && errorClass &&
114
- errorClass.indexOf(conf.trace_ignore_err_cls_contains) < 0) {
115
- shouldAddError = true;
116
- } else if (conf._is_trace_ignore_err_msg_contains === true && errorMessage &&
117
- errorMessage.indexOf(conf.trace_ignore_err_msg_contains) < 0) {
118
- shouldAddError = true;
119
- } else if (conf._is_trace_ignore_err_cls_contains === false &&
120
- conf._is_trace_ignore_err_msg_contains === false) {
121
- shouldAddError = true;
122
- }
123
-
124
- if (shouldAddError) {
125
- if (!ctx.error) ctx.error = 1;
126
- ctx.status = 500;
127
- var errors = [errorClass];
128
- if (errorMessage) {
129
- errors.push(errorMessage);
130
- }
131
- if (errorStack || err.stack) {
132
- errors.push(errorStack || err.stack);
133
- }
134
-
135
- AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
136
- return errorClass; // 에러 정보 반환
137
- }
138
- } catch (e) {
139
- Logger.printError('WHATAP-227', 'Error handling Socket.IO error', e, false);
140
- }
141
- return null;
142
- }
143
-
144
143
  SocketIOObserver.prototype.inject = function (mod, moduleName) {
145
144
  if (mod.__whatap_observe__) {
146
145
  return;
@@ -150,6 +149,33 @@ SocketIOObserver.prototype.inject = function (mod, moduleName) {
150
149
 
151
150
  var self = this;
152
151
 
152
+ // 공통 emit 래핑 함수
153
+ const wrapEmitFunction = function(target, emitType, getContextInfo) {
154
+ if (!target || typeof target.emit !== 'function' || target.emit.__wrapped__) {
155
+ return false;
156
+ }
157
+
158
+ const originalEmit = target.emit;
159
+ target.emit = function(event, ...args) {
160
+ if (!config.trace_background_socket_enabled) {
161
+ return originalEmit.apply(this, [event, ...args]);
162
+ }
163
+
164
+ const contextInfo = getContextInfo ? getContextInfo.call(this) : {};
165
+ self.__handleSocketEmitEvent(this, event, args, emitType, contextInfo);
166
+ return originalEmit.apply(this, [event, ...args]);
167
+ };
168
+
169
+ // 원본 함수 속성 보존
170
+ Object.defineProperties(target.emit, {
171
+ length: { value: originalEmit.length },
172
+ name: { value: originalEmit.name },
173
+ __wrapped__: { value: true }
174
+ });
175
+
176
+ return true;
177
+ };
178
+
153
179
  const wrapSocketIOEvents = function(target) {
154
180
  if (!target || typeof target.on !== 'function') {
155
181
  return false;
@@ -362,164 +388,141 @@ SocketIOObserver.prototype.inject = function (mod, moduleName) {
362
388
  if (mod.Server && mod.Server.prototype) {
363
389
  wrapSocketIOEvents(mod.Server.prototype);
364
390
  }
391
+
392
+ // 3. 직접 모듈 래핑 (일부 버전에서 필요할 수 있음)
393
+ // if (typeof mod.on === 'function') {
394
+ // wrapSocketIOEvents(mod);
395
+ // }
365
396
  };
366
397
 
398
+ // 소켓 이벤트 처리 최적화 (CPU 및 메모리 사용 감소) - broadcast 지원 추가
367
399
  SocketIOObserver.prototype.__handleSocketEmitEvent = function(socket, event, args, emitType, contextInfo) {
400
+ // 빠른 샘플링 체크 (조기 반환으로 성능 향상)
368
401
  if (config.trace_sampling_enabled && !this.socketCounter.checkSampling()) {
402
+ MeterService.add(0, 1, 0, SecurityMaster.PCODE, SecurityMaster.OKIND, SecurityMaster.OID);
369
403
  return;
370
404
  }
371
405
 
372
- TraceContextManager._asyncLocalStorage.run(this.__initCtx(socket, args, emitType, contextInfo), () => {
373
- var ctx = TraceContextManager._asyncLocalStorage.getStore();
374
- if (!ctx) {
375
- return;
376
- }
406
+ // 리소스 프로파일링 샘플링 (전체 이벤트의 일부만 측정)
407
+ const shouldProfileResource = Math.random() < config.resource_sampling_rate;
377
408
 
409
+ // 컨텍스트 생성 및 실행
410
+ TraceContextManager._asyncLocalStorage.run(this.__initCtx(socket, args, shouldProfileResource, emitType, contextInfo), () => {
378
411
  try {
379
- ctx.service_name = "/socket.io";
380
- let hostname = '';
381
-
382
- let datas = [
383
- hostname,
384
- ctx.service_name,
385
- ctx.remoteIp || 0,
386
- ctx.userAgentString || '',
387
- ctx.referer || '',
388
- String(ctx.userid || 0),
389
- String(false), // isStaticContents
390
- ''
391
- ];
392
-
393
- AsyncSender.send_packet(PacketTypeEnum.TX_START, ctx, datas);
394
-
395
- this.__sendSocketMessage(ctx, emitType, contextInfo);
396
- this.__sendSocketEvent(ctx, socket, event, args, emitType, contextInfo);
412
+ var ctx = TraceContextManager._asyncLocalStorage.getStore();
413
+ if (!ctx) return;
414
+
415
+ // IP 주소 처리 최적화
416
+ var host = null;
417
+ var remoteAddress = contextInfo.remoteAddress || (socket && socket.conn && socket.conn.remoteAddress);
418
+
419
+ if (remoteAddress) {
420
+ const ipInfo = this.getProcessedIp(remoteAddress);
421
+ if (ipInfo) {
422
+ host = ipInfo.host;
423
+ // 이미 처리된 IP 바이트 사용
424
+ var step = new SocketStep();
425
+ step.start_time = ctx.getElapsedTime();
426
+ step.ipaddr = ipInfo.ipBytes;
427
+ step.elapsed = 0; // 즉시 종료 (측정 최소화)
428
+
429
+ // 브로드캐스트 타입 정보 추가
430
+ if (emitType !== 'socket') {
431
+ step.broadcast_type = emitType;
432
+ step.broadcast_info = contextInfo;
433
+ }
434
+
435
+ ctx.profile.push(step);
436
+ }
437
+ } else {
438
+ const ipBytes = Buffer.from(IPUtil.stringToBytes("0.0.0.0"));
439
+ var step = new SocketStep();
440
+ step.start_time = ctx.getElapsedTime();
441
+ step.ipaddr = ipBytes;
442
+ step.elapsed = 0;
443
+
444
+ ctx.profile.push(step);
445
+ }
446
+
447
+ // 트랜잭션 종료 및 데이터 전송
397
448
  this.__endTransaction(null, ctx);
449
+
398
450
  } catch (e) {
399
- Logger.printError('WHATAP-228', 'socket.io emit transaction error', e, false);
451
+ Logger.printError('WHATAP-616', 'socket.io emit transaction error', e, false);
400
452
 
401
- var errorInfo = handleSocketError(ctx, e);
402
- this.__endTransaction(errorInfo, ctx);
453
+ // 에러 컨텍스트 정리 (메모리 누수 방지)
454
+ var ctx = TraceContextManager._asyncLocalStorage.getStore();
455
+ if (ctx) {
456
+ TraceContextManager.end(ctx._id);
457
+ }
403
458
  }
404
459
  });
405
460
  };
406
461
 
407
- SocketIOObserver.prototype.__sendSocketMessage = function(ctx, emitType, contextInfo) {
462
+ // 트랜잭션 종료 최적화 (배치 처리 및 메모리 사용 감소)
463
+ SocketIOObserver.prototype.__endTransaction = function(error, ctx) {
408
464
  try {
409
- var title, message;
410
-
411
- switch (emitType) {
412
- case 'broadcast':
413
- title = "Broadcast";
414
- message = "Broadcast to all clients except sender";
415
- break;
416
- case 'room_broadcast':
417
- title = "Room Broadcast";
418
- message = `Broadcast to room: ${contextInfo.room || 'unknown'}`;
419
- break;
420
- case 'global_broadcast':
421
- title = "Global Broadcast";
422
- message = `Total clients: ${contextInfo.connectedSockets || 0}`;
423
- break;
424
- case 'server_room_broadcast':
425
- title = "Server Room Broadcast";
426
- message = `Room: ${contextInfo.room || 'unknown'}, Total clients: ${contextInfo.connectedSockets || 0}`;
427
- break;
428
- default:
429
- title = "Socket";
430
- message = "Direct socket message";
465
+ // 기본 성능 데이터 수집
466
+ var profile = new ProfilePack();
467
+ var wtx = new TxRecord();
468
+ wtx.endTime = DateUtil.currentTime();
469
+ profile.time = wtx.endTime;
470
+ wtx.elapsed = ctx.getElapsedTime();
471
+
472
+ // 서비스 이름에 브로드캐스트 타입 정보 추가
473
+ let serviceName = ctx.service_name;
474
+ DataTextAgent.SERVICE.add(ctx.service_hash, serviceName);
475
+
476
+ wtx.seq = ctx.txid;
477
+ wtx.service = ctx.service_hash;
478
+
479
+ // 리소스 프로파일링은 샘플링된 경우에만 수행
480
+ if (ctx.shouldProfileResource) {
481
+ wtx.cpuTime = ResourceProfile.getCPUTime() - ctx.start_cpu;
482
+ wtx.malloc = ResourceProfile.getUsedHeapSize() - ctx.start_malloc;
483
+ if(wtx.malloc < 0) { wtx.malloc = 0; }
484
+ } else {
485
+ wtx.cpuTime = 0;
486
+ wtx.malloc = 0;
431
487
  }
432
488
 
433
- AsyncSender.send_packet(PacketTypeEnum.TX_MSG, ctx, [title, '', message]);
434
- } catch (e) {
435
- Logger.printError('WHATAP-229', 'Socket.io send message error', e, false);
436
- throw e;
437
- }
438
- };
489
+ wtx.status = 2;
490
+ wtx.ipaddr = ctx.remoteIp;
439
491
 
440
- SocketIOObserver.prototype.__sendSocketEvent = function(ctx, socket, event, args, emitType, contextInfo) {
441
- try {
442
- var start_time = Date.now();
443
- ctx.start_time = start_time;
444
- ctx.elapsed = 0;
445
- ctx.error = 0;
446
-
447
- // IP 주소 및 포트 처리
448
- var ipString = "0.0.0.0";
449
- var port = 0;
450
- var remoteAddress = contextInfo.remoteAddress || (socket && socket.conn && socket.conn.remoteAddress);
451
-
452
- if (remoteAddress) {
453
- const ipInfo = this.getProcessedIp(remoteAddress);
454
- if (ipInfo) {
455
- ipString = ipInfo.host;
456
- ctx.remoteIp = ipInfo.ipInt;
457
-
458
- if (socket && socket.conn && socket.conn.remotePort) {
459
- port = Number(socket.conn.remotePort);
460
- } else if (remoteAddress.includes(':')) {
461
- // remoteAddress에서 포트 추출 시도
462
- const parts = remoteAddress.split(':');
463
- if (parts.length > 1) {
464
- const portStr = parts[parts.length - 1];
465
- const parsedPort = parseInt(portStr, 10);
466
- if (!isNaN(parsedPort)) {
467
- port = parsedPort;
468
- }
469
- }
470
- }
471
- }
472
- }
492
+ // 기본 측정 데이터 기록
493
+ MeterService.add(0, wtx.elapsed, 0, SecurityMaster.PCODE, SecurityMaster.OKIND, SecurityMaster.OID);
473
494
 
474
- var elapsed = Date.now() - start_time;
475
- ctx.elapsed = elapsed;
495
+ profile.oid = SecurityMaster.OID;
496
+ profile.service = wtx;
476
497
 
477
- var datas = [ipString, port, elapsed, 0];
478
- AsyncSender.send_packet(PacketTypeEnum.TX_WEB_SOCKET, ctx, datas);
479
- } catch (e) {
480
- Logger.printError('WHATAP-230', 'Socket.io send event error', e, false);
481
- throw e;
482
- }
483
- };
498
+ // 컨텍스트 ID 보존 컨텍스트 종료 (메모리 누수 방지)
499
+ const ctxId = ctx._id;
500
+ TraceContextManager.end(ctxId);
484
501
 
485
- SocketIOObserver.prototype.__endTransaction = function(error, ctx) {
486
- try {
487
- if (error) {
488
- TraceContextManager.end(ctx != null ? ctx.id : null);
489
- ctx = null;
490
- return;
491
- }
502
+ // 프로파일 데이터 버퍼에 추가 (비동기 처리 최적화)
503
+ this.profileBuffer.push({
504
+ ctx: ctx,
505
+ profile: profile,
506
+ timestamp: Date.now()
507
+ });
492
508
 
493
- if (ctx == null || TraceContextManager.isExist(ctx.id) === false)
494
- return;
495
-
496
- ctx.start_time = Date.now();
497
- let datas = [
498
- os.hostname(), // hostname
499
- ctx.service_name,
500
- 0, // mtid
501
- 0, // mdepth
502
- 0, // mcaller_txid
503
- 0, // mcaller_pcode
504
- '', // mcaller_spec
505
- String(0), // mcaller_url_hash
506
- 200
507
- ];
508
- ctx.elapsed = Date.now() - ctx.start_time;
509
- AsyncSender.send_packet(PacketTypeEnum.TX_END, ctx, datas);
510
-
511
- TraceContextManager.end(ctx.id);
509
+ if (this.profileBuffer.length > 1000) {
510
+ // 가장 오래된 항목부터 제거
511
+ this.profileBuffer = this.profileBuffer.slice(-500);
512
+ }
512
513
 
513
514
  } catch (e) {
514
- Logger.printError('WHATAP-231', 'Socket.io end transaction error', e, false);
515
- TraceContextManager.end(ctx.id);
515
+ Logger.printError('WHATAP-615', 'Socket.io end transaction error', e, false);
516
+ // 에러 시에도 컨텍스트 정리
517
+ TraceContextManager.end(ctx._id);
516
518
  ctx = null;
517
519
  }
518
520
  };
519
521
 
520
- SocketIOObserver.prototype.__initCtx = function(socket, args, emitType, contextInfo) {
522
+ // 컨텍스트 초기화 최적화 - broadcast 정보 추가
523
+ SocketIOObserver.prototype.__initCtx = function(socket, args, shouldProfileResource, emitType, contextInfo) {
521
524
  const ctx = TraceContextManager.start();
522
- if (!ctx) return null;
525
+ if (!ctx) {return;}
523
526
 
524
527
  var remote_addr;
525
528
  var remoteAddress = contextInfo.remoteAddress || (socket && socket.conn && socket.conn.remoteAddress);
@@ -531,18 +534,54 @@ SocketIOObserver.prototype.__initCtx = function(socket, args, emitType, contextI
531
534
  }
532
535
 
533
536
  ctx.service_name = "/socket.io";
537
+ ctx.service_hash = HashUtil.hashFromString(ctx.service_name);
538
+
539
+ // MessageStep 생성 - 브로드캐스트 타입별 정보
540
+ var step = new MessageStep();
541
+ var title, message;
542
+
543
+ switch (emitType) {
544
+ case 'broadcast':
545
+ title = "Broadcast";
546
+ message = "Broadcast to all clients except sender";
547
+ break;
548
+ case 'room_broadcast':
549
+ title = "Room Broadcast";
550
+ message = `Broadcast to room: ${contextInfo.room || 'unknown'}`;
551
+ break;
552
+ case 'global_broadcast':
553
+ title = "Global Broadcast";
554
+ message = `Total clients: ${contextInfo.connectedSockets || 0}`;
555
+ break;
556
+ case 'server_room_broadcast':
557
+ title = "Server Room Broadcast";
558
+ message = `Room: ${contextInfo.room || 'unknown'}, Total clients: ${contextInfo.connectedSockets || 0}`;
559
+ break;
560
+ default:
561
+ title = "Socket";
562
+ message = "Direct socket message";
563
+ }
564
+
565
+ step.hash = HashUtil.hashFromString(title);
566
+ step.start_time = ctx.getElapsedTime();
567
+ step.desc = message;
568
+ DataTextAgent.MESSAGE.add(step.hash, title);
569
+ ctx.profile.push(step);
570
+
571
+ // 리소스 측정은 샘플링된 경우에만 수행
572
+ ctx.shouldProfileResource = shouldProfileResource;
573
+ if (shouldProfileResource) {
574
+ ctx.start_malloc = ResourceProfile.getUsedHeapSize();
575
+ ctx.start_cpu = ResourceProfile.getCPUTime();
576
+ }
534
577
 
535
578
  if (remote_addr) {
536
579
  remote_addr = IPUtil.checkIp4(remote_addr);
537
580
  ctx.remoteIp = IPUtil.stringToInt(remote_addr);
538
- ctx.userid = ctx.remoteIp;
581
+ ctx.userid = Long.fromNumber(ctx.remoteIp);
582
+ MeterUsers.add(ctx.userid);
539
583
  }
540
584
 
541
- // 기본값 설정
542
- ctx.userAgentString = '';
543
- ctx.referer = '';
544
- ctx.status = 200;
545
-
546
585
  return ctx;
547
586
  };
548
587
 
@@ -0,0 +1,19 @@
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
+
8
+ var StreamObserver = function(agent) {
9
+ this.agent = agent;
10
+ this.aop = agent.aop;
11
+ this.packages = ['stream'];
12
+ };
13
+ StreamObserver.prototype.inject = function (mod, moduleName) {
14
+ if(mod.__whatap_observe__) { return; }
15
+ mod.__whatap_observe__ = true;
16
+
17
+ };
18
+
19
+ exports.StreamObserver = StreamObserver;