whatap 0.5.26 → 1.0.0-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 +78 -32
  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 +882 -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 -10
  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
@@ -0,0 +1,294 @@
1
+ var TraceContextManager = require('../trace/trace-context-manager'),
2
+ HashUtil = require('../util/hashutil'),
3
+ Logger = require('../logger'),
4
+ conf = require('../conf/configure'),
5
+ AsyncSender = require('../udp/async_sender'),
6
+ PacketTypeEnum = require('../udp/packet_type_enum');
7
+
8
+ const { AsyncResource } = require('async_hooks');
9
+
10
+ var IORedisObserver = function (agent) {
11
+ this.agent = agent;
12
+ this.aop = agent.aop;
13
+ this.packages = ['ioredis'];
14
+ };
15
+
16
+ IORedisObserver.prototype.inject = function (mod, modName) {
17
+ if (mod.__whatap_observe__) {
18
+ return;
19
+ }
20
+ mod.__whatap_observe__ = true;
21
+ Logger.initPrint("IORedisObserver");
22
+
23
+ if (conf.sql_enabled === false) {
24
+ return;
25
+ }
26
+
27
+ var self = this;
28
+ var dbc_hash = 0;
29
+ var dbc = 'redis://';
30
+
31
+ // Redis 명령어 매핑 (Redis observer와 동일)
32
+ var REDIS_COMMANDS = {
33
+ // 기본 명령어들
34
+ 'ping': 'PING', 'get': 'GET', 'set': 'SET', 'del': 'DEL', 'exists': 'EXISTS', 'keys': 'KEYS',
35
+ 'setex': 'SETEX', 'setEx': 'SETEX', 'expire': 'EXPIRE', 'expireAt': 'EXPIREAT', 'expireat': 'EXPIREAT',
36
+ 'ttl': 'TTL', 'incr': 'INCR', 'decr': 'DECR', 'incrBy': 'INCRBY', 'incrby': 'INCRBY',
37
+ 'decrBy': 'DECRBY', 'decrby': 'DECRBY', 'mGet': 'MGET', 'mget': 'MGET', 'mSet': 'MSET', 'mset': 'MSET',
38
+
39
+ // Hash 명령어들
40
+ 'hSet': 'HSET', 'hset': 'HSET', 'hGet': 'HGET', 'hget': 'HGET', 'hGetAll': 'HGETALL', 'hgetall': 'HGETALL',
41
+ 'hMGet': 'HMGET', 'hmget': 'HMGET', 'hMSet': 'HMSET', 'hmset': 'HMSET', 'hDel': 'HDEL', 'hdel': 'HDEL',
42
+
43
+ // List 명령어들
44
+ 'lPush': 'LPUSH', 'lpush': 'LPUSH', 'rPush': 'RPUSH', 'rpush': 'RPUSH', 'lPop': 'LPOP', 'lpop': 'LPOP',
45
+ 'rPop': 'RPOP', 'rpop': 'RPOP', 'lRange': 'LRANGE', 'lrange': 'LRANGE', 'lLen': 'LLEN', 'llen': 'LLEN',
46
+ 'lSet': 'LSET', 'lset': 'LSET', 'lRem': 'LREM', 'lrem': 'LREM',
47
+
48
+ // Set 명령어들
49
+ 'sAdd': 'SADD', 'sadd': 'SADD', 'sMembers': 'SMEMBERS', 'smembers': 'SMEMBERS', 'sRem': 'SREM', 'srem': 'SREM',
50
+ 'sCard': 'SCARD', 'scard': 'SCARD',
51
+
52
+ // Sorted Set 명령어들
53
+ 'zAdd': 'ZADD', 'zadd': 'ZADD', 'zRange': 'ZRANGE', 'zrange': 'ZRANGE', 'zRem': 'ZREM', 'zrem': 'ZREM'
54
+ };
55
+
56
+ // DBC 정보 설정
57
+ function setupDbcInfo(connectionOptions) {
58
+ if (dbc_hash === 0 && connectionOptions) {
59
+ dbc = 'redis://';
60
+ dbc += connectionOptions.host || 'localhost';
61
+ dbc += ':';
62
+ dbc += connectionOptions.port || 6379;
63
+ dbc += '/';
64
+ dbc += connectionOptions.db || 0;
65
+ dbc_hash = HashUtil.hashFromString(dbc);
66
+ }
67
+ }
68
+
69
+ function handleRedisError(ctx, err) {
70
+ if (!err) return;
71
+
72
+ try {
73
+ var errorClass = err.code || err.name || 'IORedisError';
74
+ var errorMessage = err.message || 'ioredis error';
75
+
76
+ if (!ctx.error) ctx.error = 1;
77
+ ctx.status = 500;
78
+ var errors = [errorClass, errorMessage];
79
+
80
+ AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
81
+ } catch (e) {
82
+ Logger.printError('WHATAP-234', 'Error handling IORedis error', e, false);
83
+ }
84
+ }
85
+
86
+ // IORedis 클래스의 prototype.sendCommand 메서드를 직접 후킹하는 방식
87
+ if (mod && mod.prototype && !mod.__whatap_wrapped__) {
88
+ // IORedis의 핵심 메서드인 sendCommand를 후킹
89
+ if (mod.prototype.sendCommand && !mod.prototype.sendCommand.__whatap_wrapped__) {
90
+ var originalSendCommand = mod.prototype.sendCommand;
91
+
92
+ mod.prototype.sendCommand = function(command, stream) {
93
+ var ctx = TraceContextManager.getCurrentContext();
94
+ if (!ctx || !command || !command.name) {
95
+ return originalSendCommand.apply(this, arguments);
96
+ }
97
+
98
+ var commandName = command.name.toLowerCase();
99
+ var normalizedCommand = REDIS_COMMANDS[commandName] || command.name.toUpperCase();
100
+
101
+ // 연결 정보 설정 (한 번만)
102
+ if (dbc_hash === 0 && this.options) {
103
+ setupDbcInfo(this.options);
104
+ }
105
+
106
+ var self = this;
107
+ var commandArgs = command.args || [];
108
+
109
+ const asyncResource = new AsyncResource('ioredis-command');
110
+
111
+ return asyncResource.runInAsyncScope(() => {
112
+ // DB 연결 패킷 전송
113
+ ctx.start_time = Date.now();
114
+ ctx.elapsed = 0;
115
+ ctx.active_sqlhash = true;
116
+ AsyncSender.send_packet(PacketTypeEnum.TX_DB_CONN, ctx, [dbc]);
117
+
118
+ // IORedis 명령 시작
119
+ var command_start_time = Date.now();
120
+ ctx.footprint(`IORedis ${normalizedCommand} Start`);
121
+
122
+ // IORedis에서는 command.args에서 쿼리 조합 (Python APM 방식 참고)
123
+ var queryParts = [normalizedCommand];
124
+ if (commandArgs && commandArgs.length > 0) {
125
+ // 각 인자를 문자열로 변환하고 20자로 제한
126
+ var argStrings = commandArgs.map(function(arg) {
127
+ var argStr = String(arg);
128
+ return argStr.length > 20 ? argStr.substring(0, 20) + '...' : argStr;
129
+ });
130
+ queryParts = queryParts.concat(argStrings);
131
+ }
132
+ var commandText = `Redis ` + queryParts.join(' ');
133
+
134
+ try {
135
+ var result = originalSendCommand.apply(self, arguments);
136
+
137
+ // IORedis는 항상 Promise를 반환
138
+ if (result && typeof result.then === 'function') {
139
+ return result.then(
140
+ function(res) {
141
+ var command_elapsed = Date.now() - command_start_time;
142
+ var resultCount = 0;
143
+
144
+ if (res !== undefined && res !== null) {
145
+ if (Array.isArray(res)) {
146
+ resultCount = res.length;
147
+ } else if (typeof res === 'string' || typeof res === 'number') {
148
+ resultCount = 1;
149
+ }
150
+ }
151
+
152
+ ctx.elapsed = command_elapsed;
153
+ ctx.active_sqlhash = false;
154
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, ctx, [dbc, commandText, String(resultCount)]);
155
+ ctx.footprint(`IORedis ${normalizedCommand} Done`);
156
+ return res;
157
+ },
158
+ function(err) {
159
+ handleRedisError(ctx, err);
160
+ var command_elapsed = Date.now() - command_start_time;
161
+ ctx.elapsed = command_elapsed;
162
+ ctx.active_sqlhash = false;
163
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, ctx, [dbc, commandText, '0']);
164
+ ctx.footprint(`IORedis ${normalizedCommand} Done`);
165
+ throw err;
166
+ }
167
+ );
168
+ } else {
169
+ // 예외적으로 Promise가 아닌 경우 처리
170
+ var command_elapsed = Date.now() - command_start_time;
171
+ var resultCount = 0;
172
+
173
+ if (result !== undefined && result !== null) {
174
+ if (Array.isArray(result)) {
175
+ resultCount = result.length;
176
+ } else if (typeof result === 'string' || typeof result === 'number') {
177
+ resultCount = 1;
178
+ }
179
+ }
180
+
181
+ ctx.elapsed = command_elapsed;
182
+ ctx.active_sqlhash = false;
183
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, ctx, [dbc, commandText, String(resultCount)]);
184
+ ctx.footprint(`IORedis ${normalizedCommand} Done`);
185
+ return result;
186
+ }
187
+ } catch (commandError) {
188
+ handleRedisError(ctx, commandError);
189
+ var command_elapsed = Date.now() - command_start_time;
190
+ ctx.elapsed = command_elapsed;
191
+ ctx.active_sqlhash = false;
192
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, ctx, [dbc, commandText, '0']);
193
+ ctx.footprint(`IORedis ${normalizedCommand} Done`);
194
+ throw commandError;
195
+ }
196
+ });
197
+ };
198
+
199
+ mod.prototype.sendCommand.__whatap_wrapped__ = true;
200
+ }
201
+
202
+ // IORedis 생성자도 후킹하여 연결 정보 수집
203
+ var originalConstructor = mod;
204
+ function WrappedIORedis() {
205
+ var args = Array.prototype.slice.call(arguments);
206
+ var ctx = TraceContextManager.getCurrentContext();
207
+
208
+ if (ctx) {
209
+ ctx.start_time = Date.now();
210
+ ctx.footprint('IORedis Client Creating');
211
+ ctx.db_opening = true;
212
+ }
213
+
214
+ // 원본 생성자 호출
215
+ var client = Object.create(originalConstructor.prototype);
216
+ originalConstructor.apply(client, args);
217
+
218
+ // 연결 옵션 파싱 및 DBC 정보 설정
219
+ var options = {};
220
+ if (args.length > 0) {
221
+ if (typeof args[0] === 'string') {
222
+ try {
223
+ var url = new URL(args[0]);
224
+ options = {
225
+ host: url.hostname,
226
+ port: parseInt(url.port) || 6379,
227
+ db: parseInt(url.pathname.slice(1)) || 0
228
+ };
229
+ } catch (e) {
230
+ options = { host: 'localhost', port: 6379, db: 0 };
231
+ }
232
+ } else if (typeof args[0] === 'number') {
233
+ options = {
234
+ host: args[1] || 'localhost',
235
+ port: args[0],
236
+ db: 0
237
+ };
238
+ } else if (typeof args[0] === 'object') {
239
+ options = args[0] || {};
240
+ if (!options.host) options.host = 'localhost';
241
+ if (!options.port) options.port = 6379;
242
+ if (options.db === undefined) options.db = 0;
243
+ }
244
+ } else {
245
+ options = { host: 'localhost', port: 6379, db: 0 };
246
+ }
247
+
248
+ setupDbcInfo(options);
249
+
250
+ if (ctx) {
251
+ ctx.elapsed = Date.now() - (ctx.start_time || Date.now());
252
+ ctx.footprint('IORedis Client Created');
253
+ if (ctx.db_opening !== undefined) {
254
+ ctx.db_opening = false;
255
+ }
256
+ }
257
+
258
+ return client;
259
+ }
260
+
261
+ // 프로토타입과 정적 속성 복사
262
+ WrappedIORedis.prototype = originalConstructor.prototype;
263
+ Object.setPrototypeOf(WrappedIORedis, originalConstructor);
264
+
265
+ Object.getOwnPropertyNames(originalConstructor).forEach(function(prop) {
266
+ if (prop !== 'prototype' && prop !== 'name' && prop !== 'length') {
267
+ try {
268
+ var descriptor = Object.getOwnPropertyDescriptor(originalConstructor, prop);
269
+ if (descriptor && descriptor.configurable !== false) {
270
+ Object.defineProperty(WrappedIORedis, prop, descriptor);
271
+ }
272
+ } catch (e) {
273
+ // 복사할 수 없는 속성은 건너뛰기
274
+ }
275
+ }
276
+ });
277
+
278
+ // 모듈 교체
279
+ Object.getOwnPropertyNames(WrappedIORedis).forEach(function(prop) {
280
+ if (prop !== 'prototype') {
281
+ try {
282
+ mod[prop] = WrappedIORedis[prop];
283
+ } catch (e) {
284
+ // 설정할 수 없는 속성은 건너뛰기
285
+ }
286
+ }
287
+ });
288
+
289
+ mod.__whatap_wrapped__ = true;
290
+ Logger.initPrint("IORedis sendCommand method successfully wrapped");
291
+ }
292
+ };
293
+
294
+ exports.IORedisObserver = IORedisObserver;