whatap 0.5.26 → 1.0.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 (196) hide show
  1. package/agent/darwin/arm64/whatap_nodejs +0 -0
  2. package/agent/linux/amd64/whatap_nodejs +0 -0
  3. package/agent/linux/arm64/whatap_nodejs +0 -0
  4. package/build.txt +4 -0
  5. package/lib/conf/config-default.js +3 -10
  6. package/lib/conf/configure.js +349 -369
  7. package/lib/conf/license.js +1 -1
  8. package/lib/control/packagectr-helper.js +3 -34
  9. package/lib/core/agent.js +882 -176
  10. package/lib/core/interceptor.js +6 -6
  11. package/lib/core/shimmer.js +36 -82
  12. package/lib/counter/counter-manager.js +8 -79
  13. package/lib/counter/task/activetransaction.js +17 -68
  14. package/lib/io/data-inputx.js +3 -13
  15. package/lib/io/data-outputx.js +206 -268
  16. package/lib/logger.js +6 -6
  17. package/lib/net/security-master.js +20 -139
  18. package/lib/observers/apollo-server-observer.js +27 -33
  19. package/lib/observers/global-observer.js +80 -155
  20. package/lib/observers/http-observer.js +236 -666
  21. package/lib/observers/ioredis-observer.js +294 -0
  22. package/lib/observers/maria-observer.js +362 -204
  23. package/lib/observers/mongodb-observer.js +226 -169
  24. package/lib/observers/mongoose-observer.js +323 -518
  25. package/lib/observers/mssql-observer.js +418 -177
  26. package/lib/observers/mysql-observer.js +449 -342
  27. package/lib/observers/mysql2-observer.js +358 -396
  28. package/lib/observers/oracle-observer.js +384 -559
  29. package/lib/observers/pgsql-observer.js +489 -231
  30. package/lib/observers/prisma-observer.js +92 -303
  31. package/lib/observers/process-observer.js +35 -79
  32. package/lib/observers/redis-observer.js +331 -166
  33. package/lib/observers/socket.io-observer.js +187 -226
  34. package/lib/observers/websocket-observer.js +301 -175
  35. package/lib/pack/counter-pack.js +0 -3
  36. package/lib/pack/log-sink-pack.js +52 -14
  37. package/lib/pack/tagcount-pack.js +4 -4
  38. package/lib/{counter/task → system}/gc-action.js +74 -27
  39. package/lib/trace/trace-context-manager.js +25 -113
  40. package/lib/trace/trace-context.js +7 -21
  41. package/lib/trace/trace-httpc.js +11 -17
  42. package/lib/trace/trace-sql.js +21 -29
  43. package/lib/udp/async_sender.js +119 -0
  44. package/lib/udp/index.js +17 -0
  45. package/lib/udp/packet_enum.js +52 -0
  46. package/lib/udp/packet_queue.js +69 -0
  47. package/lib/udp/packet_type_enum.js +33 -0
  48. package/lib/udp/param_def.js +72 -0
  49. package/lib/udp/udp_session.js +336 -0
  50. package/lib/util/escape-literal-sql.js +5 -5
  51. package/lib/util/hashutil.js +18 -18
  52. package/lib/util/keygen.js +3 -0
  53. package/lib/util/linkedset.js +2 -1
  54. package/lib/util/nodeutil.js +1 -2
  55. package/lib/util/sql-util.js +178 -0
  56. package/lib/util/trace-helper.js +91 -0
  57. package/lib/util/transfer.js +58 -0
  58. package/lib/value/map-value.js +2 -3
  59. package/package.json +5 -10
  60. package/lib/conf/conf-sys-mon.js +0 -101
  61. package/lib/control/cmd-config.js +0 -24
  62. package/lib/control/control-handler.js +0 -367
  63. package/lib/core/request-agent.js +0 -27
  64. package/lib/counter/meter/meter-activex.js +0 -67
  65. package/lib/counter/meter/meter-httpc.js +0 -57
  66. package/lib/counter/meter/meter-resource.js +0 -9
  67. package/lib/counter/meter/meter-service.js +0 -168
  68. package/lib/counter/meter/meter-socket.io.js +0 -51
  69. package/lib/counter/meter/meter-sql.js +0 -71
  70. package/lib/counter/meter/meter-users.js +0 -58
  71. package/lib/counter/meter.js +0 -183
  72. package/lib/counter/task/agentinfo.js +0 -107
  73. package/lib/counter/task/gcstat.js +0 -34
  74. package/lib/counter/task/heapmem.js +0 -25
  75. package/lib/counter/task/httpc.js +0 -76
  76. package/lib/counter/task/metering-info.js +0 -125
  77. package/lib/counter/task/proc-cpu.js +0 -29
  78. package/lib/counter/task/realtimeuser.js +0 -31
  79. package/lib/counter/task/res/systemECSTask.js +0 -39
  80. package/lib/counter/task/res/systemKubeTask.js +0 -53
  81. package/lib/counter/task/res/util/awsEcsClientThread.js +0 -218
  82. package/lib/counter/task/res/util/linuxProcStatUtil.js +0 -14
  83. package/lib/counter/task/res-sys-cpu.js +0 -62
  84. package/lib/counter/task/service.js +0 -202
  85. package/lib/counter/task/socketio.js +0 -30
  86. package/lib/counter/task/sql.js +0 -105
  87. package/lib/counter/task/systemperf.js +0 -43
  88. package/lib/data/datapack-sender.js +0 -289
  89. package/lib/data/dataprofile-agent.js +0 -162
  90. package/lib/data/datatext-agent.js +0 -135
  91. package/lib/data/event-level.js +0 -15
  92. package/lib/data/test.js +0 -49
  93. package/lib/data/zipprofile.js +0 -197
  94. package/lib/env/constants.js +0 -21
  95. package/lib/error/error-handler.js +0 -437
  96. package/lib/kube/kube-client.js +0 -144
  97. package/lib/lang/text-types.js +0 -58
  98. package/lib/logsink/line-log-util.js +0 -87
  99. package/lib/logsink/line-log.js +0 -12
  100. package/lib/logsink/log-sender.js +0 -78
  101. package/lib/logsink/log-tracer.js +0 -40
  102. package/lib/logsink/sender-util.js +0 -56
  103. package/lib/logsink/zip/zip-send.js +0 -177
  104. package/lib/net/netflag.js +0 -55
  105. package/lib/net/receiver.js +0 -66
  106. package/lib/net/sender.js +0 -141
  107. package/lib/net/tcp-return.js +0 -18
  108. package/lib/net/tcp-session.js +0 -286
  109. package/lib/net/tcpreq-client-proxy.js +0 -70
  110. package/lib/net/tcprequest-mgr.js +0 -58
  111. package/lib/observers/cluster-observer.js +0 -22
  112. package/lib/observers/express-observer.js +0 -215
  113. package/lib/observers/file-observer.js +0 -184
  114. package/lib/observers/grpc-observer.js +0 -336
  115. package/lib/observers/memcached-observer.js +0 -56
  116. package/lib/observers/mongo-observer.js +0 -317
  117. package/lib/observers/net-observer.js +0 -77
  118. package/lib/observers/promise-observer.js +0 -31
  119. package/lib/observers/schedule-observer.js +0 -67
  120. package/lib/observers/stream-observer.js +0 -19
  121. package/lib/observers/thrift-observer.js +0 -197
  122. package/lib/pack/activestack-pack.js +0 -55
  123. package/lib/pack/apenum.js +0 -8
  124. package/lib/pack/errorsnap-pack.js +0 -69
  125. package/lib/pack/event-pack.js +0 -54
  126. package/lib/pack/hitmap-pack.js +0 -63
  127. package/lib/pack/hitmap-pack1.js +0 -152
  128. package/lib/pack/netstat.js +0 -15
  129. package/lib/pack/otype.js +0 -7
  130. package/lib/pack/profile-pack.js +0 -49
  131. package/lib/pack/realtimeuser-pack.js +0 -41
  132. package/lib/pack/stat-general-pack.js +0 -96
  133. package/lib/pack/staterror-pack.js +0 -120
  134. package/lib/pack/stathttpc-pack.js +0 -66
  135. package/lib/pack/stathttpc-rec.js +0 -78
  136. package/lib/pack/statremote-pack.js +0 -46
  137. package/lib/pack/statservice-pack.js +0 -63
  138. package/lib/pack/statservice-pack1.js +0 -88
  139. package/lib/pack/statservice-rec.js +0 -292
  140. package/lib/pack/statservice-rec_dep.js +0 -151
  141. package/lib/pack/statsql-pack.js +0 -69
  142. package/lib/pack/statsql-rec.js +0 -100
  143. package/lib/pack/statuseragent-pack.js +0 -44
  144. package/lib/pack/tagctr.js +0 -15
  145. package/lib/pack/text-pack.js +0 -50
  146. package/lib/pack/time-count.js +0 -25
  147. package/lib/pack/websocket.js +0 -15
  148. package/lib/pack/zip-pack.js +0 -70
  149. package/lib/pii/pii-item.js +0 -31
  150. package/lib/pii/pii-mask.js +0 -174
  151. package/lib/plugin/plugin-loadermanager.js +0 -57
  152. package/lib/plugin/plugin.js +0 -75
  153. package/lib/service/tx-record.js +0 -332
  154. package/lib/stat/stat-error.js +0 -116
  155. package/lib/stat/stat-httpc.js +0 -98
  156. package/lib/stat/stat-remote-ip.js +0 -46
  157. package/lib/stat/stat-remote-ipurl.js +0 -88
  158. package/lib/stat/stat-sql.js +0 -113
  159. package/lib/stat/stat-tranx.js +0 -58
  160. package/lib/stat/stat-tx-caller.js +0 -160
  161. package/lib/stat/stat-tx-domain.js +0 -111
  162. package/lib/stat/stat-tx-referer.js +0 -112
  163. package/lib/stat/stat-useragent.js +0 -48
  164. package/lib/stat/timingsender.js +0 -76
  165. package/lib/step/activestack-step.js +0 -38
  166. package/lib/step/dbc-step.js +0 -36
  167. package/lib/step/http-stepx.js +0 -67
  168. package/lib/step/message-step.js +0 -40
  169. package/lib/step/method-stepx.js +0 -45
  170. package/lib/step/resultset-step.js +0 -40
  171. package/lib/step/securemsg-step.js +0 -44
  172. package/lib/step/socket-step.js +0 -46
  173. package/lib/step/sql-stepx.js +0 -68
  174. package/lib/step/sqlxtype.js +0 -16
  175. package/lib/step/step.js +0 -66
  176. package/lib/step/stepenum.js +0 -54
  177. package/lib/topology/link.js +0 -63
  178. package/lib/topology/nodeinfo.js +0 -123
  179. package/lib/topology/status-detector.js +0 -111
  180. package/lib/util/anylist.js +0 -103
  181. package/lib/util/cardinality/hyperloglog.js +0 -106
  182. package/lib/util/cardinality/murmurhash.js +0 -31
  183. package/lib/util/cardinality/registerset.js +0 -75
  184. package/lib/util/errordata.js +0 -21
  185. package/lib/util/iputil_x.js +0 -527
  186. package/lib/util/kube-util.js +0 -73
  187. package/lib/util/paramsecurity.js +0 -80
  188. package/lib/util/pre-process.js +0 -13
  189. package/lib/util/process-seq.js +0 -166
  190. package/lib/util/property-util.js +0 -36
  191. package/lib/util/request-queue.js +0 -70
  192. package/lib/util/requestdouble-queue.js +0 -72
  193. package/lib/util/resourceprofile.js +0 -157
  194. package/lib/util/stop-watch.js +0 -30
  195. package/lib/util/system-util.js +0 -10
  196. package/lib/util/userid-util.js +0 -57
@@ -1,17 +1,18 @@
1
1
  var TraceContextManager = require('../trace/trace-context-manager'),
2
- DataTextAgent = require('../data/datatext-agent'),
3
- SqlStepX = require('../step/sql-stepx'),
4
- DBCStep = require('../step/dbc-step'),
5
2
  HashUtil = require('../util/hashutil'),
3
+ Long = require('long'),
6
4
  Logger = require('../logger'),
7
5
  conf = require('../conf/configure'),
8
- ParamSecurity = require('../util/paramsecurity');
9
- const shimmer = require('../core/shimmer');
10
- const {Buffer} = require("buffer");
6
+ DateUtil = require('../util/dateutil'),
7
+ AsyncSender = require('../udp/async_sender'),
8
+ PacketTypeEnum = require('../udp/packet_type_enum'),
9
+ shimmer = require('../core/shimmer');
10
+ const { AsyncResource } = require('async_hooks');
11
11
 
12
12
  var RedisObserver = function (agent) {
13
13
  this.agent = agent;
14
- this.packages = ['redis', 'ioredis'];
14
+ this.aop = agent.aop;
15
+ this.packages = ['redis'];
15
16
  };
16
17
 
17
18
  RedisObserver.prototype.inject = function (mod, modName) {
@@ -21,197 +22,361 @@ RedisObserver.prototype.inject = function (mod, modName) {
21
22
  mod.__whatap_observe__ = true;
22
23
  Logger.initPrint("RedisObserver");
23
24
 
24
- if (modName === 'redis') {
25
- this._injectRedis(mod);
26
- } else if (modName === 'ioredis') {
27
- this._injectIORedis(mod);
25
+ if (conf.sql_enabled === false) {
26
+ return;
28
27
  }
29
- };
30
28
 
31
- RedisObserver.prototype._injectRedis = function (mod) {
29
+ var self = this;
32
30
  var dbc_hash = 0;
33
- var dbc = '';
34
-
35
- const selectCommand = new Set(['get', 'hGet', 'hmGet']);
36
- const insertCommand = new Set(['set', 'hSet', 'hset', 'hmSet', 'hmset', 'zAdd', 'zadd', 'lSet', 'lset']);
37
- const updateCommand = new Set(['set', 'lSet', 'lset', 'hSet', 'hset', 'zAdd', 'zadd']);
38
- const deleteCommand = new Set(['del', 'lRem', 'sRem', 'srem', 'hDel', 'hdel', 'zRem', 'zrem']);
39
- const commands = new Set([...selectCommand, ...insertCommand, ...updateCommand, ...deleteCommand]);
40
-
41
- shimmer.wrap(mod, 'createClient', function (original) {
42
- return function wrappedCreateClient() {
43
- if (dbc_hash === 0) {
44
- if (arguments.length > 0) {
45
- var info = (arguments[0] || {});
46
- dbc = info.url || '';
47
- dbc_hash = HashUtil.hashFromString(dbc);
48
-
49
- DataTextAgent.DBC.add(dbc_hash, dbc);
50
- DataTextAgent.METHOD.add(dbc_hash, dbc);
51
- DataTextAgent.ERROR.add(dbc_hash, dbc);
52
- }
31
+ var dbc = 'redis://';
32
+
33
+ // Redis 명령어 매핑 ( 번만 정의)
34
+ var REDIS_COMMANDS = {
35
+ // 기본 명령어들
36
+ 'ping': 'PING', 'get': 'GET', 'set': 'SET', 'del': 'DEL', 'exists': 'EXISTS', 'keys': 'KEYS',
37
+ 'setex': 'SETEX', 'setEx': 'SETEX', 'expire': 'EXPIRE', 'expireAt': 'EXPIREAT', 'expireat': 'EXPIREAT',
38
+ 'ttl': 'TTL', 'incr': 'INCR', 'decr': 'DECR', 'incrBy': 'INCRBY', 'incrby': 'INCRBY',
39
+ 'decrBy': 'DECRBY', 'decrby': 'DECRBY', 'mGet': 'MGET', 'mget': 'MGET', 'mSet': 'MSET', 'mset': 'MSET',
40
+
41
+ // Hash 명령어들
42
+ 'hSet': 'HSET', 'hset': 'HSET', 'hGet': 'HGET', 'hget': 'HGET', 'hGetAll': 'HGETALL', 'hgetall': 'HGETALL',
43
+ 'hMGet': 'HMGET', 'hmget': 'HMGET', 'hMSet': 'HMSET', 'hmset': 'HMSET', 'hDel': 'HDEL', 'hdel': 'HDEL',
44
+
45
+ // List 명령어들
46
+ 'lPush': 'LPUSH', 'lpush': 'LPUSH', 'rPush': 'RPUSH', 'rpush': 'RPUSH', 'lPop': 'LPOP', 'lpop': 'LPOP',
47
+ 'rPop': 'RPOP', 'rpop': 'RPOP', 'lRange': 'LRANGE', 'lrange': 'LRANGE', 'lLen': 'LLEN', 'llen': 'LLEN',
48
+ 'lSet': 'LSET', 'lset': 'LSET', 'lRem': 'LREM', 'lrem': 'LREM',
49
+
50
+ // Set 명령어들
51
+ 'sAdd': 'SADD', 'sadd': 'SADD', 'sMembers': 'SMEMBERS', 'smembers': 'SMEMBERS', 'sRem': 'SREM', 'srem': 'SREM',
52
+ 'sCard': 'SCARD', 'scard': 'SCARD',
53
+
54
+ // Sorted Set 명령어들
55
+ 'zAdd': 'ZADD', 'zadd': 'ZADD', 'zRange': 'ZRANGE', 'zrange': 'ZRANGE', 'zRem': 'ZREM', 'zrem': 'ZREM'
56
+ };
57
+
58
+ // DBC 정보 설정
59
+ function setupDbcInfo(connectionOptions) {
60
+ if (dbc_hash === 0 && connectionOptions) {
61
+ dbc = 'redis://';
62
+ dbc += connectionOptions.host || 'localhost';
63
+ dbc += ':';
64
+ dbc += connectionOptions.port || 6379;
65
+ dbc += '/';
66
+ dbc += connectionOptions.db || 0;
67
+ dbc_hash = HashUtil.hashFromString(dbc);
68
+ }
69
+ }
70
+
71
+ // 에러 처리 함수
72
+ function handleRedisError(ctx, err) {
73
+ if (!err) return;
74
+
75
+ try {
76
+ var errorClass = err.code || err.name || 'RedisError';
77
+ var errorMessage = err.message || 'redis error';
78
+
79
+ if (!ctx.error) ctx.error = 1;
80
+ ctx.status = 500;
81
+ var errors = [errorClass, errorMessage];
82
+
83
+ AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
84
+ } catch (e) {
85
+ Logger.printError('WHATAP-216', 'Error handling Redis error', e, false);
86
+ }
87
+ }
88
+
89
+ // 통합 모니터링 함수 (한 번만 정의, 모든 명령어에서 재사용)
90
+ function createRedisWrapper(originalFunction, commandName) {
91
+ return function wrappedMethod() {
92
+ var ctx = TraceContextManager.getCurrentContext();
93
+ if (!ctx) {
94
+ return originalFunction.apply(this, arguments);
53
95
  }
54
96
 
55
- var client = original.apply(this, arguments);
97
+ var args = Array.prototype.slice.call(arguments);
98
+ var callback = null;
99
+ var hasCallback = false;
100
+
101
+ // 콜백 함수 찾기
102
+ if (args.length > 0 && typeof args[args.length - 1] === 'function') {
103
+ callback = args[args.length - 1];
104
+ hasCallback = true;
105
+ }
56
106
 
57
- const commandsArray = Array.from(commands);
58
- commandsArray.forEach(cmdName => {
59
- shimmer.wrap(client, cmdName, function (originalMethod) {
60
- const wrappedFunction = function wrappedSendCommand() {
61
- try{
62
- var ctx = TraceContextManager.getCurrentContext();
63
- if (!ctx) {
64
- return originalMethod.apply(this, arguments);
107
+ const asyncResource = new AsyncResource('redis-command');
108
+
109
+ return asyncResource.runInAsyncScope(() => {
110
+ // DB 연결 패킷 전송
111
+ ctx.start_time = Date.now();
112
+ ctx.elapsed = 0;
113
+ ctx.active_sqlhash = true;
114
+ AsyncSender.send_packet(PacketTypeEnum.TX_DB_CONN, ctx, [dbc]);
115
+
116
+ // Redis 명령 시작
117
+ var command_start_time = Date.now();
118
+ ctx.footprint(`Redis ${commandName} Start`);
119
+
120
+ // Redis에서는 args에서 쿼리 조합 (Python APM 방식 참고)
121
+ var queryParts = [commandName];
122
+ if (args && args.length > 0) {
123
+ // 콜백 함수 제외하고 실제 Redis 인자들만 처리
124
+ var redisArgs = hasCallback ? args.slice(0, -1) : args;
125
+ // 각 인자를 문자열로 변환하고 20자로 제한
126
+ var argStrings = redisArgs.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
+ if (hasCallback) {
135
+ // 비동기 콜백 처리
136
+ const callbackResource = new AsyncResource('redis-callback');
137
+ args[args.length - 1] = asyncResource.bind(function(err, result) {
138
+ var command_elapsed = Date.now() - command_start_time;
139
+ var resultCount = 0;
140
+
141
+ if (err) {
142
+ handleRedisError(ctx, err);
143
+ } else if (result !== undefined) {
144
+ if (Array.isArray(result)) {
145
+ resultCount = result.length;
146
+ } else if (typeof result === 'string' || typeof result === 'number') {
147
+ resultCount = 1;
65
148
  }
149
+ }
66
150
 
67
- var dbc_step = new DBCStep();
68
- dbc_step.hash = dbc_hash;
69
- ctx.profile.push(dbc_step);
70
-
71
- ctx.footprint('Redis Command Start');
72
-
73
- var command = wrappedFunction._commandName || 'unknown';
74
- var sql = `${command.toUpperCase()}`;
75
- var sql_step = new SqlStepX();
76
- sql_step.start_time = ctx.getElapsedTime();
77
- sql_step.dbc = dbc_hash;
78
-
79
- if(conf.getProperty('profile_redis_param_enabled', false)){
80
- if (arguments[0] && typeof arguments[0] === 'string')
81
- sql += ' ' + arguments[0];
82
- else if (arguments[0] && Array.isArray(arguments[0]))
83
- sql += ' ' + JSON.stringify(arguments[0]);
84
- else if (arguments[0] && typeof arguments[0] === 'object'){
85
- sql += ' ' + JSON.stringify(Object.keys(arguments[0]))
86
- }
151
+ ctx.elapsed = command_elapsed;
152
+ ctx.active_sqlhash = false;
153
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, ctx, [dbc, commandText, String(resultCount)]);
154
+ ctx.footprint(`Redis ${commandName} Done`);
87
155
 
88
- if(arguments[1] && arguments[1].length > 0){
89
- var args = Array.isArray(arguments[1]) ? arguments[1] : [arguments[1]];
90
- sql_step.setTrue(1);
91
- var crc = {value: 0};
92
- const result = args.map((arg) => {
93
- if (typeof arg === 'string') {
94
- return `'${arg}'`
156
+ if (callback && typeof callback === 'function') {
157
+ return callbackResource.bind(callback).apply(this, arguments);
158
+ }
159
+ });
160
+
161
+ try {
162
+ return originalFunction.apply(this, args);
163
+ } catch (err) {
164
+ handleRedisError(ctx, err);
165
+ throw err;
166
+ }
167
+ } else {
168
+ // Promise 또는 동기 처리
169
+ try {
170
+ var result = originalFunction.apply(this, args);
171
+
172
+ // Promise 결과 처리
173
+ if (result && typeof result.then === 'function') {
174
+ return result.then(
175
+ function(res) {
176
+ var command_elapsed = Date.now() - command_start_time;
177
+ var resultCount = 0;
178
+
179
+ if (res !== undefined) {
180
+ if (Array.isArray(res)) {
181
+ resultCount = res.length;
182
+ } else if (typeof res === 'string' || typeof res === 'number') {
183
+ resultCount = 1;
95
184
  }
96
- return arg
97
- }).toString()
98
- sql_step.p2 = toParamBytes(result, crc);
99
- sql_step.pcrc = crc.value;
100
- }
101
- }
102
- sql_step.hash = HashUtil.hashFromString(sql);
103
- ctx.profile.push(sql_step);
185
+ }
104
186
 
105
- DataTextAgent.SQL.add(sql_step.hash, sql);
187
+ ctx.elapsed = command_elapsed;
188
+ ctx.active_sqlhash = false;
189
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, ctx, [dbc, commandText, String(resultCount)]);
190
+ ctx.footprint(`Redis ${commandName} Done`);
191
+ return res;
192
+ },
193
+ function(err) {
194
+ handleRedisError(ctx, err);
195
+ var command_elapsed = Date.now() - command_start_time;
196
+ ctx.elapsed = command_elapsed;
197
+ ctx.active_sqlhash = false;
198
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, ctx, [dbc, commandText, '0']);
199
+ ctx.footprint(`Redis ${commandName} Done`);
200
+ throw err;
201
+ }
202
+ );
203
+ } else {
204
+ // 동기 결과 처리
205
+ var command_elapsed = Date.now() - command_start_time;
206
+ var resultCount = 0;
106
207
 
107
- var result = originalMethod.apply(this, arguments);
108
- sql_step.elapsed = ctx.getElapsedTime();
208
+ if (result !== undefined) {
209
+ if (Array.isArray(result)) {
210
+ resultCount = result.length;
211
+ } else if (typeof result === 'string' || typeof result === 'number') {
212
+ resultCount = 1;
213
+ }
214
+ }
109
215
 
110
- ctx.footprint('Redis Command Done');
216
+ ctx.elapsed = command_elapsed;
217
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, ctx, [dbc, commandText, String(resultCount)]);
218
+ ctx.footprint(`Redis ${commandName} Done`);
111
219
  return result;
112
- }catch (e) {
113
- Logger.printError("WHATAP-605", "Redis CRUD error", e, false);
114
- return originalMethod.apply(this, arguments);
115
220
  }
116
- };
117
- wrappedFunction._commandName = cmdName;
118
- return wrappedFunction;
119
- });
221
+ } catch (commandError) {
222
+ handleRedisError(ctx, commandError);
223
+ var command_elapsed = Date.now() - command_start_time;
224
+ ctx.elapsed = command_elapsed;
225
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, ctx, [dbc, commandText, '0']);
226
+ ctx.footprint(`Redis ${commandName} Done`);
227
+ throw commandError;
228
+ }
229
+ }
120
230
  });
121
- return client;
122
231
  };
123
- });
232
+ }
124
233
 
125
- return this;
126
- };
234
+ function wrapRedisMethods(client) {
235
+ var wrappedCount = 0;
236
+ var skippedCount = 0;
237
+ var notFoundCount = 0;
238
+ var alreadyWrappedCount = 0;
127
239
 
128
- RedisObserver.prototype._injectIORedis = function (mod) {
129
- var ioredis_dbc_hash = 0;
130
- var ioredis_dbc = '';
240
+ // 클라이언트에 실제로 존재하는 메서드들 확인
241
+ var availableMethods = Object.getOwnPropertyNames(client)
242
+ .concat(Object.getOwnPropertyNames(Object.getPrototypeOf(client)))
243
+ .filter(name => typeof client[name] === 'function' && !name.startsWith('_'))
244
+ .sort();
131
245
 
132
- const selectCommand = new Set(['get', 'hget', 'hmget', 'zrange', 'smembers', 'lrange']);
133
- const insertCommand = new Set(['set', 'hset', 'hmset', 'zadd', 'lset', 'sadd', 'lpush', 'rpush', 'evalsha']);
134
- const updateCommand = new Set(['set', 'lset', 'hset', 'zadd']);
135
- const deleteCommand = new Set(['del', 'lrem', 'srem', 'hdel', 'zrem']);
136
- const commands = new Set([...selectCommand, ...insertCommand, ...updateCommand, ...deleteCommand]);
246
+ // 번의 루프로 모든 메서드 처리
247
+ Object.keys(REDIS_COMMANDS).forEach(function(methodName) {
137
248
 
138
- shimmer.wrap(mod.prototype, 'sendCommand', function (original) {
139
- return function wrappedSendCommand(command) {
140
- // Get trace context
141
- var ctx = TraceContextManager.getCurrentContext();
142
- if (!ctx) {
143
- return original.apply(this, arguments);
249
+ if (!client[methodName]) {
250
+ notFoundCount++;
251
+ skippedCount++;
252
+ return;
253
+ }
254
+ if (client[methodName].__whatap_wrapped__) {
255
+ alreadyWrappedCount++;
256
+ skippedCount++;
257
+ return;
144
258
  }
145
259
 
146
- if (ioredis_dbc_hash === 0 && this.options) {
147
- ioredis_dbc = 'redis://';
148
- ioredis_dbc += this.options.host || '';
149
- ioredis_dbc += ':';
150
- ioredis_dbc += this.options.port || '';
151
- ioredis_dbc_hash = HashUtil.hashFromString(ioredis_dbc);
152
- DataTextAgent.DBC.add(ioredis_dbc_hash, ioredis_dbc);
260
+ try {
261
+ // 원본 함수 참조 저장
262
+ var originalFunction = client[methodName];
263
+ client[methodName] = createRedisWrapper(originalFunction, REDIS_COMMANDS[methodName]);
264
+ client[methodName].__whatap_wrapped__ = true;
265
+ wrappedCount++;
266
+ } catch (e) {
267
+ skippedCount++;
268
+ Logger.printError('WHATAP-217', `Error wrapping ${methodName}`, e, false);
153
269
  }
270
+ });
154
271
 
155
- if (command && command.name && commands.has(command.name.toLowerCase())) {
156
- var dbc_step = new DBCStep();
157
- dbc_step.hash = ioredis_dbc_hash;
158
- dbc_step.start_time = ctx.getElapsedTime();
159
- ctx.profile.push(dbc_step);
160
-
161
- try {
162
- var sql_step = new SqlStepX();
163
- var sql = command.name.toUpperCase();
164
- if(conf.getProperty('profile_redis_param_enabled', false)){
165
- var args = command.args;
166
- var key = args.shift();
167
- if (key && typeof key === 'string')
168
- sql += ' ' + key;
169
-
170
- if(args && args.length > 0){
171
- sql_step.setTrue(1);
172
- var crc = {value: 0};
173
- const result = args.map((arg) => {
174
- if (typeof arg === 'string') {
175
- return `'${arg}'`
176
- }
177
- return arg;
178
- }).toString()
179
- sql_step.p2 = toParamBytes(result, crc);
180
- sql_step.pcrc = crc.value;
272
+ return wrappedCount > 0;
273
+ }
274
+
275
+ // createClient 래핑
276
+ if (mod.createClient && !mod.createClient.__whatap_wrapped__) {
277
+ shimmer.wrap(mod, 'createClient', function(original) {
278
+ return function wrappedCreateClient() {
279
+ var args = Array.prototype.slice.call(arguments);
280
+ var ctx = TraceContextManager.getCurrentContext();
281
+
282
+ if (ctx) {
283
+ ctx.start_time = Date.now();
284
+ ctx.footprint('IORedis Client Created');
285
+ ctx.db_opening = true;
286
+ }
287
+
288
+ if (args.length > 0) {
289
+ var options = args[0] || {};
290
+ // URL에서 연결 정보 추출
291
+ if (typeof options === 'string') {
292
+ try {
293
+ var url = new URL(options);
294
+ setupDbcInfo({
295
+ host: url.hostname,
296
+ port: parseInt(url.port) || 6379,
297
+ db: url.pathname.slice(1) || 0
298
+ });
299
+ } catch (e) {
300
+ setupDbcInfo({ host: 'localhost', port: 6379, db: 0 });
301
+ }
302
+ } else if (options.url) {
303
+ try {
304
+ var url = new URL(options.url);
305
+ setupDbcInfo({
306
+ host: url.hostname,
307
+ port: parseInt(url.port) || 6379,
308
+ db: options.database || url.pathname.slice(1) || 0
309
+ });
310
+ } catch (e) {
311
+ setupDbcInfo(options);
181
312
  }
313
+ } else {
314
+ setupDbcInfo(options);
182
315
  }
316
+ }
183
317
 
184
- sql_step.hash = HashUtil.hashFromString(sql);
185
- sql_step.start_time = ctx.getElapsedTime();
186
- DataTextAgent.SQL.add(sql_step.hash, sql);
187
- ctx.profile.push(sql_step);
318
+ var client = original.apply(this, args);
188
319
 
189
- var originalPromise = original.apply(this, arguments);
190
- return originalPromise.finally(function () {
191
- dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
192
- sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
193
- });
194
- } catch (e) {
195
- Logger.printError("WHATAP-605", "ioredis CRUD error", e, false);
196
- return original.apply(this, arguments);
197
- }
198
- }
320
+ // Redis 클라이언트 후킹
321
+ if (client) {
322
+ var wrapAttempts = 0;
323
+ var maxAttempts = 3;
324
+ var wrapCompleted = false; // 완료 플래그 추가
199
325
 
200
- return original.apply(this, arguments);
201
- };
202
- });
326
+ function attemptBatchWrap(reason) {
327
+ if (wrapCompleted) {
328
+ return true;
329
+ }
203
330
 
204
- return this;
205
- };
331
+ wrapAttempts++;
206
332
 
207
- var toParamBytes = function (p, crc) {
208
- if (p == null || p.length === 0) {
209
- return null;
210
- }
211
- try {
212
- return ParamSecurity.encrypt(Buffer.from(p, 'utf8'), crc);
213
- } catch (e) {
214
- return null;
333
+ if (wrapRedisMethods(client)) {
334
+ wrapCompleted = true; // 완료 마킹
335
+ return true;
336
+ }
337
+
338
+ if (wrapAttempts >= maxAttempts) {
339
+ return false;
340
+ }
341
+
342
+ return false;
343
+ }
344
+
345
+ // 이벤트 기반 래핑
346
+ if (client.on && typeof client.on === 'function') {
347
+ client.on('connect', function() {
348
+ attemptBatchWrap('Redis connect event');
349
+ });
350
+
351
+ client.on('ready', function() {
352
+ attemptBatchWrap('Redis ready event');
353
+ });
354
+ }
355
+
356
+ // 즉시 시도
357
+ if (!attemptBatchWrap('Immediate attempt')) {
358
+ // 지연 시도
359
+ setImmediate(function() {
360
+ if (!attemptBatchWrap('setImmediate attempt')) {
361
+ // 1초 후 마지막 시도
362
+ setTimeout(function() {
363
+ attemptBatchWrap('Final timeout attempt');
364
+ }, 1000);
365
+ }
366
+ });
367
+ }
368
+ }
369
+
370
+ if (ctx) {
371
+ ctx.elapsed = Date.now() - ctx.start_time;
372
+ ctx.footprint('Redis Connecting Done');
373
+ ctx.db_opening = false;
374
+ }
375
+
376
+ return client;
377
+ };
378
+ });
379
+ mod.createClient.__whatap_wrapped__ = true;
215
380
  }
216
381
  };
217
382