whatap 0.5.8 → 0.5.10

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.
@@ -271,7 +271,10 @@ var ConfigDefault = {
271
271
 
272
272
  "profile_graphql_enabled": bool("profile_graphql_enabled", true),
273
273
  "profile_graphql_variable_enabled": bool("profile_graphql_variable_enabled", false),
274
- "ignore_graphql_operation": str("ignore_graphql_operation", '')
274
+ "ignore_graphql_operation": str("ignore_graphql_operation", ''),
275
+
276
+ "prisma_read_func_name": str("prisma_read_func_name", "read"),
277
+ "prisma_database_url_name": str("prisma_database_url_name", "DATABASE_URL"),
275
278
  };
276
279
 
277
280
  ConfigDefault._hook_method_ignore_prefix = ConfigDefault.hook_method_ignore_prefixes.split(',');
package/lib/core/agent.js CHANGED
@@ -30,8 +30,9 @@ var Interceptor = require('./interceptor').Interceptor,
30
30
  PromiseObserver = require('../observers/promise-observer').PromiseObserver,
31
31
  PgSqlObserver = require('../observers/pgsql-observer').PgSqlObserver,
32
32
  ScheduleObserver = require('../observers/schedule-observer').ScheduleObserver,
33
- GRpcObserver = require('../observers/grpc-observer').GRpcObserver,
34
- ApolloObserver = require('../observers/apollo-server-observer').ApolloServerObserver;
33
+ // GRpcObserver = require('../observers/grpc-observer').GRpcObserver,
34
+ ApolloObserver = require('../observers/apollo-server-observer').ApolloServerObserver,
35
+ PrismaObserver = require('../observers/prisma-observer').PrismaObserver;
35
36
 
36
37
 
37
38
  var Configuration = require('./../conf/configure'),
@@ -114,6 +115,12 @@ NodeAgent.prototype.init = function(cb) {
114
115
  self.findRoot();
115
116
 
116
117
  Logger.initializer.process();
118
+
119
+ // 최초 1회 실행 후 1시간마다 실행
120
+ const ONE_HOUR = 60 * 60 * 1000;
121
+ Logger.clearOldLog();
122
+ setInterval(Logger.clearOldLog, ONE_HOUR);
123
+
117
124
  Logger.print('WHATAP-001', 'Start initialize WhaTap Agent... Root[' + self._conf['app.root'] + ']', true);
118
125
 
119
126
  if(self._conf['app.root'] == null || self._conf['app.root'].length == 0) {
@@ -271,6 +278,7 @@ NodeAgent.prototype.loadObserves = function() {
271
278
  observes.push(ScheduleObserver);
272
279
  // observes.push(GRpcObserver);
273
280
  observes.push(ApolloObserver);
281
+ observes.push(PrismaObserver);
274
282
 
275
283
  var packageToObserve = {};
276
284
  observes.forEach(function(observeObj) {
package/lib/logger.js CHANGED
@@ -27,7 +27,7 @@
27
27
  var now = DateUtil.currentTime();
28
28
  if(now > this.last + DateUtil.MILLIS_PER_HOUR) {
29
29
  this.last = now;
30
- Logger.clearOldLog();
30
+ // Logger.clearOldLog();
31
31
  }
32
32
  if(this.lastFileRotation !== conf.log_rotation_enabled
33
33
  || this.lastDateUnit !== DateUtil.getDateUnit()
@@ -210,7 +210,6 @@
210
210
  log_prefix = WHATAP_CONF + "-";
211
211
 
212
212
  fs.readdir(dir, function (err, files) {
213
-
214
213
  for (var i = 0; i < files.length; i++) {
215
214
  var stat = fs.statSync(path.join(dir, files[i]));
216
215
  if (stat.isDirectory()) {
@@ -232,12 +231,12 @@
232
231
  var fileUnit = DateUtil.getDateUnit(d);
233
232
  try {
234
233
  if (nowUnit - fileUnit > conf.log_keep_days) {
234
+ let filePath = `${dir}/${files[i]}`;
235
235
  fs.rmSync(filePath);
236
236
  }
237
237
  } catch (e) {
238
238
  }
239
239
  }
240
-
241
240
  });
242
241
  },
243
242
  read : function( file, endpos, length , callback) {
@@ -11,8 +11,6 @@ const HashUtil = require('../util/hashutil');
11
11
  const DataTextAgent = require('../data/datatext-agent');
12
12
  const MessageStep = require('../step/message-step');
13
13
  const shimmer = require('../core/shimmer');
14
- const StatError = require("../stat/stat-error");
15
- const TextTypes = require("../lang/text-types");
16
14
 
17
15
  var profile_graphql_enabled = conf.getProperty('profile_graphql_enabled', true);
18
16
  var profile_graphql_variable_enabled = conf.getProperty('profile_graphql_variable_enabled', false);
@@ -0,0 +1,1057 @@
1
+ /**
2
+ * Copyright 2025 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
+ 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
+ const conf = require("../conf/configure");
16
+ const IntKeyMap = require("../util/intkey-map");
17
+ const EscapeLiteralSQL = require("../util/escape-literal-sql");
18
+ 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
+ const Logger = require("../logger");
23
+ const Buffer = require("buffer").Buffer;
24
+ const DateUtil = require("../util/dateutil");
25
+ const TraceSQL = require("../trace/trace-sql");
26
+ 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
+ })
33
+
34
+ var PrismaObserver = function(agent) {
35
+ this.agent = agent;
36
+ this.packages = ["@prisma/client"];
37
+ };
38
+
39
+ var dbc_hash = 0;
40
+ var dbc = "";
41
+
42
+ PrismaObserver.prototype.inject = function(mod, moduleName) {
43
+ if (mod.__whatap_observe__) {
44
+ return;
45
+ }
46
+
47
+ mod.__whatap_observe__ = true;
48
+ Logger.initPrint("PrismaObserver");
49
+
50
+ const self = this;
51
+
52
+ if (conf.sql_enabled === false) {
53
+ return;
54
+ }
55
+
56
+ // Prisma Client 초기화 메서드 후킹
57
+ if (mod.PrismaClient) {
58
+ // 직접 PrismaClient 생성자 후킹
59
+ shimmer.wrap(mod, 'PrismaClient', function(originalConstructor) {
60
+ return function() {
61
+ // 원래 생성자 호출
62
+ const instance = new originalConstructor(...arguments);
63
+
64
+ // 이 시점에서 instance가 생성된 Prisma 클라이언트 인스턴스
65
+ self.patchPrismaInstance(instance);
66
+
67
+ if(!instance[prisma_read_func_name]){
68
+ instance[prisma_read_func_name] = function() {
69
+ return this;
70
+ }
71
+ }
72
+
73
+ return instance;
74
+ };
75
+ });
76
+ }
77
+ };
78
+
79
+ // 각 Prisma 인스턴스에 패치 적용
80
+ PrismaObserver.prototype.patchPrismaInstance = function(prismaInstance) {
81
+ if (prismaInstance.__whatap_observe__) {
82
+ return;
83
+ }
84
+ prismaInstance.__whatap_observe__ = true;
85
+
86
+ // 모든 DB 연결 정보 추출 및 초기화
87
+ this.setupConnectionInfo(prismaInstance);
88
+
89
+ // $connect 후킹
90
+ // this.hookConnect(prismaInstance);
91
+
92
+ // Raw 쿼리 메서드 후킹
93
+ // this.hookRawQueryMethods(prismaInstance);
94
+
95
+ // $use 미들웨어 후킹 (모델 메서드 추적)
96
+ this.hookUseMiddleware(prismaInstance);
97
+
98
+ // 각 모델에 대한 직접 메서드 후킹 (더 안정적인 추적을 위해)
99
+ // this.hookModelMethods(prismaInstance);
100
+ };
101
+
102
+ // 연결 정보 설정
103
+ PrismaObserver.prototype.setupConnectionInfo = function(prismaInstance) {
104
+ try {
105
+ // 연결 정보 가져오기 시도
106
+ const url = prismaInstance._engineConfig?.overrideDatasources?.db?.url ||
107
+ prismaInstance.env?.DATABASE_URL ||
108
+ process.env[prisma_database_url_name] ||
109
+ 'prisma:unknown';
110
+
111
+ if (url && url !== "prisma:unknown" && !dbc_hash) {
112
+ const dbUrl = new URL(url);
113
+ const protocol = dbUrl.protocol.replace(':', '');
114
+
115
+ // 프로토콜에 따라 접두사 설정 (postgresql -> pgsql로 변환할 수도 있음)
116
+ const dbProtocol = protocol === 'pgsql' ? 'postgresql' : protocol;
117
+
118
+ // MySQL 관찰자와 동일한 형식으로 구성
119
+ dbc = `${dbProtocol}://`;
120
+ dbc += dbUrl.username || '';
121
+ dbc += "@";
122
+ dbc += dbUrl.hostname || '';
123
+ dbc += '/';
124
+ dbc += dbUrl.pathname.replace('/', '') || '';
125
+ dbc_hash = HashUtil.hashFromString(dbc);
126
+
127
+ DataTextAgent.DBC.add(dbc_hash, dbc);
128
+ DataTextAgent.METHOD.add(dbc_hash, dbc);
129
+ DataTextAgent.ERROR.add(dbc_hash, dbc);
130
+ }
131
+
132
+ } catch (e) {
133
+ Logger.printError("WHATAP-301", "Failed to extract connection info", e, false);
134
+ dbc = "prisma:unknown";
135
+ dbc_hash = HashUtil.hashFromString(dbc);
136
+ }
137
+ };
138
+
139
+ // Connect 메서드 후킹
140
+ // PrismaObserver.prototype.hookConnect = function(prismaInstance) {
141
+ // const self = this;
142
+ //
143
+ // shimmer.wrap(prismaInstance, "$connect", function(original) {
144
+ // return async function() {
145
+ // const ctx = TraceContextManager.getCurrentContext();
146
+ // if (!ctx || ctx.db_opening) {
147
+ // return original.apply(this, arguments);
148
+ // }
149
+ //
150
+ // ctx.db_opening = true;
151
+ // ctx.footprint("Prisma Connecting Start");
152
+ //
153
+ // const dbc_step = new DBCStep();
154
+ // dbc_step.start_time = ctx.getElapsedTime();
155
+ //
156
+ // try {
157
+ // const result = await original.apply(this, arguments);
158
+ //
159
+ // dbc_step.hash = dbc_hash;
160
+ // dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
161
+ //
162
+ // ctx.db_opening = false;
163
+ // ctx.footprint("Prisma Connecting Done");
164
+ // ctx.profile.push(dbc_step);
165
+ //
166
+ // return result;
167
+ // } catch (err) {
168
+ // ctx.db_opening = false;
169
+ //
170
+ // dbc_step.hash = dbc_hash;
171
+ // dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
172
+ // dbc_step.error = StatError.addError("prisma-connection", err.message, ctx.service_hash);
173
+ //
174
+ // if (ctx.error.isZero()) {
175
+ // ctx.error = dbc_step.error;
176
+ // }
177
+ //
178
+ // ctx.profile.push(dbc_step);
179
+ // Logger.printError("WHATAP-302", "Connection Error", err);
180
+ // throw err;
181
+ // }
182
+ // };
183
+ // });
184
+ // };
185
+
186
+ // Raw 쿼리 메서드 후킹
187
+ // PrismaObserver.prototype.hookRawQueryMethods = function(prismaInstance) {
188
+ // const self = this;
189
+ // const queryMethods = ["$queryRaw", "$executeRaw", "$queryRawUnsafe", "$executeRawUnsafe"];
190
+ //
191
+ // queryMethods.forEach(method => {
192
+ // if (typeof prismaInstance[method] === 'function') {
193
+ // shimmer.wrap(prismaInstance, method, function(original) {
194
+ // return async function() {
195
+ // const ctx = TraceContextManager.getCurrentContext();
196
+ // if (!ctx) {
197
+ // return original.apply(this, arguments);
198
+ // }
199
+ //
200
+ // const sql_step = new SqlStepX();
201
+ // sql_step.start_time = ctx.getElapsedTime();
202
+ // ctx.profile.push(sql_step);
203
+ // ctx.footprint(`Prisma ${method} Start`);
204
+ //
205
+ // ctx.sql_count = (ctx.sql_count || 0) + 1;
206
+ //
207
+ // // SQL 쿼리 추출
208
+ // let sql = "";
209
+ // let psql = null;
210
+ //
211
+ // if (arguments.length > 0) {
212
+ // if (typeof arguments[0] === "object" && arguments[0].sql) {
213
+ // // Tagged template으로 전달된 경우
214
+ // sql = arguments[0].sql;
215
+ // } else if (typeof arguments[0] === "string") {
216
+ // // Raw string으로 전달된 경우
217
+ // sql = arguments[0];
218
+ // }
219
+ // }
220
+ //
221
+ // // SQL 파싱
222
+ // if (sql && sql.length > 0) {
223
+ // try {
224
+ // psql = escapeLiteral(sql);
225
+ // } catch (e) {
226
+ // Logger.printError("WHATAP-303", "escapeliteral error", e, false);
227
+ // }
228
+ // } else {
229
+ // sql = "";
230
+ // psql = escapeLiteral(sql);
231
+ // }
232
+ //
233
+ // if (psql != null) {
234
+ // sql_step.hash = psql.sql;
235
+ // }
236
+ // sql_step.dbc = dbc_hash;
237
+ //
238
+ // var els = new EscapeLiteralSQL(sql);
239
+ // els.process();
240
+ //
241
+ // ctx.active_sqlhash = sql_step.hash;
242
+ // ctx.active_dbc = sql_step.dbc;
243
+ //
244
+ // // 파라미터 정보 추출
245
+ // if (conf.profile_sql_param_enabled) {
246
+ // var params = Array.from(arguments).slice(1);
247
+ // sql_step.setTrue(1);
248
+ // var crc = { value: 0 };
249
+ // sql_step.p1 = toParamBytes(psql.param, crc);
250
+ //
251
+ // if (params.length > 0) {
252
+ // const result = params.map((param) => {
253
+ // if (typeof param === "string") {
254
+ // return `'${param}'`;
255
+ // }
256
+ // return param;
257
+ // }).toString();
258
+ // sql_step.p2 = toParamBytes(result, crc);
259
+ // }
260
+ //
261
+ // sql_step.pcrc = crc.value;
262
+ // }
263
+ //
264
+ // try {
265
+ // const result = await original.apply(this, arguments);
266
+ //
267
+ // // SELECT 쿼리의 결과셋 처리
268
+ // if (method === "$queryRaw" || method === "$queryRawUnsafe") {
269
+ // if (Array.isArray(result)) {
270
+ // var result_step = new ResultSetStep();
271
+ // result_step.start_time = ctx.getElapsedTime();
272
+ // result_step.elapsed = 0;
273
+ // result_step.fetch = result.length;
274
+ // result_step.sqlhash = psql.sql;
275
+ // result_step.dbc = dbc_hash;
276
+ // ctx.profile.push(result_step);
277
+ //
278
+ // ctx.rs_count = ctx.rs_count ? ctx.rs_count + result.length : result.length;
279
+ // ctx.rs_time = ctx.rs_time ? ctx.rs_time + sql_step.elapsed : sql_step.elapsed;
280
+ //
281
+ // MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
282
+ // StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
283
+ //
284
+ // TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
285
+ // }
286
+ // }
287
+ //
288
+ // // UPDATE/INSERT/DELETE 쿼리 결과 처리
289
+ // if ((method === "$executeRaw" || method === "$executeRawUnsafe") && typeof result === "number") {
290
+ // sql_step.updated = result;
291
+ // }
292
+ //
293
+ // self._finishQuery(ctx, sql_step);
294
+ // return result;
295
+ // } catch (err) {
296
+ // Logger.printError("WHATAP-304", `${method} error: ${err.message}`, err);
297
+ //
298
+ // self._handleError(ctx, sql_step, err);
299
+ //
300
+ // if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
301
+ // var traceDepth = conf.trace_sql_error_depth;
302
+ // var errorStack = err.stack.split("\n");
303
+ //
304
+ // if (errorStack.length > traceDepth) {
305
+ // errorStack = errorStack.slice(0, traceDepth + 1);
306
+ // }
307
+ //
308
+ // ctx.error_message = errorStack.join("\n");
309
+ // sql_step.error = ctx.error = StatError.addError("prisma-" + (err.code || "unknown"), err.message, ctx.service_hash, TextTypes.SQL, null);
310
+ // }
311
+ //
312
+ // throw err;
313
+ // }
314
+ // };
315
+ // });
316
+ // }
317
+ // });
318
+ // };
319
+
320
+ PrismaObserver.prototype.hookUseMiddleware = function(prismaInstance) {
321
+ const self = this;
322
+
323
+ // 원본 $use 함수를 저장
324
+ const originalUse = prismaInstance.$use;
325
+
326
+ // 우리만의 미들웨어 추가
327
+ if (typeof originalUse === 'function') {
328
+ // 간단한 미들웨어 추가 (모든 쿼리를 추적)
329
+ prismaInstance.$use(async (params, next) => {
330
+ var result;
331
+ const ctx = TraceContextManager.getCurrentContext();
332
+ if (!ctx) {
333
+ return next(params);
334
+ }
335
+
336
+ var dbc_step = new DBCStep();
337
+ dbc_hash = HashUtil.hashFromString(dbc);
338
+ DataTextAgent.DBC.add(dbc_hash, dbc);
339
+ DataTextAgent.METHOD.add(dbc_hash, dbc);
340
+ DataTextAgent.ERROR.add(dbc_hash, dbc)
341
+
342
+ dbc_step.hash = dbc_hash;
343
+ dbc_step.start_time = ctx.getElapsedTime();
344
+ ctx.profile.push(dbc_step);
345
+
346
+ const sql_step = new SqlStepX();
347
+ sql_step.start_time = ctx.getElapsedTime();
348
+ ctx.profile.push(sql_step);
349
+
350
+ const modelName = params.model || 'unknown';
351
+ const action = params.action || 'unknown';
352
+
353
+ ctx.footprint(`Prisma ${modelName}.${action} Start`);
354
+ ctx.sql_count = (ctx.sql_count || 0) + 1;
355
+
356
+ // 사용된 모델과 액션으로 SQL 문자열 구성
357
+ const queryInfo = `Prisma ${action.toUpperCase()} ${modelName}`;
358
+ const queryHash = HashUtil.hashFromString(queryInfo);
359
+
360
+ DataTextAgent.SQL.add(queryHash, queryInfo);
361
+ sql_step.hash = queryHash;
362
+ sql_step.dbc = dbc_hash;
363
+
364
+ ctx.active_sqlhash = sql_step.hash;
365
+ ctx.active_dbc = sql_step.dbc;
366
+
367
+ // 쿼리 파라미터 정보 추출
368
+ if (conf.profile_sql_param_enabled) {
369
+ const paramsString = JSON.stringify(params.args || {});
370
+ sql_step.setTrue(1);
371
+ var crc = { value: 0 };
372
+ sql_step.p1 = toParamBytes(paramsString, crc);
373
+ sql_step.pcrc = crc.value;
374
+ }
375
+
376
+ try {
377
+ result = await next(params);
378
+
379
+ if (action === "queryRaw" || action === "executeRaw" || action === "queryRawUnsafe" || action === "executeRawUnsafe") {
380
+ // SQL 문자열 추출
381
+ let sqlString = "";
382
+ if (params.args && params.args.length > 0) {
383
+ // 첫 번째 인자가 문자열 배열인 경우 (tagged template)
384
+ if (params.args[0] && params.args[0].strings && Array.isArray(params.args[0].strings)) {
385
+ sqlString = params.args[0].strings.join('');
386
+ }
387
+ // 첫 번째 인자가 직접 문자열인 경우
388
+ else if (typeof params.args[0] === 'string') {
389
+ sqlString = params.args[0];
390
+ }
391
+ }
392
+
393
+ // SQL 문자열이 있으면 처리
394
+ if (sqlString && sqlString.length > 0) {
395
+ try {
396
+ var psql = escapeLiteral(sqlString);
397
+ if (psql != null) {
398
+ sql_step.hash = psql.sql;
399
+ // CRUD 타입 설정 (S: Select, U: Update 등)
400
+ // if (psql.type) {
401
+ // sql_step.crud = psql.type.charCodeAt(0);
402
+ // }
403
+ }
404
+
405
+ // 추가 SQL 정보 처리
406
+ var els = new EscapeLiteralSQL(sqlString);
407
+ els.process();
408
+
409
+ // SQL 파라미터 처리
410
+ if (conf.profile_sql_param_enabled) {
411
+ var params = params.args.slice(1);
412
+ sql_step.setTrue(1);
413
+ var crc = {value: 0};
414
+ sql_step.p1 = toParamBytes(psql.param, crc);
415
+
416
+ if (params && params.length > 0) {
417
+ const result = params.map((param) => {
418
+ if (typeof param === 'string') {
419
+ return `'${param}'`;
420
+ }
421
+ return param;
422
+ }).toString();
423
+ sql_step.p2 = toParamBytes(result, crc);
424
+ }
425
+ sql_step.pcrc = crc.value;
426
+ }
427
+ } catch (e) {
428
+ Logger.printError("WHATAP-305", "escapeLiteral error", e, false);
429
+ }
430
+ }
431
+
432
+ // Raw 쿼리 결과셋 처리
433
+ if ((action === "queryRaw" || action === "queryRawUnsafe") && result) {
434
+ let recordCount = 0;
435
+
436
+ if (Array.isArray(result)) {
437
+ recordCount = result.length;
438
+ } else if (result && typeof result === "object") {
439
+ recordCount = 1;
440
+ }
441
+
442
+ if (recordCount > 0) {
443
+ // 가장 최근에 처리된 SQL 해시 사용 (Raw 쿼리에서 업데이트된 경우)
444
+ const sqlHashToUse = psql ? psql.sql : queryHash;
445
+
446
+ var result_step = new ResultSetStep();
447
+ result_step.start_time = ctx.getElapsedTime();
448
+ result_step.elapsed = 0;
449
+ result_step.fetch = recordCount;
450
+ result_step.sqlhash = sqlHashToUse;
451
+ result_step.dbc = dbc_hash;
452
+ ctx.profile.push(result_step);
453
+
454
+ ctx.rs_count = ctx.rs_count ? ctx.rs_count + recordCount : recordCount;
455
+ ctx.rs_time = ctx.rs_time ? ctx.rs_time + sql_step.elapsed : sql_step.elapsed;
456
+
457
+ MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
458
+ StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
459
+
460
+ TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
461
+ }
462
+ }
463
+ }
464
+
465
+ // 결과셋 처리 (findMany, findFirst, findUnique 등)
466
+ if (["findMany", "findFirst", "findUnique"].includes(action)) {
467
+ let recordCount = 0;
468
+
469
+ if (Array.isArray(result)) {
470
+ recordCount = result.length;
471
+ } else if (result && typeof result === "object") {
472
+ recordCount = 1;
473
+ }
474
+
475
+ if (recordCount > 0) {
476
+ var result_step = new ResultSetStep();
477
+ result_step.start_time = ctx.getElapsedTime();
478
+ result_step.elapsed = 0;
479
+ result_step.fetch = recordCount;
480
+ result_step.sqlhash = queryHash;
481
+ result_step.dbc = dbc_hash;
482
+ ctx.profile.push(result_step);
483
+
484
+ ctx.rs_count = ctx.rs_count ? ctx.rs_count + recordCount : recordCount;
485
+ ctx.rs_time = ctx.rs_time ? ctx.rs_time + sql_step.elapsed : sql_step.elapsed;
486
+
487
+ MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
488
+ StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
489
+
490
+ TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
491
+ }
492
+ }
493
+
494
+ // 수정된 레코드 수 처리 (create, update, delete 등)
495
+ if (["create", "createMany", "update", "updateMany", "delete", "deleteMany"].includes(action)) {
496
+ if (result && result.count !== undefined) {
497
+ sql_step.updated = result.count;
498
+ } else if (result && typeof result === "object") {
499
+ sql_step.updated = 1;
500
+ }
501
+ }
502
+
503
+ // findUnique, create 등의 메서드에 대한 SQL 형식의 정보 구성
504
+ if (modelName !== 'unknown' && action !== 'queryRaw' && action !== 'executeRaw' &&
505
+ action !== 'queryRawUnsafe' && action !== 'executeRawUnsafe') {
506
+
507
+ try {
508
+ // 모델 메서드를 SQL 스타일로 변환
509
+ let sqlStyleQuery = "";
510
+
511
+ // 액션에 따라 SQL 명령 결정
512
+ if (action.startsWith('find')) {
513
+ sqlStyleQuery = "SELECT ";
514
+
515
+ // select 필드가 있는 경우 해당 필드 사용
516
+ if (params.args && params.args.select) {
517
+ const fields = Object.keys(params.args.select)
518
+ .filter(key => params.args.select[key] === true);
519
+
520
+ if (fields.length > 0) {
521
+ sqlStyleQuery += fields.join(", ");
522
+ } else {
523
+ sqlStyleQuery += "*";
524
+ }
525
+ } else {
526
+ sqlStyleQuery += "*";
527
+ }
528
+
529
+ sqlStyleQuery += ` FROM ${modelName}`;
530
+
531
+ // where 조건 추가
532
+ if (params.args && params.args.where) {
533
+ const conditions = [];
534
+ for (const [key, value] of Object.entries(params.args.where)) {
535
+ if (typeof value === 'object' && value !== null) {
536
+ // 복합 조건(contains, startsWith 등)
537
+ for (const [op, val] of Object.entries(value)) {
538
+ let sqlOp = "";
539
+
540
+ // Prisma 연산자를 SQL 연산자로 변환
541
+ switch (op) {
542
+ case 'equals': sqlOp = '='; break;
543
+ case 'not': sqlOp = '!='; break;
544
+ case 'in': sqlOp = 'IN'; break;
545
+ case 'notIn': sqlOp = 'NOT IN'; break;
546
+ case 'lt': sqlOp = '<'; break;
547
+ case 'lte': sqlOp = '<='; break;
548
+ case 'gt': sqlOp = '>'; break;
549
+ case 'gte': sqlOp = '>='; break;
550
+ case 'contains': sqlOp = 'LIKE'; break;
551
+ case 'startsWith': sqlOp = 'LIKE'; break;
552
+ case 'endsWith': sqlOp = 'LIKE'; break;
553
+ default: sqlOp = op;
554
+ }
555
+
556
+ let sqlVal = val;
557
+ if (op === 'contains') {
558
+ sqlVal = `'%${val}%'`;
559
+ } else if (op === 'startsWith') {
560
+ sqlVal = `'${val}%'`;
561
+ } else if (op === 'endsWith') {
562
+ sqlVal = `'%${val}'`;
563
+ } else if (typeof val === 'string') {
564
+ sqlVal = `'${val}'`;
565
+ } else if (Array.isArray(val)) {
566
+ sqlVal = `(${val.map(v => typeof v === 'string' ? `'${v}'` : v).join(', ')})`;
567
+ }
568
+
569
+ conditions.push(`${key} ${sqlOp} ${sqlVal}`);
570
+ }
571
+ } else {
572
+ // 단순 조건
573
+ let formattedValue = typeof value === 'string' ? `'${value}'` : value;
574
+ conditions.push(`${key} = ${formattedValue}`);
575
+ }
576
+ }
577
+
578
+ if (conditions.length > 0) {
579
+ sqlStyleQuery += ` WHERE ${conditions.join(' AND ')}`;
580
+ }
581
+ }
582
+
583
+ // 정렬 조건 추가
584
+ if (params.args && params.args.orderBy) {
585
+ const orderClauses = [];
586
+
587
+ if (Array.isArray(params.args.orderBy)) {
588
+ for (const item of params.args.orderBy) {
589
+ for (const [field, dir] of Object.entries(item)) {
590
+ orderClauses.push(`${field} ${dir}`);
591
+ }
592
+ }
593
+ } else {
594
+ for (const [field, dir] of Object.entries(params.args.orderBy)) {
595
+ orderClauses.push(`${field} ${dir}`);
596
+ }
597
+ }
598
+
599
+ if (orderClauses.length > 0) {
600
+ sqlStyleQuery += ` ORDER BY ${orderClauses.join(', ')}`;
601
+ }
602
+ }
603
+
604
+ // 페이징 정보 추가
605
+ if (params.args) {
606
+ if (params.args.skip !== undefined) {
607
+ sqlStyleQuery += ` OFFSET ${params.args.skip}`;
608
+ }
609
+
610
+ if (params.args.take !== undefined) {
611
+ sqlStyleQuery += ` LIMIT ${params.args.take}`;
612
+ }
613
+ }
614
+
615
+ } else if (action.startsWith('create')) {
616
+ sqlStyleQuery = `INSERT INTO ${modelName}`;
617
+
618
+ if (params.args && params.args.data) {
619
+ const columns = [];
620
+ const values = [];
621
+
622
+ for (const [key, val] of Object.entries(params.args.data)) {
623
+ columns.push(key);
624
+ if (typeof val === 'string') {
625
+ values.push(`'${val}'`);
626
+ } else if (val === null) {
627
+ values.push('NULL');
628
+ } else if (typeof val === 'object') {
629
+ values.push(`'${JSON.stringify(val)}'`);
630
+ } else {
631
+ values.push(val);
632
+ }
633
+ }
634
+
635
+ sqlStyleQuery += ` (${columns.join(', ')}) VALUES (${values.join(', ')})`;
636
+ }
637
+
638
+ } else if (action.startsWith('update')) {
639
+ sqlStyleQuery = `UPDATE ${modelName}`;
640
+
641
+ if (params.args && params.args.data) {
642
+ const setExpressions = [];
643
+
644
+ for (const [key, val] of Object.entries(params.args.data)) {
645
+ let formattedValue;
646
+ if (typeof val === 'string') {
647
+ formattedValue = `'${val}'`;
648
+ } else if (val === null) {
649
+ formattedValue = 'NULL';
650
+ } else if (typeof val === 'object') {
651
+ formattedValue = `'${JSON.stringify(val)}'`;
652
+ } else {
653
+ formattedValue = val;
654
+ }
655
+
656
+ setExpressions.push(`${key} = ${formattedValue}`);
657
+ }
658
+
659
+ if (setExpressions.length > 0) {
660
+ sqlStyleQuery += ` SET ${setExpressions.join(', ')}`;
661
+ }
662
+ }
663
+
664
+ // where 조건 추가
665
+ if (params.args && params.args.where) {
666
+ const conditions = [];
667
+ for (const [key, value] of Object.entries(params.args.where)) {
668
+ if (typeof value === 'object' && value !== null) {
669
+ for (const [op, val] of Object.entries(value)) {
670
+ let sqlOp = op === 'equals' ? '=' : op;
671
+ let sqlVal = typeof val === 'string' ? `'${val}'` : val;
672
+ conditions.push(`${key} ${sqlOp} ${sqlVal}`);
673
+ }
674
+ } else {
675
+ let formattedValue = typeof value === 'string' ? `'${value}'` : value;
676
+ conditions.push(`${key} = ${formattedValue}`);
677
+ }
678
+ }
679
+
680
+ if (conditions.length > 0) {
681
+ sqlStyleQuery += ` WHERE ${conditions.join(' AND ')}`;
682
+ }
683
+ }
684
+
685
+ } else if (action.startsWith('delete')) {
686
+ sqlStyleQuery = `DELETE FROM ${modelName}`;
687
+
688
+ // where 조건 추가
689
+ if (params.args && params.args.where) {
690
+ const conditions = [];
691
+ for (const [key, value] of Object.entries(params.args.where)) {
692
+ if (typeof value === 'object' && value !== null) {
693
+ for (const [op, val] of Object.entries(value)) {
694
+ let sqlOp = op === 'equals' ? '=' : op;
695
+ let sqlVal = typeof val === 'string' ? `'${val}'` : val;
696
+ conditions.push(`${key} ${sqlOp} ${sqlVal}`);
697
+ }
698
+ } else {
699
+ let formattedValue = typeof value === 'string' ? `'${value}'` : value;
700
+ conditions.push(`${key} = ${formattedValue}`);
701
+ }
702
+ }
703
+
704
+ if (conditions.length > 0) {
705
+ sqlStyleQuery += ` WHERE ${conditions.join(' AND ')}`;
706
+ }
707
+ }
708
+ } else {
709
+ // 기타 액션은 기본 형식으로
710
+ sqlStyleQuery = `${action.toUpperCase()} ${modelName}`;
711
+
712
+ if (params.args) {
713
+ sqlStyleQuery += ` ${JSON.stringify(params.args)}`;
714
+ }
715
+ }
716
+
717
+ // 생성된 SQL 쿼리에 escapeLiteral 적용하여 처리
718
+ try {
719
+ var psql = escapeLiteral(sqlStyleQuery);
720
+ if (psql != null) {
721
+ sql_step.hash = psql.sql;
722
+ // CRUD 타입 설정
723
+ if (psql.type) {
724
+ sql_step.crud = psql.type.charCodeAt(0);
725
+ }
726
+ }
727
+
728
+ // 추가 SQL 정보 처리
729
+ var els = new EscapeLiteralSQL(sqlStyleQuery);
730
+ els.process();
731
+
732
+ // SQL 파라미터 처리
733
+ if (conf.profile_sql_param_enabled) {
734
+ sql_step.setTrue(1);
735
+ var crc = {value: 0};
736
+ sql_step.p1 = toParamBytes(psql.param, crc);
737
+
738
+ // 원래 인자를 파라미터로 추가
739
+ const paramsString = JSON.stringify(params.args || {});
740
+ sql_step.p2 = toParamBytes(paramsString, crc);
741
+ sql_step.pcrc = crc.value;
742
+ }
743
+ } catch (e) {
744
+ Logger.printError("WHATAP-306", "escapeLiteral error for model method", e, false);
745
+ }
746
+ } catch (e) {
747
+ Logger.printError("WHATAP-307", "Error creating SQL-style query", e, false);
748
+ }
749
+ }
750
+
751
+ sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
752
+ ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
753
+
754
+ TraceSQL.isSlowSQL(ctx);
755
+
756
+ MeterSql.add(dbc_hash, sql_step.elapsed, false);
757
+ StatSql.addSqlTime(ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, false, 0);
758
+
759
+ ctx.footprint(`Prisma ${modelName}.${action} Done`);
760
+
761
+ return result;
762
+ } catch (err) {
763
+ Logger.printError("WHATAP-308", `Middleware error in ${modelName}.${action}: ${err.message}`, err, false);
764
+
765
+ sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
766
+ ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
767
+
768
+ TraceSQL.isSlowSQL(ctx);
769
+
770
+ MeterSql.add(dbc_hash, sql_step.elapsed, true);
771
+ StatSql.addSqlTime(ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, true, 0);
772
+
773
+ // 에러 처리
774
+ try {
775
+ sql_step.error = StatError.addError("prisma-" + (err.code || "unknown"), err.message, ctx.service_hash, TextTypes.SQL, sql_step.hash);
776
+
777
+ if (ctx.error.isZero()) {
778
+ ctx.error = sql_step.error;
779
+ }
780
+
781
+ if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
782
+ var traceDepth = conf.trace_sql_error_depth;
783
+ var errorStack = err.stack.split("\n");
784
+
785
+ if (errorStack.length > traceDepth) {
786
+ errorStack = errorStack.slice(0, traceDepth + 1);
787
+ }
788
+
789
+ ctx.error_message = errorStack.join("\n");
790
+ }
791
+ } catch (e) {
792
+ Logger.printError("WHATAP-309", "Error handling failed", e, false);
793
+ }
794
+
795
+ ctx.footprint(`Prisma ${modelName}.${action} Error`);
796
+ throw err;
797
+ }
798
+ });
799
+ }
800
+ };
801
+
802
+ // 모델 메서드 직접 후킹
803
+ // PrismaObserver.prototype.hookModelMethods = function(prismaInstance) {
804
+ // const self = this;
805
+ //
806
+ // // Prisma에서 모든 모델 가져오기
807
+ // let models = [];
808
+ // try {
809
+ // // DMMF를 통해 모델 이름 얻기
810
+ // if (prismaInstance._baseDmmf && prismaInstance._baseDmmf.modelMap) {
811
+ // models = Object.keys(prismaInstance._baseDmmf.modelMap);
812
+ // } else if (prismaInstance._dmmf && prismaInstance._dmmf.modelMap) {
813
+ // models = Object.keys(prismaInstance._dmmf.modelMap);
814
+ // }
815
+ // } catch (e) {
816
+ // Logger.printError("WHATAP-PRISMA", "Failed to get models", e, false);
817
+ // }
818
+ //
819
+ // // 모델 메서드 목록
820
+ // const methods = [
821
+ // "findUnique", "findFirst", "findMany",
822
+ // "create", "createMany",
823
+ // "update", "updateMany",
824
+ // "upsert",
825
+ // "delete", "deleteMany",
826
+ // "count", "aggregate", "groupBy"
827
+ // ];
828
+ //
829
+ // Logger.print("WHATAP-PRISMA", `Found models: ${models.join(', ')}`, false);
830
+ //
831
+ // // 각 모델에 대해 메서드 후킹
832
+ // models.forEach(model => {
833
+ // if (prismaInstance[model]) {
834
+ // methods.forEach(method => {
835
+ // if (typeof prismaInstance[model][method] === 'function') {
836
+ // shimmer.wrap(prismaInstance[model], method, function(original) {
837
+ // return async function() {
838
+ // const ctx = TraceContextManager.getCurrentContext();
839
+ // if (!ctx) {
840
+ // return original.apply(this, arguments);
841
+ // }
842
+ //
843
+ // Logger.print("WHATAP-PRISMA", `Direct model method called: ${model}.${method}`, false);
844
+ //
845
+ // const sql_step = new SqlStepX();
846
+ // sql_step.start_time = ctx.getElapsedTime();
847
+ // ctx.profile.push(sql_step);
848
+ // ctx.footprint(`Prisma ${model}.${method} Start (Direct)`);
849
+ //
850
+ // ctx.sql_count = (ctx.sql_count || 0) + 1;
851
+ //
852
+ // // 쿼리 정보
853
+ // const queryInfo = `${method.toUpperCase()} ${model}`;
854
+ // const queryHash = HashUtil.hashFromString(queryInfo);
855
+ //
856
+ // DataTextAgent.SQL.add(queryHash, queryInfo);
857
+ // sql_step.hash = queryHash;
858
+ // sql_step.dbc = dbc_hash;
859
+ //
860
+ // ctx.active_sqlhash = sql_step.hash;
861
+ // ctx.active_dbc = sql_step.dbc;
862
+ //
863
+ // // 쿼리 파라미터
864
+ // if (conf.profile_sql_param_enabled) {
865
+ // const argsString = JSON.stringify(arguments[0] || {});
866
+ // sql_step.setTrue(1);
867
+ // var crc = { value: 0 };
868
+ // sql_step.p1 = toParamBytes(argsString, crc);
869
+ // sql_step.pcrc = crc.value;
870
+ // }
871
+ //
872
+ // try {
873
+ // const result = await original.apply(this, arguments);
874
+ //
875
+ // // 결과셋 처리
876
+ // if (["findMany", "findFirst", "findUnique"].includes(method)) {
877
+ // let recordCount = 0;
878
+ //
879
+ // if (Array.isArray(result)) {
880
+ // recordCount = result.length;
881
+ // } else if (result && typeof result === "object") {
882
+ // recordCount = 1;
883
+ // }
884
+ //
885
+ // if (recordCount > 0) {
886
+ // var result_step = new ResultSetStep();
887
+ // result_step.start_time = ctx.getElapsedTime();
888
+ // result_step.elapsed = 0;
889
+ // result_step.fetch = recordCount;
890
+ // result_step.sqlhash = queryHash;
891
+ // result_step.dbc = dbc_hash;
892
+ // ctx.profile.push(result_step);
893
+ //
894
+ // ctx.rs_count = ctx.rs_count ? ctx.rs_count + recordCount : recordCount;
895
+ // ctx.rs_time = ctx.rs_time ? ctx.rs_time + sql_step.elapsed : sql_step.elapsed;
896
+ //
897
+ // MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
898
+ // StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
899
+ //
900
+ // TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
901
+ // }
902
+ // }
903
+ //
904
+ // // 수정된 레코드 처리
905
+ // if (["create", "createMany", "update", "updateMany", "delete", "deleteMany"].includes(method)) {
906
+ // if (result && result.count !== undefined) {
907
+ // sql_step.updated = result.count;
908
+ // } else if (result && typeof result === "object") {
909
+ // sql_step.updated = 1;
910
+ // }
911
+ // }
912
+ //
913
+ // sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
914
+ // ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
915
+ //
916
+ // TraceSQL.isSlowSQL(ctx);
917
+ //
918
+ // MeterSql.add(dbc_hash, sql_step.elapsed, false);
919
+ // StatSql.addSqlTime(ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, false, 0);
920
+ //
921
+ // ctx.footprint(`Prisma ${model}.${method} Done (Direct)`);
922
+ // Logger.print("WHATAP-PRISMA", `Direct model method completed: ${model}.${method}`, false);
923
+ //
924
+ // return result;
925
+ // } catch (err) {
926
+ // Logger.printError("WHATAP-PRISMA", `Direct model method error in ${model}.${method}: ${err.message}`, err);
927
+ //
928
+ // sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
929
+ // ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
930
+ //
931
+ // TraceSQL.isSlowSQL(ctx);
932
+ //
933
+ // MeterSql.add(dbc_hash, sql_step.elapsed, true);
934
+ // StatSql.addSqlTime(ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, true, 0);
935
+ //
936
+ // try {
937
+ // sql_step.error = StatError.addError("prisma-" + (err.code || "unknown"), err.message, ctx.service_hash, TextTypes.SQL, sql_step.hash);
938
+ //
939
+ // if (ctx.error.isZero()) {
940
+ // ctx.error = sql_step.error;
941
+ // }
942
+ //
943
+ // if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
944
+ // var traceDepth = conf.trace_sql_error_depth;
945
+ // var errorStack = err.stack.split("\n");
946
+ //
947
+ // if (errorStack.length > traceDepth) {
948
+ // errorStack = errorStack.slice(0, traceDepth + 1);
949
+ // }
950
+ //
951
+ // ctx.error_message = errorStack.join("\n");
952
+ // }
953
+ // } catch (e) {
954
+ // Logger.printError("WHATAP-PRISMA", "Error handling failed", e, false);
955
+ // }
956
+ //
957
+ // ctx.footprint(`Prisma ${model}.${method} Error (Direct)`);
958
+ // throw err;
959
+ // }
960
+ // };
961
+ // });
962
+ // }
963
+ // });
964
+ // }
965
+ // });
966
+ // };
967
+
968
+ PrismaObserver.prototype._finishQuery = function(ctx, sql_step) {
969
+ sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
970
+ ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
971
+
972
+ TraceSQL.isSlowSQL(ctx);
973
+
974
+ MeterSql.add(dbc_hash, sql_step.elapsed, false);
975
+ StatSql.addSqlTime(ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, false, 0);
976
+
977
+ ctx.footprint("Prisma Query Done");
978
+ };
979
+
980
+ PrismaObserver.prototype._handleError = function(ctx, sql_step, err) {
981
+ sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
982
+ ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
983
+
984
+ TraceSQL.isSlowSQL(ctx);
985
+
986
+ MeterSql.add(dbc_hash, sql_step.elapsed, true);
987
+ StatSql.addSqlTime(ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, true, 0);
988
+
989
+ try {
990
+ sql_step.error = StatError.addError("prisma-" + (err.code || "unknown"), err.message, ctx.service_hash, TextTypes.SQL, sql_step.hash);
991
+
992
+ if (ctx.error.isZero()) {
993
+ ctx.error = sql_step.error;
994
+ }
995
+ } catch (e) {
996
+ Logger.printError("WHATAP-310", "Error handling failed", e, false);
997
+ }
998
+
999
+ ctx.footprint("Prisma Query Error");
1000
+ };
1001
+
1002
+ var toParamBytes = function(p, crc) {
1003
+ if (p == null || p.length === 0) {
1004
+ return null;
1005
+ }
1006
+ try {
1007
+ return ParamSecurity.encrypt(Buffer.from(p, "utf8"), crc);
1008
+ } catch (e) {
1009
+ return null;
1010
+ }
1011
+ };
1012
+
1013
+ var checkedSql = new IntKeyMap(2000).setMax(2000);
1014
+ var nonLiteSql = new IntKeyMap(5000).setMax(5000);
1015
+ var date = DateUtil.yyyymmdd();
1016
+
1017
+ function escapeLiteral(sql) {
1018
+ if (sql == null) {
1019
+ sql = "";
1020
+ }
1021
+
1022
+ if (date !== DateUtil.yyyymmdd()) {
1023
+ checkedSql.clear();
1024
+ nonLiteSql.clear();
1025
+ date = DateUtil.yyyymmdd();
1026
+ Logger.print("WHATAP-SQL-CLEAR", "PrismaObserver CLEAR OK!!!!!!!!!!!!!!!!", false);
1027
+ }
1028
+
1029
+ var sqlHash = HashUtil.hashFromString(sql);
1030
+ var psql = nonLiteSql.get(sqlHash);
1031
+
1032
+ if (psql != null) {
1033
+ return psql;
1034
+ }
1035
+ psql = checkedSql.get(sqlHash);
1036
+
1037
+ if (psql != null) {
1038
+ return psql;
1039
+ }
1040
+
1041
+ var els = new EscapeLiteralSQL(sql);
1042
+ els.process();
1043
+
1044
+ var hash = HashUtil.hashFromString(els.getParsedSql());
1045
+ DataTextAgent.SQL.add(hash, els.getParsedSql());
1046
+
1047
+ if (hash === sqlHash) {
1048
+ psql = new ParsedSql(els.sqlType, hash, null);
1049
+ nonLiteSql.put(sqlHash, psql);
1050
+ } else {
1051
+ psql = new ParsedSql(els.sqlType, hash, els.getParameter());
1052
+ checkedSql.put(sqlHash, psql);
1053
+ }
1054
+ return psql;
1055
+ }
1056
+
1057
+ exports.PrismaObserver = PrismaObserver;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "whatap",
3
3
  "homepage": "http://www.whatap.io",
4
- "version": "0.5.8",
5
- "releaseDate": "20250218",
4
+ "version": "0.5.10",
5
+ "releaseDate": "20250310",
6
6
  "description": "Monitoring and Profiling Service",
7
7
  "main": "index.js",
8
8
  "scripts": {},