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
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Created by yunhoi on 12/06/2017.
3
+ */
4
+
5
+ var TraceContextManager = require('../trace/trace-context-manager'),
6
+ Logger = require('../logger');
7
+
8
+ var PromiseObserver = function (agent) {
9
+ this.agent = agent;
10
+ this.packages = ['promise', 'bluebird'];
11
+ };
12
+
13
+ PromiseObserver.prototype.inject = function (mod, moduleName) {
14
+
15
+ var self = this;
16
+ var aop = self.agent.aop;
17
+
18
+ if(mod.__whatap_observe__) {return;}
19
+ mod.__whatap_observe__ = true;
20
+ Logger.initPrint("PromiseObserver");
21
+ aop.both(mod.prototype, 'then'
22
+ , function (obj, args, lctx) {
23
+ var ctx = TraceContextManager.getCurrentContext();
24
+ aop.functionHook(args, -1, function (obj, args) {
25
+ TraceContextManager.resume(ctx);
26
+ })
27
+ });
28
+
29
+ };
30
+
31
+ exports.PromiseObserver = PromiseObserver;
@@ -1,18 +1,17 @@
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'),
2
5
  HashUtil = require('../util/hashutil'),
3
- Long = require('long'),
4
6
  Logger = require('../logger'),
5
7
  conf = require('../conf/configure'),
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');
8
+ ParamSecurity = require('../util/paramsecurity');
9
+ const shimmer = require('../core/shimmer');
10
+ const {Buffer} = require("buffer");
11
11
 
12
12
  var RedisObserver = function (agent) {
13
13
  this.agent = agent;
14
- this.aop = agent.aop;
15
- this.packages = ['redis'];
14
+ this.packages = ['redis', 'ioredis'];
16
15
  };
17
16
 
18
17
  RedisObserver.prototype.inject = function (mod, modName) {
@@ -22,361 +21,197 @@ RedisObserver.prototype.inject = function (mod, modName) {
22
21
  mod.__whatap_observe__ = true;
23
22
  Logger.initPrint("RedisObserver");
24
23
 
25
- if (conf.sql_enabled === false) {
26
- return;
24
+ if (modName === 'redis') {
25
+ this._injectRedis(mod);
26
+ } else if (modName === 'ioredis') {
27
+ this._injectIORedis(mod);
27
28
  }
29
+ };
28
30
 
29
- var self = this;
31
+ RedisObserver.prototype._injectRedis = function (mod) {
30
32
  var dbc_hash = 0;
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);
95
- }
96
-
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
- }
106
-
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);
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);
131
52
  }
132
- var commandText = `Redis ` + queryParts.join(' ');
53
+ }
133
54
 
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;
55
+ var client = original.apply(this, arguments);
140
56
 
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;
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);
148
65
  }
149
- }
150
66
 
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`);
155
-
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;
184
- }
185
- }
186
-
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;
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]))
201
86
  }
202
- );
203
- } else {
204
- // 동기 결과 처리
205
- var command_elapsed = Date.now() - command_start_time;
206
- var resultCount = 0;
207
87
 
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;
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}'`
95
+ }
96
+ return arg
97
+ }).toString()
98
+ sql_step.p2 = toParamBytes(result, crc);
99
+ sql_step.pcrc = crc.value;
213
100
  }
214
101
  }
102
+ sql_step.hash = HashUtil.hashFromString(sql);
103
+ ctx.profile.push(sql_step);
104
+
105
+ DataTextAgent.SQL.add(sql_step.hash, sql);
215
106
 
216
- ctx.elapsed = command_elapsed;
217
- AsyncSender.send_packet(PacketTypeEnum.TX_SQL, ctx, [dbc, commandText, String(resultCount)]);
218
- ctx.footprint(`Redis ${commandName} Done`);
107
+ var result = originalMethod.apply(this, arguments);
108
+ sql_step.elapsed = ctx.getElapsedTime();
109
+
110
+ ctx.footprint('Redis Command Done');
219
111
  return result;
112
+ }catch (e) {
113
+ Logger.printError("WHATAP-605", "Redis CRUD error", e, false);
114
+ return originalMethod.apply(this, arguments);
220
115
  }
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
- }
116
+ };
117
+ wrappedFunction._commandName = cmdName;
118
+ return wrappedFunction;
119
+ });
230
120
  });
121
+ return client;
231
122
  };
232
- }
123
+ });
233
124
 
234
- function wrapRedisMethods(client) {
235
- var wrappedCount = 0;
236
- var skippedCount = 0;
237
- var notFoundCount = 0;
238
- var alreadyWrappedCount = 0;
125
+ return this;
126
+ };
239
127
 
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();
128
+ RedisObserver.prototype._injectIORedis = function (mod) {
129
+ var ioredis_dbc_hash = 0;
130
+ var ioredis_dbc = '';
245
131
 
246
- // 번의 루프로 모든 메서드 처리
247
- Object.keys(REDIS_COMMANDS).forEach(function(methodName) {
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]);
248
137
 
249
- if (!client[methodName]) {
250
- notFoundCount++;
251
- skippedCount++;
252
- return;
253
- }
254
- if (client[methodName].__whatap_wrapped__) {
255
- alreadyWrappedCount++;
256
- skippedCount++;
257
- return;
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);
258
144
  }
259
145
 
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);
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);
269
153
  }
270
- });
271
-
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);
312
- }
313
- } else {
314
- setupDbcInfo(options);
315
- }
316
- }
317
-
318
- var client = original.apply(this, args);
319
154
 
320
- // Redis 클라이언트 후킹
321
- if (client) {
322
- var wrapAttempts = 0;
323
- var maxAttempts = 3;
324
- var wrapCompleted = false; // 완료 플래그 추가
325
-
326
- function attemptBatchWrap(reason) {
327
- if (wrapCompleted) {
328
- return true;
329
- }
330
-
331
- wrapAttempts++;
332
-
333
- if (wrapRedisMethods(client)) {
334
- wrapCompleted = true; // 완료 마킹
335
- return true;
336
- }
337
-
338
- if (wrapAttempts >= maxAttempts) {
339
- return false;
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;
340
181
  }
341
-
342
- return false;
343
182
  }
344
183
 
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
- }
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);
355
188
 
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
- }
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);
368
197
  }
198
+ }
369
199
 
370
- if (ctx) {
371
- ctx.elapsed = Date.now() - ctx.start_time;
372
- ctx.footprint('Redis Connecting Done');
373
- ctx.db_opening = false;
374
- }
200
+ return original.apply(this, arguments);
201
+ };
202
+ });
203
+
204
+ return this;
205
+ };
375
206
 
376
- return client;
377
- };
378
- });
379
- mod.createClient.__whatap_wrapped__ = true;
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;
380
215
  }
381
216
  };
382
217
 
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Copyright 2016 the WHATAP project authors. All rights reserved.
3
+ * Use of this source code is governed by a license that
4
+ * can be found in the LICENSE file.
5
+ */
6
+
7
+ var TraceContextManager = require('../trace/trace-context-manager'),
8
+ conf = require('../conf/configure'),
9
+ DataTextAgent = require('../data/datatext-agent'),
10
+ DataProfileAgent = require('../data/dataprofile-agent'),
11
+ EventLevel = require('../data/event-level'),
12
+ TxRecord = require('../service/tx-record'),
13
+ ProfilePack = require('../pack/profile-pack'),
14
+ SecurityMaster = require('../net/security-master'),
15
+ HashUtil = require('../util/hashutil'),
16
+ DateUtil = require('../util/dateutil'),
17
+ ResourceProfile = require('../util/resourceprofile'),
18
+ MessageStep = require('../step/message-step'),
19
+ Logger = require('../logger');
20
+
21
+ var ScheduleObserver = function(agent){
22
+ this.agent = agent;
23
+ this.packages = ['node-schedule'];
24
+ };
25
+
26
+ ScheduleObserver.prototype.inject = function (mod, moduleName) {
27
+ if(mod.__whatap_observe__) { return; }
28
+ mod.__whatap_observe__ = true;
29
+ Logger.initPrint("ScheduleObserver");
30
+ var self = this;
31
+ var aop = self.agent.aop;
32
+
33
+ aop.after(mod, 'scheduleJob',
34
+ function (obj, args, ret) {
35
+ var orig = ret.job;
36
+ if(!orig) return;
37
+ ret.job = async () => {
38
+ var ctx = null;
39
+ if(conf['trace_node_schedule_enabled']) {
40
+ ctx = TraceContextManager.startTrace('ScheduleJob');
41
+ }
42
+ var result = null;
43
+ try {
44
+ result = await orig.apply(this, arguments);
45
+ } catch(err) {
46
+ Logger.printError('WHATAP-607', 'End batch error..', err, false);
47
+ }
48
+ if(conf['trace_node_schedule_enabled'] && ctx != null) {
49
+ try {
50
+ var step = new MessageStep();
51
+ step.msg = 'Node ScheduleJob finished!!!!!'
52
+ step.hash = HashUtil.hashFromString(step.msg);;
53
+ step.start_time = ctx.getElapsedTime();
54
+ step.desc = '';
55
+ DataTextAgent.MESSAGE.add(step.hash, step.msg);
56
+ ctx.profile.add(step);
57
+ TraceContextManager.endTrace(ctx);
58
+ if(ctx != null) ctx = null;
59
+ } catch(err) {}
60
+ }
61
+ return result;
62
+ };
63
+ }
64
+ );
65
+ };
66
+
67
+ exports.ScheduleObserver = ScheduleObserver;