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,583 +1,388 @@
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
+
1
7
  var TraceContextManager = require('../trace/trace-context-manager'),
2
- SqlStepX = require('../step/sql-stepx'),
3
- DataTextAgent = require('../data/datatext-agent'),
4
- HashUtil = require('../util/hashutil');
5
- const DBCStep = require("../step/dbc-step");
6
- const Logger = require("../logger");
7
- const ParamSecurity = require("../util/paramsecurity");
8
- const {Buffer} = require("buffer");
9
- const conf = require('../conf/configure');
10
- const shimmer = require('../core/shimmer');
8
+ HashUtil = require('../util/hashutil'),
9
+ Logger = require('../logger'),
10
+ conf = require('../conf/configure'),
11
+ AsyncSender = require('../udp/async_sender'),
12
+ PacketTypeEnum = require('../udp/packet_type_enum'),
13
+ shimmer = require('../core/shimmer'),
14
+ AsyncResource = require('async_hooks').AsyncResource;
11
15
 
12
16
  var MongooseObserver = function (agent) {
13
17
  this.agent = agent;
14
18
  this.packages = ['mongoose'];
15
19
  };
16
20
 
17
- var dbc_step, dbc, conn_dbc_hash;
18
-
19
- // 단일 필터 메서드 (첫 번째 인자가 filter)
20
- const collectionMethodsWithFilter = [
21
- 'find',
22
- // 'findById',
23
- 'findOne',
24
- 'countDocuments',
25
- 'distinct',
26
- 'deleteOne',
27
- 'deleteMany'
28
- ];
29
-
30
- // 이중 필터 메서드 (첫 번째: filter, 두 번째: update)
31
- const collectionMethodsWithTwoFilters = [
32
- 'findOneAndUpdate',
33
- // 'findByIdAndUpdate',
34
- 'updateOne',
35
- 'updateMany',
36
- 'findOneAndDelete',
37
- // 'findByIdAndDelete',
38
- 'replaceOne'
39
- ];
40
-
41
- // 특수 메서드
42
- const specialMethods = [
43
- 'create',
44
- 'insertMany',
45
- 'aggregate'
46
- ];
47
-
48
- MongooseObserver.prototype.inject = function (mod, modName) {
49
- var self = this;
50
-
51
- // mongoose.connect 후킹 (연결 정보 저장)
52
- shimmer.wrap(mod, 'connect', function(originalConnect) {
53
- return function wrappedConnect() {
54
- if (arguments[0]) {
55
- dbc = arguments[0];
56
- conn_dbc_hash = HashUtil.hashFromString(dbc);
57
- }
58
- return originalConnect.apply(this, arguments);
59
- };
60
- });
61
-
62
- // Model 생성 후킹
63
- shimmer.wrap(mod, 'model', function(originalModel) {
64
- return function wrappedModel() {
65
- const model = originalModel.apply(this, arguments);
66
-
67
- // 생성된 Model에 메서드 패치 적용
68
- self.patchModelMethods(model);
69
-
70
- return model;
71
- };
72
- });
21
+ var dbc_hash = 0;
22
+ var dbc = '';
23
+
24
+ // Mongoose 명령어 매핑
25
+ var MONGOOSE_COMMANDS = {
26
+ 'create': 'CREATE',
27
+ 'insertMany': 'INSERTMANY',
28
+ 'find': 'FIND',
29
+ 'findById': 'FINDBYID',
30
+ 'findOne': 'FINDONE',
31
+ 'countDocuments': 'COUNTDOCUMENTS',
32
+ 'distinct': 'DISTINCT',
33
+ 'updateMany': 'UPDATEMANY',
34
+ 'updateOne': 'UPDATEONE',
35
+ 'replaceOne': 'REPLACEONE',
36
+ 'findOneAndUpdate': 'FINDONEANDUPDATE',
37
+ 'findByIdAndUpdate': 'FINDBYIDANDUPDATE',
38
+ 'deleteOne': 'DELETEONE',
39
+ 'deleteMany': 'DELETEMANY',
40
+ 'findOneAndDelete': 'FINDONEANDDELETE',
41
+ 'findByIdAndDelete': 'FINDBYIDANDDELETE',
42
+ 'aggregate': 'AGGREGATE'
73
43
  };
74
44
 
75
- MongooseObserver.prototype.patchModelMethods = function(Model) {
76
- if(Model.__whatap_observe__) {return;}
77
- Model.__whatap_observe__ = true;
78
- var self = this;
79
-
80
- // 단일 필터 메서드 패치
81
- collectionMethodsWithFilter.forEach(function(methodName) {
82
- if (Model[methodName]) {
83
- shimmer.wrap(Model, methodName, function(originalMethod) {
84
- return self.createSingleFilterWrapper(originalMethod, methodName);
85
- });
86
- }
87
- });
88
-
89
- // 이중 필터 메서드 패치
90
- collectionMethodsWithTwoFilters.forEach(function(methodName) {
91
- if (Model[methodName]) {
92
- shimmer.wrap(Model, methodName, function(originalMethod) {
93
- return self.createDoubleFilterWrapper(originalMethod, methodName);
94
- });
95
- }
96
- });
97
-
98
- // aggregate 메서드 패치
99
- if (Model.aggregate) {
100
- shimmer.wrap(Model, 'aggregate', function(originalAggregate) {
101
- return self.createAggregateWrapper(originalAggregate);
102
- });
45
+ // DBC 정보 설정
46
+ function setupDbcInfo(connectionString) {
47
+ if (dbc_hash === 0 && connectionString) {
48
+ dbc = connectionString;
49
+ dbc_hash = HashUtil.hashFromString(dbc);
50
+ Logger.print('WHATAP-MONGOOSE-DBC', 'DBC setup: ' + dbc, false);
103
51
  }
52
+ }
104
53
 
105
- // create, insertMany 패치
106
- ['create', 'insertMany'].forEach(function(methodName) {
107
- if (Model[methodName]) {
108
- shimmer.wrap(Model, methodName, function(originalMethod) {
109
- return self.createInsertWrapper(originalMethod, methodName);
110
- });
111
- }
112
- });
113
- };
114
-
115
- MongooseObserver.prototype.createSingleFilterWrapper = function(originalMethod, methodName) {
116
- var self = this;
117
-
118
- return function wrappedSingleFilter() {
119
- const ctx = TraceContextManager.getCurrentContext();
120
- if (!ctx) {
121
- return originalMethod.apply(this, arguments);
122
- }
123
- ctx.__mongoose_traced__ = true;
124
-
125
- const dbc_step = self.createDBCStep(ctx);
126
- dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
127
- ctx.profile.push(dbc_step);
128
-
129
- // SQL Step 생성
130
- const sql_step = new SqlStepX();
131
- sql_step.start_time = ctx.getElapsedTime();
132
-
133
- const result = originalMethod.apply(this, arguments);
134
- sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
135
-
136
- try {
137
- const filterObj = arguments[0];
138
- const parseResult = self.parseSingleFilter(methodName, filterObj);
54
+ // Mongoose 에러 처리
55
+ function handleMongooseError(ctx, err) {
56
+ if (!err) return;
139
57
 
140
- // SQL 문자열 생성: "ModelName methodName where=[field1,field2]"
141
- const modelName = this.modelName || 'Unknown';
142
- let sql = `${modelName} ${methodName}`;
143
-
144
- if (parseResult.whereFields.length > 0) {
145
- sql += ` where=[${parseResult.whereFields.join(',')}]`;
146
- }
147
-
148
- // SQL Step 완성
149
- sql_step.hash = HashUtil.hashFromString(sql);
150
- DataTextAgent.SQL.add(sql_step.hash, sql);
151
-
152
- // 파라미터 암호화 처리
153
- if (conf.getProperty('profile_mongodb_param_enabled', false) === true &&
154
- parseResult.values.length > 0) {
155
- sql_step.setTrue(1);
156
- var crc = {value: 0};
157
- const valuesString = parseResult.values.map(val => {
158
- if (typeof val === 'string') {
159
- return `'${val}'`;
160
- }
161
- return val;
162
- }).toString();
163
- sql_step.p2 = toParamBytes(valuesString, crc);
164
- sql_step.pcrc = crc.value;
58
+ try {
59
+ var errorClass = err.code || err.name || 'MongooseError';
60
+ var errorMessage = err.message || 'mongoose error';
61
+ var errorStack = '';
62
+
63
+ if (conf.trace_sql_error_stack && conf.trace_sql_error_depth && err.stack) {
64
+ var traceDepth = conf.trace_sql_error_depth;
65
+ var stackLines = err.stack.split("\n");
66
+ if (stackLines.length > traceDepth) {
67
+ stackLines = stackLines.slice(0, traceDepth + 1);
165
68
  }
166
-
167
- ctx.profile.push(sql_step);
168
-
169
- } catch (e) {
170
- Logger.printError("WHATAP-611", "Mongodb single filter query error", e, false);
69
+ errorStack = stackLines.join("\n");
171
70
  }
172
71
 
173
- return result;
174
- };
175
- };
176
-
177
- MongooseObserver.prototype.createDoubleFilterWrapper = function(originalMethod, methodName) {
178
- var self = this;
179
-
180
- return function wrappedDoubleFilter() {
181
- const ctx = TraceContextManager.getCurrentContext();
182
- if (!ctx) {
183
- return originalMethod.apply(this, arguments);
72
+ var shouldAddError = false;
73
+ if (conf._is_trace_ignore_err_cls_contains === true && errorClass &&
74
+ errorClass.toString().indexOf(conf.trace_ignore_err_cls_contains) < 0) {
75
+ shouldAddError = true;
76
+ } else if (conf._is_trace_ignore_err_msg_contains === true && errorMessage &&
77
+ errorMessage.indexOf(conf.trace_ignore_err_msg_contains) < 0) {
78
+ shouldAddError = true;
79
+ } else if (conf._is_trace_ignore_err_cls_contains === false &&
80
+ conf._is_trace_ignore_err_msg_contains === false) {
81
+ shouldAddError = true;
184
82
  }
185
- ctx.__mongoose_traced__ = true;
186
-
187
- // DBC Step 생성
188
- const dbc_step = self.createDBCStep(ctx);
189
- dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
190
- ctx.profile.push(dbc_step);
191
-
192
- // SQL Step 생성
193
- const sql_step = new SqlStepX();
194
- sql_step.start_time = ctx.getElapsedTime();
195
-
196
- const result = originalMethod.apply(this, arguments);
197
- sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
198
-
199
- try {
200
- const filterObj = arguments[0];
201
- const updateObj = arguments[1];
202
- const parseResult = self.parseDoubleFilter(methodName, filterObj, updateObj);
203
83
 
204
- // SQL 문자열 생성: "ModelName methodName where=[field1] field=[field2,field3]"
205
- const modelName = this.modelName || 'Unknown';
206
- let sql = `${modelName} ${methodName}`;
207
-
208
- if (parseResult.whereFields.length > 0) {
209
- sql += ` where=[${parseResult.whereFields.join(',')}]`;
210
- }
211
-
212
- if (parseResult.updateFields.length > 0) {
213
- sql += ` field=[${parseResult.updateFields.join(',')}]`;
84
+ if (shouldAddError) {
85
+ if (!ctx.error) ctx.error = 1;
86
+ ctx.status = 500;
87
+ var errors = [errorClass];
88
+ if (errorMessage) {
89
+ errors.push(errorMessage);
214
90
  }
215
-
216
- // SQL Step 완성
217
- sql_step.hash = HashUtil.hashFromString(sql);
218
- DataTextAgent.SQL.add(sql_step.hash, sql);
219
-
220
- // 파라미터 암호화 처리 (where 값 + update 값)
221
- if (conf.getProperty('profile_mongodb_param_enabled', false) === true && parseResult.values.length > 0) {
222
- sql_step.setTrue(1);
223
- var crc = {value: 0};
224
- const valuesString = parseResult.values.map(val => {
225
- if (typeof val === 'string') {
226
- return `'${val}'`;
227
- }
228
- return val;
229
- }).toString();
230
- sql_step.p2 = toParamBytes(valuesString, crc);
231
- sql_step.pcrc = crc.value;
91
+ if (errorStack || err.stack) {
92
+ errors.push(errorStack || err.stack);
232
93
  }
233
94
 
234
- ctx.profile.push(sql_step);
235
-
236
- } catch (e) {
237
- Logger.printError("WHATAP-611", "Mongodb double filter query error", e, false);
238
- }
239
-
240
- return result;
241
- };
242
- };
243
-
244
- MongooseObserver.prototype.createAggregateWrapper = function(originalAggregate) {
245
- var self = this;
246
-
247
- return function wrappedAggregate() {
248
- const ctx = TraceContextManager.getCurrentContext();
249
- if (!ctx) {
250
- return originalAggregate.apply(this, arguments);
95
+ AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
251
96
  }
252
- ctx.__mongoose_traced__ = true;
253
-
254
- const dbc_step = self.createDBCStep(ctx);
255
- dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
256
- ctx.profile.push(dbc_step);
257
-
258
- // SQL Step 생성
259
- const sql_step = new SqlStepX();
260
- sql_step.start_time = ctx.getElapsedTime();
261
-
262
- const result = originalAggregate.apply(this, arguments);
263
- sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
264
-
265
- try {
266
- const pipeline = arguments[0];
267
- const parseResult = self.parseAggregatePipeline(pipeline);
268
-
269
- // SQL 문자열 생성
270
- const modelName = this.modelName || 'Unknown';
271
- let sql = `${modelName} aggregate`;
272
-
273
- if (parseResult.matchFields.length > 0) {
274
- sql += ` where=[${parseResult.matchFields.join(',')}]`;
275
- }
276
-
277
- if (parseResult.groupFields.length > 0) {
278
- sql += ` group=[${parseResult.groupFields.join(',')}]`;
97
+ } catch (e) {
98
+ Logger.printError('WHATAP-245', 'Error handling Mongoose error', e, false);
99
+ }
100
+ }
101
+
102
+ // Model 메서드 래퍼 생성
103
+ function createModelMethodWrapper(methodName) {
104
+ return function(original) {
105
+ return function wrappedMethod() {
106
+ var ctx = TraceContextManager.getCurrentContext();
107
+ if (!ctx) {
108
+ return original.apply(this, arguments);
279
109
  }
280
110
 
281
- if (parseResult.projectFields.length > 0) {
282
- sql += ` field=[${parseResult.projectFields.join(',')}]`;
283
- }
111
+ var args = Array.prototype.slice.call(arguments);
112
+ var modelName = this.modelName || this.collection?.collectionName || 'unknown';
113
+ var commandName = MONGOOSE_COMMANDS[methodName] || methodName.toUpperCase();
114
+
115
+ const asyncResource = new AsyncResource('mongoose-command');
116
+
117
+ return asyncResource.runInAsyncScope(() => {
118
+ // DB 연결 패킷 전송
119
+ ctx.start_time = Date.now();
120
+ ctx.elapsed = 0;
121
+ AsyncSender.send_packet(PacketTypeEnum.TX_DB_CONN, ctx, [dbc]);
122
+
123
+ var command_start_time = Date.now();
124
+ ctx.footprint(`Mongoose ${commandName} Start`);
125
+
126
+ // 쿼리 텍스트 구성
127
+ var queryParts = [commandName, modelName];
128
+ var fieldNames = [];
129
+
130
+ try {
131
+ if (methodName === 'aggregate' && Array.isArray(args[0])) {
132
+ // aggregate 파이프라인 처리
133
+ var pipelineFields = [];
134
+ args[0].forEach(function(stage) {
135
+ if (stage.$match) {
136
+ pipelineFields = pipelineFields.concat(Object.keys(stage.$match));
137
+ }
138
+ if (stage.$group) {
139
+ pipelineFields = pipelineFields.concat(Object.keys(stage.$group));
140
+ }
141
+ });
142
+ if (pipelineFields.length > 0) {
143
+ queryParts.push('field=[' + pipelineFields.join(',') + ']');
144
+ }
145
+ } else {
146
+ // 첫 번째 인자에서 필드명 추출
147
+ if (args[0] && typeof args[0] === 'object' && !Array.isArray(args[0])) {
148
+ fieldNames = Object.keys(args[0]);
149
+ }
150
+ if (fieldNames.length > 0) {
151
+ queryParts.push('field=[' + fieldNames.join(',') + ']');
152
+ }
284
153
 
285
- // SQL Step 완성
286
- sql_step.hash = HashUtil.hashFromString(sql);
287
- DataTextAgent.SQL.add(sql_step.hash, sql);
288
-
289
- // 파라미터 암호화 처리 (주로 $match 조건값들)
290
- if (conf.getProperty('profile_mongodb_param_enabled', false) === true &&
291
- parseResult.values.length > 0) {
292
- sql_step.setTrue(1);
293
- var crc = {value: 0};
294
- const valuesString = parseResult.values.map(val => {
295
- if (typeof val === 'string') {
296
- return `'${val}'`;
154
+ // 번째 인자에서 업데이트 필드 추출 (update 계열 메서드)
155
+ if (args[1] && typeof args[1] === 'object' &&
156
+ (methodName.includes('update') || methodName.includes('Update'))) {
157
+ var updateFields = [];
158
+ if (args[1].$set) {
159
+ updateFields = updateFields.concat(Object.keys(args[1].$set));
160
+ } else {
161
+ updateFields = Object.keys(args[1]);
162
+ }
163
+ if (updateFields.length > 0) {
164
+ queryParts.push('value=[' + updateFields.join(',') + ']');
165
+ }
166
+ }
297
167
  }
298
- return val;
299
- }).toString();
300
- sql_step.p2 = toParamBytes(valuesString, crc);
301
- sql_step.pcrc = crc.value;
302
- }
168
+ } catch (e) {
169
+ Logger.printError('WHATAP-246', 'Error extracting field names', e, false);
170
+ }
303
171
 
304
- ctx.profile.push(sql_step);
172
+ var commandText = 'MongoDB ' + queryParts.join(' ');
305
173
 
306
- } catch (e) {
307
- Logger.printError("WHATAP-611", "Mongodb aggregate query error", e, false);
308
- }
174
+ function executeCallback(err, result) {
175
+ try {
176
+ var command_elapsed = Date.now() - command_start_time;
309
177
 
310
- return result;
311
- };
312
- };
178
+ if (err) {
179
+ handleMongooseError(ctx, err);
180
+ }
313
181
 
314
- MongooseObserver.prototype.createInsertWrapper = function(originalMethod, methodName) {
315
- var self = this;
182
+ ctx.elapsed = command_elapsed;
183
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, ctx, [dbc, commandText, '0']);
184
+ ctx.footprint(`Mongoose ${commandName} Done`);
185
+ } catch (e) {
186
+ Logger.printError('WHATAP-247', 'Error in Mongoose callback', e, false);
187
+ }
188
+ }
316
189
 
317
- return function wrappedInsert() {
318
- const ctx = TraceContextManager.getCurrentContext();
319
- if (!ctx) {
320
- return originalMethod.apply(this, arguments);
321
- }
322
- ctx.__mongoose_traced__ = true;
190
+ // 콜백 처리
191
+ var hasCallback = false;
192
+ for (var i = args.length - 1; i >= 0; i--) {
193
+ if (typeof args[i] === 'function') {
194
+ hasCallback = true;
195
+ var originalCallback = args[i];
196
+
197
+ args[i] = asyncResource.bind(function() {
198
+ var callbackArgs = Array.prototype.slice.call(arguments);
199
+ executeCallback(callbackArgs[0], callbackArgs[1]);
200
+
201
+ if (originalCallback && typeof originalCallback === 'function') {
202
+ return originalCallback.apply(this, callbackArgs);
203
+ }
204
+ });
205
+ break;
206
+ }
207
+ }
323
208
 
324
- const dbc_step = self.createDBCStep(ctx);
325
- dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
326
- ctx.profile.push(dbc_step);
209
+ try {
210
+ var result = original.apply(this, args);
211
+
212
+ // Promise 기반 처리
213
+ if (!hasCallback && result && typeof result.then === 'function') {
214
+ return result.then(function(res) {
215
+ executeCallback(null, res);
216
+ return res;
217
+ }).catch(function(err) {
218
+ executeCallback(err, null);
219
+ throw err;
220
+ });
221
+ }
327
222
 
328
- // SQL Step 생성
329
- const sql_step = new SqlStepX();
330
- sql_step.start_time = ctx.getElapsedTime();
223
+ // 동기적 결과 처리
224
+ if (!hasCallback) {
225
+ executeCallback(null, result);
226
+ }
331
227
 
332
- const result = originalMethod.apply(this, arguments);
333
- sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
228
+ return result;
229
+ } catch (executeError) {
230
+ executeCallback(executeError, null);
231
+ throw executeError;
232
+ }
233
+ });
234
+ };
235
+ };
236
+ }
334
237
 
335
- try {
336
- const docs = arguments[0];
337
- const parseResult = self.parseInsertDocuments(methodName, docs);
238
+ // Connection wrapper 함수
239
+ var createConnectWrapper = function() {
240
+ return function(original) {
241
+ return function wrappedConnect() {
242
+ var args = Array.prototype.slice.call(arguments);
243
+ var connectionString = args[0];
244
+ var ctx = TraceContextManager.getCurrentContext();
338
245
 
339
- // SQL 문자열 생성
340
- const modelName = this.modelName || 'Unknown';
341
- let sql = `${modelName} ${methodName}`;
246
+ setupDbcInfo(connectionString);
342
247
 
343
- if (parseResult.insertFields.length > 0) {
344
- sql += ` field=[${parseResult.insertFields.join(',')}]`;
248
+ if (!ctx) {
249
+ return original.apply(this, arguments);
345
250
  }
346
251
 
347
- // SQL Step 완성
348
- sql_step.hash = HashUtil.hashFromString(sql);
349
- DataTextAgent.SQL.add(sql_step.hash, sql);
350
-
351
- // 파라미터 암호화 처리
352
- if (conf.getProperty('profile_mongodb_param_enabled', false) === true &&
353
- parseResult.values.length > 0) {
354
- sql_step.setTrue(1);
355
- var crc = {value: 0};
356
- const valuesString = parseResult.values.map(val => {
357
- if (typeof val === 'string') {
358
- return `'${val}'`;
252
+ const connectionResource = new AsyncResource('mongoose-connect');
253
+
254
+ return connectionResource.runInAsyncScope(() => {
255
+ ctx.start_time = Date.now();
256
+ ctx.footprint('Mongoose Connecting Start');
257
+ ctx.db_opening = true;
258
+
259
+ // 콜백 래핑
260
+ for (var i = args.length - 1; i >= 0; i--) {
261
+ if (typeof args[i] === 'function') {
262
+ var originalCallback = args[i];
263
+ args[i] = connectionResource.bind(function() {
264
+ var callbackArgs = Array.prototype.slice.call(arguments);
265
+ var err = callbackArgs[0];
266
+
267
+ if (ctx) {
268
+ ctx.elapsed = Date.now() - ctx.start_time;
269
+ ctx.db_opening = false;
270
+
271
+ if (err) {
272
+ handleMongooseError(ctx, err);
273
+ ctx.footprint('Mongoose Connecting Error');
274
+ } else {
275
+ ctx.footprint('Mongoose Connecting Done');
276
+ }
277
+ }
278
+
279
+ return originalCallback.apply(this, callbackArgs);
280
+ });
281
+ break;
359
282
  }
360
- return val;
361
- }).toString();
362
- sql_step.p2 = toParamBytes(valuesString, crc);
363
- sql_step.pcrc = crc.value;
364
- }
365
-
366
- ctx.profile.push(sql_step);
367
-
368
- } catch (e) {
369
- Logger.printError("WHATAP-611", "Mongodb insert query error", e, false);
370
- }
371
-
372
- return result;
373
- };
374
- };
375
-
376
- MongooseObserver.prototype.createDBCStep = function(ctx) {
377
- if (dbc && conn_dbc_hash) {
378
- DataTextAgent.DBC.add(conn_dbc_hash, dbc);
379
- DataTextAgent.METHOD.add(conn_dbc_hash, dbc);
380
- DataTextAgent.ERROR.add(conn_dbc_hash, dbc);
381
- }
382
-
383
- var dbc_step = new DBCStep();
384
- dbc_step.hash = conn_dbc_hash;
385
- dbc_step.start_time = ctx.getElapsedTime();
283
+ }
386
284
 
387
- return dbc_step;
388
- };
285
+ var result = original.apply(this, args);
389
286
 
390
- // 비동기 결과 처리
391
- MongooseObserver.prototype.handleAsyncResult = function(result, ctx, methodName) {
392
- ctx.footprint('Mongodb Command Start: ' + methodName);
287
+ // Promise 기반 처리
288
+ if (result && typeof result.then === 'function') {
289
+ return result.then(connectionResource.bind(function(connection) {
290
+ if (ctx) {
291
+ ctx.elapsed = Date.now() - ctx.start_time;
292
+ ctx.db_opening = false;
293
+ ctx.footprint('Mongoose Connecting Done');
294
+ }
393
295
 
394
- // Promise인 경우 처리 (대부분의 Mongoose 메서드가 Promise 반환)
395
- if (result && typeof result.then === 'function') {
396
- // 이미 실행은 완료되었으므로 특별한 처리 불필요
397
- return result;
398
- }
296
+ // 연결 완료 Model 래핑
297
+ if (connection && connection.Model) {
298
+ wrapModelMethods(connection.Model);
299
+ }
399
300
 
400
- return result;
401
- };
301
+ return connection;
302
+ })).catch(connectionResource.bind(function(err) {
303
+ if (ctx) {
304
+ ctx.elapsed = Date.now() - ctx.start_time;
305
+ ctx.db_opening = false;
306
+ ctx.footprint('Mongoose Connecting Error');
307
+ handleMongooseError(ctx, err);
308
+ }
309
+ throw err;
310
+ }));
311
+ }
402
312
 
403
- // 단일 필터 파싱
404
- MongooseObserver.prototype.parseSingleFilter = function(methodName, filterObj) {
405
- const result = {
406
- whereFields: [],
407
- values: []
313
+ return result;
314
+ });
315
+ };
408
316
  };
409
-
410
- if (filterObj && typeof filterObj === 'object') {
411
- this.extractFieldsAndValues(filterObj, result.whereFields, result.values);
412
- }
413
-
414
- return result;
415
317
  };
416
318
 
417
- // 이중 필터 파싱 - 수정된 버전
418
- MongooseObserver.prototype.parseDoubleFilter = function(methodName, filterObj, updateObj) {
419
- const result = {
420
- whereFields: [],
421
- updateFields: [],
422
- values: []
423
- };
319
+ // Model 메서드 래핑 함수
320
+ function wrapModelMethods(ModelConstructor) {
321
+ if (ModelConstructor && ModelConstructor.prototype && !ModelConstructor.__whatap_wrapped__) {
322
+ Object.keys(MONGOOSE_COMMANDS).forEach(function(methodName) {
323
+ if (ModelConstructor.prototype[methodName] &&
324
+ !ModelConstructor.prototype[methodName].__whatap_wrapped__) {
424
325
 
425
- // where 조건 파싱
426
- if (filterObj && typeof filterObj === 'object') {
427
- this.extractFieldsAndValues(filterObj, result.whereFields, result.values);
326
+ shimmer.wrap(ModelConstructor.prototype, methodName,
327
+ createModelMethodWrapper(methodName));
328
+ ModelConstructor.prototype[methodName].__whatap_wrapped__ = true;
329
+ }
330
+ });
331
+ ModelConstructor.__whatap_wrapped__ = true;
332
+ Logger.print('WHATAP-MONGOOSE-MODEL', 'Model methods wrapped', false);
428
333
  }
334
+ }
429
335
 
430
- // update 조건 파싱 - 수정된 로직
431
- if (updateObj && typeof updateObj === 'object') {
432
- this.extractUpdateFieldsOnly(updateObj, result.updateFields, result.values);
336
+ MongooseObserver.prototype.inject = function(mod, moduleName) {
337
+ if (mod.__whatap_observe__) {
338
+ return;
433
339
  }
340
+ mod.__whatap_observe__ = true;
341
+ Logger.initPrint("MongooseObserver");
434
342
 
435
- return result;
436
- };
437
-
438
- // Aggregate pipeline 파싱
439
- MongooseObserver.prototype.parseAggregatePipeline = function(pipeline) {
440
- const result = {
441
- matchFields: [],
442
- groupFields: [],
443
- projectFields: [],
444
- values: []
445
- };
446
-
447
- if (Array.isArray(pipeline)) {
448
- pipeline.forEach(stage => {
449
- if (stage.$match) {
450
- this.extractFieldsAndValues(stage.$match, result.matchFields, result.values);
451
- }
452
- if (stage.$group) {
453
- this.extractGroupFields(stage.$group, result.groupFields);
454
- }
455
- if (stage.$project) {
456
- this.extractProjectFields(stage.$project, result.projectFields);
457
- }
458
- });
343
+ if (conf.sql_enabled === false) {
344
+ return;
459
345
  }
460
346
 
461
- return result;
462
- };
463
-
464
- // Insert documents 파싱
465
- MongooseObserver.prototype.parseInsertDocuments = function(methodName, docs) {
466
- const result = {
467
- insertFields: [],
468
- values: []
469
- };
347
+ var self = this;
470
348
 
471
- if (methodName === 'insertMany' && Array.isArray(docs)) {
472
- // 번째 문서의 필드들을 기준으로 함
473
- if (docs.length > 0 && typeof docs[0] === 'object') {
474
- this.extractFieldsAndValues(docs[0], result.insertFields, result.values);
475
- }
476
- } else if (docs && typeof docs === 'object') {
477
- this.extractFieldsAndValues(docs, result.insertFields, result.values);
349
+ // mongoose.connect 래핑
350
+ if (mod.connect && !mod.connect.__whatap_wrapped__) {
351
+ shimmer.wrap(mod, 'connect', createConnectWrapper());
352
+ mod.connect.__whatap_wrapped__ = true;
478
353
  }
479
354
 
480
- return result;
481
- };
482
-
483
- // 필드와 값 추출 (기본)
484
- MongooseObserver.prototype.extractFieldsAndValues = function(obj, fieldsArray, valuesArray) {
485
- if (!obj || typeof obj !== 'object') return;
486
-
487
- Object.keys(obj).forEach(key => {
488
- if (key.startsWith('$')) {
489
- // MongoDB 연산자는 중첩 처리
490
- if (typeof obj[key] === 'object') {
491
- this.extractFieldsAndValues(obj[key], fieldsArray, valuesArray);
492
- } else {
493
- valuesArray.push(obj[key]);
494
- }
495
- } else {
496
- fieldsArray.push(key);
497
- if (obj[key] !== null && obj[key] !== undefined) {
498
- if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
499
- // 중첩 객체의 값들도 추출
500
- this.extractNestedValues(obj[key], valuesArray);
501
- } else {
502
- valuesArray.push(obj[key]);
503
- }
504
- }
505
- }
506
- });
507
- };
355
+ // 기본 Model이 이미 존재하는 경우 래핑
356
+ if (mod.Model) {
357
+ wrapModelMethods(mod.Model);
358
+ }
508
359
 
509
- // Update 필드만 추출 (수정된 버전) - where 필드 중복 방지
510
- MongooseObserver.prototype.extractUpdateFieldsOnly = function(updateObj, fieldsArray, valuesArray) {
511
- if (!updateObj || typeof updateObj !== 'object') return;
512
-
513
- Object.keys(updateObj).forEach(key => {
514
- if (key.startsWith('$')) {
515
- // $set: {name: 'new', email: 'new'} 처리
516
- if (key === '$set' || key === '$inc' || key === '$push' || key === '$pull' || key === '$unset') {
517
- if (typeof updateObj[key] === 'object') {
518
- Object.keys(updateObj[key]).forEach(field => {
519
- fieldsArray.push(field);
520
- const value = updateObj[key][field];
521
- if (value !== null && value !== undefined) {
522
- valuesArray.push(value);
523
- }
524
- });
525
- }
526
- }
527
- // 다른 연산자들도 필요시 추가
528
- } else {
529
- // 직접 업데이트: {name: "new"} 형태
530
- fieldsArray.push(key);
531
- if (updateObj[key] !== null && updateObj[key] !== undefined) {
532
- valuesArray.push(updateObj[key]);
360
+ // mongoose.model() 메서드 래핑 (새로운 모델 생성 시)
361
+ if (mod.model && !mod.model.__whatap_wrapped__) {
362
+ var originalModel = mod.model;
363
+ mod.model = function() {
364
+ var model = originalModel.apply(this, arguments);
365
+
366
+ // 새로 생성된 모델의 메서드들 래핑
367
+ if (model && !model.__whatap_wrapped__) {
368
+ Object.keys(MONGOOSE_COMMANDS).forEach(function(methodName) {
369
+ if (model[methodName] && !model[methodName].__whatap_wrapped__) {
370
+ shimmer.wrap(model, methodName, createModelMethodWrapper(methodName));
371
+ model[methodName].__whatap_wrapped__ = true;
372
+ }
373
+ });
374
+ model.__whatap_wrapped__ = true;
533
375
  }
534
- }
535
- });
536
- };
537
-
538
- // Group 필드 추출
539
- MongooseObserver.prototype.extractGroupFields = function(groupObj, fieldsArray) {
540
- if (!groupObj || typeof groupObj !== 'object') return;
541
-
542
- Object.keys(groupObj).forEach(key => {
543
- if (key !== '_id') {
544
- fieldsArray.push(key);
545
- }
546
- });
547
- };
548
-
549
- // Project 필드 추출
550
- MongooseObserver.prototype.extractProjectFields = function(projectObj, fieldsArray) {
551
- if (!projectObj || typeof projectObj !== 'object') return;
552
-
553
- Object.keys(projectObj).forEach(key => {
554
- fieldsArray.push(key);
555
- });
556
- };
557
376
 
558
- // 중첩 값 추출
559
- MongooseObserver.prototype.extractNestedValues = function(obj, valuesArray) {
560
- if (!obj || typeof obj !== 'object') return;
377
+ return model;
378
+ };
561
379
 
562
- Object.values(obj).forEach(value => {
563
- if (value !== null && value !== undefined) {
564
- if (typeof value === 'object' && !Array.isArray(value)) {
565
- this.extractNestedValues(value, valuesArray);
566
- } else {
567
- valuesArray.push(value);
568
- }
569
- }
570
- });
571
- };
380
+ // 기존 속성들 복사
381
+ Object.keys(originalModel).forEach(function(key) {
382
+ mod.model[key] = originalModel[key];
383
+ });
572
384
 
573
- var toParamBytes = function (p, crc) {
574
- if (p == null || p.length === 0) {
575
- return null;
576
- }
577
- try {
578
- return ParamSecurity.encrypt(Buffer.from(p, 'utf8'), crc);
579
- } catch (e) {
580
- return null;
385
+ mod.model.__whatap_wrapped__ = true;
581
386
  }
582
387
  };
583
388