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
@@ -5,31 +5,12 @@
5
5
  */
6
6
 
7
7
  const TraceContextManager = require("../trace/trace-context-manager");
8
- const ParsedSql = require("../trace/parsed-sql");
9
- const SqlStepX = require("../step/sql-stepx");
10
- const DBCStep = require("../step/dbc-step");
11
- const ResultSetStep = require("../step/resultset-step");
12
- const DataTextAgent = require("../data/datatext-agent");
13
- const StatSql = require("../stat/stat-sql");
14
- const MeterSql = require("../counter/meter/meter-sql");
15
8
  const conf = require("../conf/configure");
16
- const IntKeyMap = require("../util/intkey-map");
17
- const EscapeLiteralSQL = require("../util/escape-literal-sql");
18
9
  const HashUtil = require("../util/hashutil");
19
- const StatError = require("../stat/stat-error");
20
- const TextTypes = require("../lang/text-types");
21
- const ParamSecurity = require("../util/paramsecurity");
22
10
  const Logger = require("../logger");
23
- const Buffer = require("buffer").Buffer;
24
- const DateUtil = require("../util/dateutil");
25
- const TraceSQL = require("../trace/trace-sql");
26
11
  const shimmer = require("../core/shimmer");
27
-
28
- var prisma_read_func_name = conf.getProperty('prisma_read_func_name', 'read');
29
- var prisma_database_url_name = conf.getProperty('prisma_database_url_name', 'DATABASE_URL')
30
- conf.on('prisma_database_url_name', function (newProps) {
31
- prisma_database_url_name = newProps;
32
- })
12
+ const AsyncSender = require('../udp/async_sender');
13
+ const PacketTypeEnum = require('../udp/packet_type_enum');
33
14
 
34
15
  var PrismaObserver = function(agent) {
35
16
  this.agent = agent;
@@ -55,40 +36,20 @@ PrismaObserver.prototype.inject = function(mod, moduleName) {
55
36
 
56
37
  // Prisma Client 초기화 메서드 후킹
57
38
  if (mod.PrismaClient) {
58
- // 직접 PrismaClient 생성자 후킹
59
39
  shimmer.wrap(mod, 'PrismaClient', function(originalConstructor) {
60
40
  return function(...args) {
61
- const originalInstance = new originalConstructor(...args);
62
- const instance = Object.create(originalInstance);
63
-
64
- const prismaServicePrototype = Object.getPrototypeOf(this);
65
- Object.getOwnPropertyNames(prismaServicePrototype).forEach((method) => {
66
- if (typeof prismaServicePrototype[method] === "function" && !instance[method]) {
67
- instance[method] = prismaServicePrototype[method].bind(instance);
68
- }
69
- });
70
-
71
- Object.getOwnPropertyNames(originalInstance).forEach((prop) => {
72
- if (!instance.hasOwnProperty(prop)) {
73
- instance[prop] = originalInstance[prop];
74
- }
75
- });
41
+ // 원래 생성자 호출
42
+ const instance = originalConstructor.apply(this, args);
76
43
 
44
+ // 패치 적용
77
45
  self.patchPrismaInstance(instance);
78
46
 
79
- if (!instance[prisma_read_func_name]) {
80
- instance[prisma_read_func_name] = function () {
81
- return this;
82
- };
83
- }
84
-
85
47
  return instance;
86
48
  };
87
49
  });
88
50
  }
89
51
  };
90
52
 
91
- // 각 Prisma 인스턴스에 패치 적용
92
53
  PrismaObserver.prototype.patchPrismaInstance = function(prismaInstance) {
93
54
  if (prismaInstance.__whatap_observe__) {
94
55
  return;
@@ -96,71 +57,56 @@ PrismaObserver.prototype.patchPrismaInstance = function(prismaInstance) {
96
57
  prismaInstance.__whatap_observe__ = true;
97
58
 
98
59
  this.setupConnectionInfo(prismaInstance);
99
-
100
60
  this.hookUseMiddleware(prismaInstance);
101
61
  };
102
62
 
103
- // 연결 정보 설정
104
63
  PrismaObserver.prototype.setupConnectionInfo = function(prismaInstance) {
105
64
  try {
106
65
  // 연결 정보 가져오기 시도
107
66
  const url = prismaInstance._engineConfig?.overrideDatasources?.db?.url ||
108
- prismaInstance.env?.DATABASE_URL ||
109
- process.env[prisma_database_url_name] ||
67
+ prismaInstance._engineConfig?.datasources?.db?.url ||
68
+ prismaInstance._baseDmmf?.datamodel?.datasources?.[0]?.url?.value ||
69
+ process.env.DATABASE_URL ||
110
70
  'prisma:unknown';
111
71
 
112
72
  if (url && url !== "prisma:unknown" && !dbc_hash) {
113
73
  const dbUrl = new URL(url);
114
74
  const protocol = dbUrl.protocol.replace(':', '');
115
75
 
116
- // 프로토콜에 따라 접두사 설정 (postgresql -> pgsql로 변환할 수도 있음)
117
- const dbProtocol = protocol === 'pgsql' ? 'postgresql' : protocol;
118
-
119
76
  // MySQL 관찰자와 동일한 형식으로 구성
120
- dbc = `${dbProtocol}://`;
77
+ dbc = `${protocol}://`;
121
78
  dbc += dbUrl.username || '';
122
79
  dbc += "@";
123
80
  dbc += dbUrl.hostname || '';
124
81
  dbc += '/';
125
82
  dbc += dbUrl.pathname.replace('/', '') || '';
126
83
  dbc_hash = HashUtil.hashFromString(dbc);
127
-
128
- DataTextAgent.DBC.add(dbc_hash, dbc);
129
- DataTextAgent.METHOD.add(dbc_hash, dbc);
130
- DataTextAgent.ERROR.add(dbc_hash, dbc);
131
84
  }
132
85
 
133
86
  } catch (e) {
134
- Logger.printError("WHATAP-301", "Failed to extract connection info", e, false);
87
+ Logger.printError("WHATAP-223", "Failed to extract connection info", e, false);
135
88
  dbc = "prisma:unknown";
136
89
  dbc_hash = HashUtil.hashFromString(dbc);
137
90
  }
138
91
  };
139
92
 
140
93
  PrismaObserver.prototype.hookUseMiddleware = function(prismaInstance) {
141
- const originalUse = prismaInstance.$use;
142
- if (typeof originalUse === 'function') {
94
+ const self = this;
95
+
96
+ if (typeof prismaInstance.$use === 'function') {
143
97
  prismaInstance.$use(async (params, next) => {
144
- var result;
145
98
  const ctx = TraceContextManager.getCurrentContext();
146
99
  if (!ctx) {
147
100
  return next(params);
148
101
  }
149
102
 
150
- var dbc_step = new DBCStep();
151
- dbc_hash = HashUtil.hashFromString(dbc);
152
- DataTextAgent.DBC.add(dbc_hash, dbc);
153
- DataTextAgent.METHOD.add(dbc_hash, dbc);
154
- DataTextAgent.ERROR.add(dbc_hash, dbc)
155
-
156
- dbc_step.hash = dbc_hash;
157
- dbc_step.start_time = ctx.getElapsedTime();
158
- ctx.profile.push(dbc_step);
103
+ // DB 연결 패킷 전송 (mysql2-observer와 동일한 패턴)
104
+ ctx.start_time = Date.now();
105
+ ctx.elapsed = 0;
106
+ AsyncSender.send_packet(PacketTypeEnum.TX_DB_CONN, ctx, [dbc]);
159
107
 
160
- const sql_step = new SqlStepX();
161
- sql_step.start_time = ctx.getElapsedTime();
162
- sql_step.elapsed = 0;
163
- ctx.profile.push(sql_step);
108
+ // SQL 시작 시간 기록
109
+ var sql_start_time = Date.now();
164
110
 
165
111
  const modelName = params.model || 'unknown';
166
112
  const action = params.action || 'unknown';
@@ -168,161 +114,21 @@ PrismaObserver.prototype.hookUseMiddleware = function(prismaInstance) {
168
114
  ctx.footprint(`Prisma ${modelName}.${action} Start`);
169
115
  ctx.sql_count = (ctx.sql_count || 0) + 1;
170
116
 
171
- // Prisma 쿼리 정보 직접 사용 (SQL 변환 없이)
117
+ // 쿼리 정보 생성
172
118
  const queryInfo = `Prisma ${modelName}.${action}`;
173
- const queryHash = HashUtil.hashFromString(queryInfo);
174
-
175
- DataTextAgent.SQL.add(queryHash, queryInfo);
176
- sql_step.hash = queryHash;
177
- sql_step.dbc = dbc_hash;
178
-
179
- ctx.active_sqlhash = sql_step.hash;
180
- ctx.active_dbc = sql_step.dbc;
181
-
182
- // 쿼리 파라미터 정보 추출
183
- if (conf.profile_sql_param_enabled) {
184
- const paramsString = JSON.stringify(params.args || {});
185
- sql_step.setTrue(1);
186
- var crc = { value: 0 };
187
- sql_step.p1 = toParamBytes(paramsString, crc);
188
- sql_step.pcrc = crc.value;
189
- }
190
119
 
191
- if (action === "queryRaw" || action === "executeRaw" || action === "queryRawUnsafe" || action === "executeRawUnsafe") {
192
- // SQL 문자열 추출
193
- let sqlString = "";
194
- if (params.args && params.args.length > 0) {
195
- if (params.args[0] && params.args[0].strings && Array.isArray(params.args[0].strings)) {
196
- sqlString = params.args[0].strings.join('').trim();
197
- } else if (params.args[0] && params.args[0] && Array.isArray(params.args[0])){
198
- sqlString = params.args[0].join('').trim();
199
- } else if (typeof params.args[0] === 'string') {
200
- sqlString = params.args[0];
201
- }
202
- }
203
-
204
- // SQL 문자열이 있으면 처리
205
- if (sqlString && sqlString.length > 0) {
206
- try {
207
- var psql = escapeLiteral(sqlString);
208
- if (psql != null) {
209
- sql_step.hash = psql.sql;
210
- }
211
-
212
- // 추가 SQL 정보 처리
213
- var els = new EscapeLiteralSQL(sqlString);
214
- els.process();
215
-
216
- // SQL 파라미터 처리
217
- if (conf.profile_sql_param_enabled) {
218
- var params = params.args.slice(1);
219
- sql_step.setTrue(1);
220
- var crc = { value: 0 };
221
- sql_step.p1 = toParamBytes(psql.param, crc);
222
-
223
- if (params && params.length > 0) {
224
- const result = params.map((param) => {
225
- if (typeof param === 'string') {
226
- return `'${param}'`;
227
- }
228
- return param;
229
- }).toString();
230
- sql_step.p2 = toParamBytes(result, crc);
231
- }
232
- sql_step.pcrc = crc.value;
233
- }
234
- } catch (e) {
235
- Logger.printError("WHATAP-305", "escapeLiteral error", e, false);
236
- }
237
- }
238
- }
120
+ ctx.active_sqlhash = HashUtil.hashFromString(queryInfo);
121
+ ctx.active_dbc = dbc_hash;
122
+
239
123
  try {
240
- result = await next(params);
241
-
242
- // Raw 쿼리 처리
243
- if (action === "queryRaw" || action === "executeRaw" || action === "queryRawUnsafe" || action === "executeRawUnsafe") {
244
- // Raw 쿼리 결과셋 처리
245
- if ((action === "queryRaw" || action === "queryRawUnsafe") && result) {
246
- let recordCount = 0;
247
-
248
- if (Array.isArray(result)) {
249
- recordCount = result.length;
250
- } else if (result && typeof result === "object") {
251
- recordCount = 1;
252
- }
253
-
254
- if(psql && psql.sql){
255
- const sqlHashToUse = psql.sql;
256
-
257
- var result_step = new ResultSetStep();
258
- result_step.start_time = ctx.getElapsedTime();
259
- result_step.elapsed = 0;
260
- result_step.fetch = recordCount;
261
- result_step.sqlhash = sqlHashToUse;
262
- result_step.dbc = dbc_hash;
263
- ctx.profile.push(result_step);
264
-
265
- ctx.rs_count = ctx.rs_count ? ctx.rs_count + recordCount : recordCount;
266
- ctx.rs_time = ctx.rs_time ? ctx.rs_time + sql_step.elapsed : sql_step.elapsed;
267
-
268
- MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
269
- StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
270
-
271
- TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
272
- }
273
- }
274
- }
275
-
276
- // 결과셋 처리 (findMany, findFirst, findUnique 등)
277
- if (["findMany", "findFirst", "findUnique"].includes(action)) {
278
- let recordCount = 0;
279
-
280
- if (Array.isArray(result)) {
281
- recordCount = result.length;
282
- } else if (result && typeof result === "object") {
283
- recordCount = 1;
284
- }
285
-
286
- var result_step = new ResultSetStep();
287
- result_step.start_time = ctx.getElapsedTime();
288
- result_step.elapsed = 0;
289
- result_step.fetch = recordCount;
290
- result_step.sqlhash = queryHash;
291
- result_step.dbc = dbc_hash;
292
- ctx.profile.push(result_step);
293
-
294
- ctx.rs_count = ctx.rs_count ? ctx.rs_count + recordCount : recordCount;
295
- ctx.rs_time = ctx.rs_time ? ctx.rs_time + sql_step.elapsed : sql_step.elapsed;
296
-
297
- MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
298
- StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
299
-
300
- TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
301
- }
302
-
303
- // 수정된 레코드 수 처리 (create, update, delete 등)
304
- if (["create", "createMany", "update", "updateMany", "delete", "deleteMany"].includes(action)) {
305
- if (result && result.count !== undefined) {
306
- sql_step.updated = result.count;
307
- } else if (result && typeof result === "object") {
308
- sql_step.updated = 1;
309
- }
310
- }
311
-
312
- // UPSERT 처리 추가
313
- if (action === "upsert") {
314
- sql_step.updated = 1; // upsert는 항상 1개의 레코드에 영향을 미침
315
- }
316
-
317
- this._finishQuery(ctx, sql_step);
124
+ const result = await next(params);
318
125
 
126
+ self._finishQuery(ctx, sql_start_time, result, queryInfo);
319
127
  ctx.footprint(`Prisma ${modelName}.${action} Done`);
320
128
 
321
129
  return result;
322
130
  } catch (err) {
323
- Logger.printError("WHATAP-308", `Middleware error in ${modelName}.${action}: ${err.message}`, err, false);
324
-
325
- this._handleError(ctx, sql_step, err);
131
+ self._handleError(ctx, sql_start_time, err, queryInfo);
326
132
  ctx.footprint(`Prisma ${modelName}.${action} Error`);
327
133
  throw err;
328
134
  }
@@ -330,106 +136,89 @@ PrismaObserver.prototype.hookUseMiddleware = function(prismaInstance) {
330
136
  }
331
137
  };
332
138
 
333
- PrismaObserver.prototype._finishQuery = function(ctx, sql_step) {
334
- sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
335
- ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
336
-
337
- TraceSQL.isSlowSQL(ctx);
338
-
339
- MeterSql.add(dbc_hash, sql_step.elapsed, false);
340
- StatSql.addSqlTime(ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, false, 0);
139
+ PrismaObserver.prototype._finishQuery = function(ctx, sql_start_time, result, queryInfo) {
140
+ var sql_elapsed = Date.now() - sql_start_time;
141
+ ctx.sql_time = (ctx.sql_time || 0) + sql_elapsed;
142
+
143
+ // 결과 개수 계산
144
+ let resultCount = 0;
145
+ if (result) {
146
+ if (Array.isArray(result)) {
147
+ resultCount = result.length;
148
+ } else if (result && typeof result === "object" && result.count !== undefined) {
149
+ resultCount = result.count;
150
+ } else if (result && typeof result === "object") {
151
+ resultCount = 1;
152
+ }
153
+ }
341
154
 
342
- ctx.footprint("Prisma Query Done");
155
+ // SQL 패킷 전송 (mysql2-observer와 동일한 패턴)
156
+ ctx.elapsed = sql_elapsed;
157
+ ctx.active_sqlhash = false;
158
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, ctx, [dbc, queryInfo, String(resultCount)]);
343
159
  };
344
160
 
345
- PrismaObserver.prototype._handleError = function(ctx, sql_step, err) {
346
- sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
347
- ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
348
-
349
- TraceSQL.isSlowSQL(ctx);
350
-
351
- MeterSql.add(dbc_hash, sql_step.elapsed, true);
352
- StatSql.addSqlTime(ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, true, 0);
161
+ PrismaObserver.prototype._handleError = function(ctx, sql_start_time, err, queryInfo) {
162
+ var sql_elapsed = Date.now() - sql_start_time;
163
+ ctx.sql_time = (ctx.sql_time || 0) + sql_elapsed;
353
164
 
354
165
  try {
355
166
  const errorClassName = err.name || err.constructor?.name || "UnknownError";
356
- ctx.error_class = errorClassName;
357
- ctx.error_message = err.message;
358
- sql_step.error = StatError.addError(errorClassName, err.message, ctx.service_hash, TextTypes.SQL, sql_step.hash);
167
+ const errorMessage = err.message || 'Prisma error';
168
+ let errorStack = '';
359
169
 
360
- if (ctx.error.isZero()) {
361
- ctx.error = sql_step.error;
362
- }
170
+ ctx.error_class = errorClassName;
171
+ ctx.error_message = errorMessage;
363
172
 
364
- if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
173
+ // 스택 트레이스 처리
174
+ if (conf.trace_sql_error_stack && conf.trace_sql_error_depth && err.stack) {
365
175
  var traceDepth = conf.trace_sql_error_depth;
366
- var errorStack = err.stack.split("\n");
176
+ var stackLines = err.stack.split("\n");
367
177
 
368
- if (errorStack.length > traceDepth) {
369
- errorStack = errorStack.slice(0, traceDepth + 1);
178
+ if (stackLines.length > traceDepth) {
179
+ stackLines = stackLines.slice(0, traceDepth + 1);
370
180
  }
371
- ctx.error_message = errorStack.join("\n");
181
+ errorStack = stackLines.join("\n");
182
+ ctx.error_message = errorStack;
372
183
  }
373
- } catch (e) {
374
- Logger.printError("WHATAP-309", "Error handling failed", e, false);
375
- }
376
184
 
377
- ctx.footprint("Prisma Query Error");
378
- };
379
-
380
- var toParamBytes = function(p, crc) {
381
- if (p == null || p.length === 0) {
382
- return null;
383
- }
384
- try {
385
- return ParamSecurity.encrypt(Buffer.from(p, "utf8"), crc);
386
- } catch (e) {
387
- return null;
388
- }
389
- };
390
-
391
- var checkedSql = new IntKeyMap(2000).setMax(2000);
392
- var nonLiteSql = new IntKeyMap(5000).setMax(5000);
393
- var date = DateUtil.yyyymmdd();
394
-
395
- function escapeLiteral(sql) {
396
- if (sql == null) {
397
- sql = "";
398
- }
399
-
400
- if (date !== DateUtil.yyyymmdd()) {
401
- checkedSql.clear();
402
- nonLiteSql.clear();
403
- date = DateUtil.yyyymmdd();
404
- Logger.print("WHATAP-SQL-CLEAR", "PrismaObserver CLEAR OK!!!!!!!!!!!!!!!!", false);
405
- }
406
-
407
- var sqlHash = HashUtil.hashFromString(sql);
408
- var psql = nonLiteSql.get(sqlHash);
409
-
410
- if (psql != null) {
411
- return psql;
412
- }
413
- psql = checkedSql.get(sqlHash);
185
+ // 에러 무시 조건 확인 (mysql2-observer와 동일한 로직)
186
+ var shouldAddError = false;
187
+ if (conf._is_trace_ignore_err_cls_contains === true && errorClassName &&
188
+ errorClassName.indexOf(conf.trace_ignore_err_cls_contains) < 0) {
189
+ shouldAddError = true;
190
+ } else if (conf._is_trace_ignore_err_msg_contains === true && errorMessage &&
191
+ errorMessage.indexOf(conf.trace_ignore_err_msg_contains) < 0) {
192
+ shouldAddError = true;
193
+ } else if (conf._is_trace_ignore_err_cls_contains === false &&
194
+ conf._is_trace_ignore_err_msg_contains === false) {
195
+ shouldAddError = true;
196
+ }
414
197
 
415
- if (psql != null) {
416
- return psql;
417
- }
198
+ if (shouldAddError) {
199
+ if(!ctx.error) ctx.error = 1;
200
+ ctx.status = 500;
201
+ ctx.errClass = errorClassName;
202
+ ctx.errMessage = errorMessage;
418
203
 
419
- var els = new EscapeLiteralSQL(sql);
420
- els.process();
204
+ var errors = [errorClassName];
205
+ if (errorMessage) {
206
+ errors.push(errorMessage);
207
+ }
208
+ if (errorStack || err.stack) {
209
+ errors.push(errorStack || err.stack);
210
+ }
211
+ AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
212
+ }
421
213
 
422
- var hash = HashUtil.hashFromString(els.getParsedSql());
423
- DataTextAgent.SQL.add(hash, els.getParsedSql());
214
+ // SQL 패킷 전송 (에러 발생시에도)
215
+ ctx.elapsed = sql_elapsed;
216
+ ctx.active_sqlhash = false;
217
+ AsyncSender.send_packet(PacketTypeEnum.TX_SQL, ctx, [dbc, queryInfo, "0"]);
424
218
 
425
- if (hash === sqlHash) {
426
- psql = new ParsedSql(els.sqlType, hash, null);
427
- nonLiteSql.put(sqlHash, psql);
428
- } else {
429
- psql = new ParsedSql(els.sqlType, hash, els.getParameter());
430
- checkedSql.put(sqlHash, psql);
219
+ } catch (e) {
220
+ Logger.printError("WHATAP-224", "Error handling failed", e, false);
431
221
  }
432
- return psql;
433
- }
222
+ };
434
223
 
435
224
  exports.PrismaObserver = PrismaObserver;
@@ -6,28 +6,26 @@
6
6
 
7
7
  const TraceContextManager = require('../trace/trace-context-manager');
8
8
  const conf = require('../conf/configure');
9
- const LogTracer = require('../logsink/log-tracer');
10
- const Logger = require('../logger');
9
+ const AsyncSender = require('../udp/async_sender');
10
+ const DataOuputX = require('../io/data-outputx');
11
+ const SecurityMaster = require('../net/security-master');
12
+ const LogSinkPack = require('../pack/log-sink-pack');
11
13
 
12
14
  let logsink_enabled = conf.getProperty('logsink_enabled', false);
13
- let logTracer = logsink_enabled ? new LogTracer() : null;
14
15
 
15
- conf.on('logsink_enabled', function (newProperty) {
16
+ conf.on('logsink_enabled', function(newProperty) {
16
17
  logsink_enabled = newProperty;
17
- logTracer = logsink_enabled ? new LogTracer() : null;
18
18
  });
19
19
 
20
20
  const ProcessObserver = function (agent) {
21
21
  this.agent = agent;
22
22
  this.packages = ['process'];
23
- this.exitHandlersRegistered = false;
24
23
  };
25
24
 
26
25
  ProcessObserver.prototype.inject = function (mod, moduleName) {
27
26
  this._hookNextTick(mod);
28
27
  this._hookStdOutWrite();
29
28
  this._hookStdErrWrite();
30
- this._hookProcessExit(mod);
31
29
  };
32
30
 
33
31
  ProcessObserver.prototype._hookNextTick = function (mod) {
@@ -41,6 +39,26 @@ ProcessObserver.prototype._hookNextTick = function (mod) {
41
39
  });
42
40
  };
43
41
 
42
+ ProcessObserver.prototype._sendLogPack = function (content, category) {
43
+ const ctx = TraceContextManager.getCurrentContext();
44
+ const tags = ctx && ctx.id ? {'@txid': ctx.id.toString()} : {};
45
+
46
+ const fields = {"filename": null};
47
+
48
+ const p = new LogSinkPack();
49
+ p.Category = category;
50
+ p.time = Date.now();
51
+ p.line = Date.now();
52
+ p.content = content;
53
+
54
+ p.pcode = SecurityMaster.PCODE;
55
+ const bout = new DataOuputX();
56
+ bout.writePack(p, null);
57
+ const packbytes = bout.toByteArray();
58
+
59
+ AsyncSender.send_relaypack(packbytes);
60
+ };
61
+
44
62
  ProcessObserver.prototype._hookStdOutWrite = function () {
45
63
  this.agent.aop.after(process.stdout, 'write', (obj, args) => {
46
64
  if (conf.getProperty('logsink_enabled', false) && args[0]) {
@@ -49,9 +67,12 @@ ProcessObserver.prototype._hookStdOutWrite = function () {
49
67
  const parsedContent = JSON.parse(content);
50
68
  content = parsedContent.message ? parsedContent.message : JSON.stringify(parsedContent);
51
69
  } catch (e) {
70
+ // JSON 파싱 실패 시 원본 content 사용
52
71
  }
53
- if (logTracer && content) {
54
- logTracer.addStdWrite(content, conf.logsink_category_stdout);
72
+
73
+ if (content && content.trim()) {
74
+ const category = conf.getProperty('logsink_category_stdout', 'AppStdOut');
75
+ this._sendLogPack(content, category);
55
76
  }
56
77
  }
57
78
  });
@@ -65,80 +86,15 @@ ProcessObserver.prototype._hookStdErrWrite = function () {
65
86
  const parsedContent = JSON.parse(content);
66
87
  content = parsedContent.message ? parsedContent.message : JSON.stringify(parsedContent);
67
88
  } catch (e) {
89
+ // JSON 파싱 실패 시 원본 content 사용
68
90
  }
69
- if (logTracer && content) {
70
- logTracer.addStdWrite(content, conf.logsink_category_stderr);
91
+
92
+ if (content && content.trim()) {
93
+ const category = conf.getProperty('logsink_category_stderr', 'AppStdErr');
94
+ this._sendLogPack(content, category);
71
95
  }
72
96
  }
73
97
  });
74
98
  };
75
99
 
76
- ProcessObserver.prototype._hookProcessExit = function (mod) {
77
- if (this.exitHandlersRegistered) {
78
- Logger.print("WHATAP-901", "[ProcessObserver] Exit handlers already registered, skipping...", false)
79
- return;
80
- }
81
- const self = this;
82
-
83
- // SIGTERM (graceful shutdown)
84
- process.on('SIGTERM', function () {
85
- const message = 'Process termination requested by system or process manager (SIGTERM) - Usually from service stop or container shutdown';
86
- Logger.print('WHATAP-902', message, false);
87
- setTimeout(() => process.exit(0), 100);
88
- });
89
-
90
- // SIGINT (Ctrl+C)
91
- process.on('SIGINT', function () {
92
- const message = 'Process interrupted by user (SIGINT) - Typically Ctrl+C or kill command';
93
- Logger.print('WHATAP-903', message, false);
94
- setTimeout(() => process.exit(0), 100);
95
- });
96
-
97
- // SIGHUP (hang up)
98
- process.on('SIGHUP', function () {
99
- const message = 'Process hangup signal received (SIGHUP) - Terminal disconnection or parent process terminated';
100
- Logger.print('WHATAP-904', message, false);
101
- setTimeout(() => process.exit(0), 100);
102
- });
103
-
104
- // SIGUSR1 (user-defined signal 1)
105
- process.on('SIGUSR1', function () {
106
- const message = 'User-defined signal 1 received (SIGUSR1) - Custom application signal';
107
- Logger.print('WHATAP-905', message, false);
108
- setTimeout(() => process.exit(0), 100);
109
- });
110
-
111
- // SIGUSR2 (user-defined signal 2)
112
- process.on('SIGUSR2', function () {
113
- const message = 'User-defined signal 2 received (SIGUSR2) - Custom application signal';
114
- Logger.print('WHATAP-906', message, false);
115
- setTimeout(() => process.exit(0), 100);
116
- });
117
-
118
- // 처리되지 않은 예외
119
- // process.on('uncaughtException', function (err) {
120
- // const message = `Unhandled exception caused process crash - Error: ${err.message}, File: ${err.stack ? err.stack.split('\n')[1] || 'unknown' : 'unknown'}`;
121
- // Logger.print('WHATAP-907', message, err, false);
122
- //
123
- // // 로그 남긴 후 프로세스 종료
124
- // setTimeout(() => {
125
- // process.exit(1);
126
- // }, 100);
127
- // });
128
- //
129
- // // 처리되지 않은 Promise rejection
130
- // process.on('unhandledRejection', function (reason, promise) {
131
- // const message = `Unhandled Promise rejection may cause process termination - Reason: ${reason instanceof Error ? reason.message : String(reason)}`;
132
- // const err = reason instanceof Error ? reason : new Error(String(reason));
133
- // Logger.print('WHATAP-908', message, err, false);
134
- //
135
- // // 로그 남긴 후 프로세스 종료
136
- // setTimeout(() => {
137
- // process.exit(1);
138
- // }, 100);
139
- // });
140
-
141
- this.exitHandlersRegistered = true;
142
- };
143
-
144
100
  module.exports.ProcessObserver = ProcessObserver;