whatap 0.5.9 → 0.5.11

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(',');
@@ -25,6 +25,12 @@ const DateUtil = require("../util/dateutil");
25
25
  const TraceSQL = require("../trace/trace-sql");
26
26
  const shimmer = require("../core/shimmer");
27
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
+
28
34
  var PrismaObserver = function(agent) {
29
35
  this.agent = agent;
30
36
  this.packages = ["@prisma/client"];
@@ -51,13 +57,31 @@ PrismaObserver.prototype.inject = function(mod, moduleName) {
51
57
  if (mod.PrismaClient) {
52
58
  // 직접 PrismaClient 생성자 후킹
53
59
  shimmer.wrap(mod, 'PrismaClient', function(originalConstructor) {
54
- return function() {
55
- // 원래 생성자 호출
56
- const instance = new originalConstructor(...arguments);
60
+ 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
+ });
57
76
 
58
- // 이 시점에서 instance가 생성된 Prisma 클라이언트 인스턴스
59
77
  self.patchPrismaInstance(instance);
60
78
 
79
+ if (!instance[prisma_read_func_name]) {
80
+ instance[prisma_read_func_name] = function () {
81
+ return this;
82
+ };
83
+ }
84
+
61
85
  return instance;
62
86
  };
63
87
  });
@@ -75,7 +99,7 @@ PrismaObserver.prototype.patchPrismaInstance = function(prismaInstance) {
75
99
  this.setupConnectionInfo(prismaInstance);
76
100
 
77
101
  // $connect 후킹
78
- this.hookConnect(prismaInstance);
102
+ // this.hookConnect(prismaInstance);
79
103
 
80
104
  // Raw 쿼리 메서드 후킹
81
105
  // this.hookRawQueryMethods(prismaInstance);
@@ -91,12 +115,12 @@ PrismaObserver.prototype.patchPrismaInstance = function(prismaInstance) {
91
115
  PrismaObserver.prototype.setupConnectionInfo = function(prismaInstance) {
92
116
  try {
93
117
  // 연결 정보 가져오기 시도
94
- const url = prismaInstance._engineConfig?.datasources?.db?.url ||
95
- prismaInstance._baseDmmf?.datamodel?.datasources?.[0]?.url?.value ||
96
- process.env.DATABASE_URL ||
118
+ const url = prismaInstance._engineConfig?.overrideDatasources?.db?.url ||
119
+ prismaInstance.env?.DATABASE_URL ||
120
+ process.env[prisma_database_url_name] ||
97
121
  'prisma:unknown';
98
122
 
99
- if (url && url !== "prisma:unknown") {
123
+ if (url && url !== "prisma:unknown" && !dbc_hash) {
100
124
  const dbUrl = new URL(url);
101
125
  const protocol = dbUrl.protocol.replace(':', '');
102
126
 
@@ -125,185 +149,185 @@ PrismaObserver.prototype.setupConnectionInfo = function(prismaInstance) {
125
149
  };
126
150
 
127
151
  // Connect 메서드 후킹
128
- PrismaObserver.prototype.hookConnect = function(prismaInstance) {
129
- const self = this;
130
-
131
- shimmer.wrap(prismaInstance, "$connect", function(original) {
132
- return async function() {
133
- const ctx = TraceContextManager.getCurrentContext();
134
- if (!ctx || ctx.db_opening) {
135
- return original.apply(this, arguments);
136
- }
137
-
138
- ctx.db_opening = true;
139
- ctx.footprint("Prisma Connecting Start");
140
-
141
- const dbc_step = new DBCStep();
142
- dbc_step.start_time = ctx.getElapsedTime();
143
-
144
- try {
145
- const result = await original.apply(this, arguments);
146
-
147
- dbc_step.hash = dbc_hash;
148
- dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
149
-
150
- ctx.db_opening = false;
151
- ctx.footprint("Prisma Connecting Done");
152
- ctx.profile.push(dbc_step);
153
-
154
- return result;
155
- } catch (err) {
156
- ctx.db_opening = false;
157
-
158
- dbc_step.hash = dbc_hash;
159
- dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
160
- dbc_step.error = StatError.addError("prisma-connection", err.message, ctx.service_hash);
161
-
162
- if (ctx.error.isZero()) {
163
- ctx.error = dbc_step.error;
164
- }
165
-
166
- ctx.profile.push(dbc_step);
167
- Logger.printError("WHATAP-302", "Connection Error", err);
168
- throw err;
169
- }
170
- };
171
- });
172
- };
152
+ // PrismaObserver.prototype.hookConnect = function(prismaInstance) {
153
+ // const self = this;
154
+ //
155
+ // shimmer.wrap(prismaInstance, "$connect", function(original) {
156
+ // return async function() {
157
+ // const ctx = TraceContextManager.getCurrentContext();
158
+ // if (!ctx || ctx.db_opening) {
159
+ // return original.apply(this, arguments);
160
+ // }
161
+ //
162
+ // ctx.db_opening = true;
163
+ // ctx.footprint("Prisma Connecting Start");
164
+ //
165
+ // const dbc_step = new DBCStep();
166
+ // dbc_step.start_time = ctx.getElapsedTime();
167
+ //
168
+ // try {
169
+ // const result = await original.apply(this, arguments);
170
+ //
171
+ // dbc_step.hash = dbc_hash;
172
+ // dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
173
+ //
174
+ // ctx.db_opening = false;
175
+ // ctx.footprint("Prisma Connecting Done");
176
+ // ctx.profile.push(dbc_step);
177
+ //
178
+ // return result;
179
+ // } catch (err) {
180
+ // ctx.db_opening = false;
181
+ //
182
+ // dbc_step.hash = dbc_hash;
183
+ // dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
184
+ // dbc_step.error = StatError.addError("prisma-connection", err.message, ctx.service_hash);
185
+ //
186
+ // if (ctx.error.isZero()) {
187
+ // ctx.error = dbc_step.error;
188
+ // }
189
+ //
190
+ // ctx.profile.push(dbc_step);
191
+ // Logger.printError("WHATAP-302", "Connection Error", err);
192
+ // throw err;
193
+ // }
194
+ // };
195
+ // });
196
+ // };
173
197
 
174
198
  // Raw 쿼리 메서드 후킹
175
- PrismaObserver.prototype.hookRawQueryMethods = function(prismaInstance) {
176
- const self = this;
177
- const queryMethods = ["$queryRaw", "$executeRaw", "$queryRawUnsafe", "$executeRawUnsafe"];
178
-
179
- queryMethods.forEach(method => {
180
- if (typeof prismaInstance[method] === 'function') {
181
- shimmer.wrap(prismaInstance, method, function(original) {
182
- return async function() {
183
- const ctx = TraceContextManager.getCurrentContext();
184
- if (!ctx) {
185
- return original.apply(this, arguments);
186
- }
187
-
188
- const sql_step = new SqlStepX();
189
- sql_step.start_time = ctx.getElapsedTime();
190
- ctx.profile.push(sql_step);
191
- ctx.footprint(`Prisma ${method} Start`);
192
-
193
- ctx.sql_count = (ctx.sql_count || 0) + 1;
194
-
195
- // SQL 쿼리 추출
196
- let sql = "";
197
- let psql = null;
198
-
199
- if (arguments.length > 0) {
200
- if (typeof arguments[0] === "object" && arguments[0].sql) {
201
- // Tagged template으로 전달된 경우
202
- sql = arguments[0].sql;
203
- } else if (typeof arguments[0] === "string") {
204
- // Raw string으로 전달된 경우
205
- sql = arguments[0];
206
- }
207
- }
208
-
209
- // SQL 파싱
210
- if (sql && sql.length > 0) {
211
- try {
212
- psql = escapeLiteral(sql);
213
- } catch (e) {
214
- Logger.printError("WHATAP-303", "escapeliteral error", e, false);
215
- }
216
- } else {
217
- sql = "";
218
- psql = escapeLiteral(sql);
219
- }
220
-
221
- if (psql != null) {
222
- sql_step.hash = psql.sql;
223
- }
224
- sql_step.dbc = dbc_hash;
225
-
226
- var els = new EscapeLiteralSQL(sql);
227
- els.process();
228
-
229
- ctx.active_sqlhash = sql_step.hash;
230
- ctx.active_dbc = sql_step.dbc;
231
-
232
- // 파라미터 정보 추출
233
- if (conf.profile_sql_param_enabled) {
234
- var params = Array.from(arguments).slice(1);
235
- sql_step.setTrue(1);
236
- var crc = { value: 0 };
237
- sql_step.p1 = toParamBytes(psql.param, crc);
238
-
239
- if (params.length > 0) {
240
- const result = params.map((param) => {
241
- if (typeof param === "string") {
242
- return `'${param}'`;
243
- }
244
- return param;
245
- }).toString();
246
- sql_step.p2 = toParamBytes(result, crc);
247
- }
248
-
249
- sql_step.pcrc = crc.value;
250
- }
251
-
252
- try {
253
- const result = await original.apply(this, arguments);
254
-
255
- // SELECT 쿼리의 결과셋 처리
256
- if (method === "$queryRaw" || method === "$queryRawUnsafe") {
257
- if (Array.isArray(result)) {
258
- var result_step = new ResultSetStep();
259
- result_step.start_time = ctx.getElapsedTime();
260
- result_step.elapsed = 0;
261
- result_step.fetch = result.length;
262
- result_step.sqlhash = psql.sql;
263
- result_step.dbc = dbc_hash;
264
- ctx.profile.push(result_step);
265
-
266
- ctx.rs_count = ctx.rs_count ? ctx.rs_count + result.length : result.length;
267
- ctx.rs_time = ctx.rs_time ? ctx.rs_time + sql_step.elapsed : sql_step.elapsed;
268
-
269
- MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
270
- StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
271
-
272
- TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
273
- }
274
- }
275
-
276
- // UPDATE/INSERT/DELETE 쿼리 결과 처리
277
- if ((method === "$executeRaw" || method === "$executeRawUnsafe") && typeof result === "number") {
278
- sql_step.updated = result;
279
- }
280
-
281
- self._finishQuery(ctx, sql_step);
282
- return result;
283
- } catch (err) {
284
- Logger.printError("WHATAP-304", `${method} error: ${err.message}`, err);
285
-
286
- self._handleError(ctx, sql_step, err);
287
-
288
- if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
289
- var traceDepth = conf.trace_sql_error_depth;
290
- var errorStack = err.stack.split("\n");
291
-
292
- if (errorStack.length > traceDepth) {
293
- errorStack = errorStack.slice(0, traceDepth + 1);
294
- }
295
-
296
- ctx.error_message = errorStack.join("\n");
297
- sql_step.error = ctx.error = StatError.addError("prisma-" + (err.code || "unknown"), err.message, ctx.service_hash, TextTypes.SQL, null);
298
- }
299
-
300
- throw err;
301
- }
302
- };
303
- });
304
- }
305
- });
306
- };
199
+ // PrismaObserver.prototype.hookRawQueryMethods = function(prismaInstance) {
200
+ // const self = this;
201
+ // const queryMethods = ["$queryRaw", "$executeRaw", "$queryRawUnsafe", "$executeRawUnsafe"];
202
+ //
203
+ // queryMethods.forEach(method => {
204
+ // if (typeof prismaInstance[method] === 'function') {
205
+ // shimmer.wrap(prismaInstance, method, function(original) {
206
+ // return async function() {
207
+ // const ctx = TraceContextManager.getCurrentContext();
208
+ // if (!ctx) {
209
+ // return original.apply(this, arguments);
210
+ // }
211
+ //
212
+ // const sql_step = new SqlStepX();
213
+ // sql_step.start_time = ctx.getElapsedTime();
214
+ // ctx.profile.push(sql_step);
215
+ // ctx.footprint(`Prisma ${method} Start`);
216
+ //
217
+ // ctx.sql_count = (ctx.sql_count || 0) + 1;
218
+ //
219
+ // // SQL 쿼리 추출
220
+ // let sql = "";
221
+ // let psql = null;
222
+ //
223
+ // if (arguments.length > 0) {
224
+ // if (typeof arguments[0] === "object" && arguments[0].sql) {
225
+ // // Tagged template으로 전달된 경우
226
+ // sql = arguments[0].sql;
227
+ // } else if (typeof arguments[0] === "string") {
228
+ // // Raw string으로 전달된 경우
229
+ // sql = arguments[0];
230
+ // }
231
+ // }
232
+ //
233
+ // // SQL 파싱
234
+ // if (sql && sql.length > 0) {
235
+ // try {
236
+ // psql = escapeLiteral(sql);
237
+ // } catch (e) {
238
+ // Logger.printError("WHATAP-303", "escapeliteral error", e, false);
239
+ // }
240
+ // } else {
241
+ // sql = "";
242
+ // psql = escapeLiteral(sql);
243
+ // }
244
+ //
245
+ // if (psql != null) {
246
+ // sql_step.hash = psql.sql;
247
+ // }
248
+ // sql_step.dbc = dbc_hash;
249
+ //
250
+ // var els = new EscapeLiteralSQL(sql);
251
+ // els.process();
252
+ //
253
+ // ctx.active_sqlhash = sql_step.hash;
254
+ // ctx.active_dbc = sql_step.dbc;
255
+ //
256
+ // // 파라미터 정보 추출
257
+ // if (conf.profile_sql_param_enabled) {
258
+ // var params = Array.from(arguments).slice(1);
259
+ // sql_step.setTrue(1);
260
+ // var crc = { value: 0 };
261
+ // sql_step.p1 = toParamBytes(psql.param, crc);
262
+ //
263
+ // if (params.length > 0) {
264
+ // const result = params.map((param) => {
265
+ // if (typeof param === "string") {
266
+ // return `'${param}'`;
267
+ // }
268
+ // return param;
269
+ // }).toString();
270
+ // sql_step.p2 = toParamBytes(result, crc);
271
+ // }
272
+ //
273
+ // sql_step.pcrc = crc.value;
274
+ // }
275
+ //
276
+ // try {
277
+ // const result = await original.apply(this, arguments);
278
+ //
279
+ // // SELECT 쿼리의 결과셋 처리
280
+ // if (method === "$queryRaw" || method === "$queryRawUnsafe") {
281
+ // if (Array.isArray(result)) {
282
+ // var result_step = new ResultSetStep();
283
+ // result_step.start_time = ctx.getElapsedTime();
284
+ // result_step.elapsed = 0;
285
+ // result_step.fetch = result.length;
286
+ // result_step.sqlhash = psql.sql;
287
+ // result_step.dbc = dbc_hash;
288
+ // ctx.profile.push(result_step);
289
+ //
290
+ // ctx.rs_count = ctx.rs_count ? ctx.rs_count + result.length : result.length;
291
+ // ctx.rs_time = ctx.rs_time ? ctx.rs_time + sql_step.elapsed : sql_step.elapsed;
292
+ //
293
+ // MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
294
+ // StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
295
+ //
296
+ // TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
297
+ // }
298
+ // }
299
+ //
300
+ // // UPDATE/INSERT/DELETE 쿼리 결과 처리
301
+ // if ((method === "$executeRaw" || method === "$executeRawUnsafe") && typeof result === "number") {
302
+ // sql_step.updated = result;
303
+ // }
304
+ //
305
+ // self._finishQuery(ctx, sql_step);
306
+ // return result;
307
+ // } catch (err) {
308
+ // Logger.printError("WHATAP-304", `${method} error: ${err.message}`, err);
309
+ //
310
+ // self._handleError(ctx, sql_step, err);
311
+ //
312
+ // if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
313
+ // var traceDepth = conf.trace_sql_error_depth;
314
+ // var errorStack = err.stack.split("\n");
315
+ //
316
+ // if (errorStack.length > traceDepth) {
317
+ // errorStack = errorStack.slice(0, traceDepth + 1);
318
+ // }
319
+ //
320
+ // ctx.error_message = errorStack.join("\n");
321
+ // sql_step.error = ctx.error = StatError.addError("prisma-" + (err.code || "unknown"), err.message, ctx.service_hash, TextTypes.SQL, null);
322
+ // }
323
+ //
324
+ // throw err;
325
+ // }
326
+ // };
327
+ // });
328
+ // }
329
+ // });
330
+ // };
307
331
 
308
332
  PrismaObserver.prototype.hookUseMiddleware = function(prismaInstance) {
309
333
  const self = this;
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.9",
5
- "releaseDate": "20250305",
4
+ "version": "0.5.11",
5
+ "releaseDate": "20250317",
6
6
  "description": "Monitoring and Profiling Service",
7
7
  "main": "index.js",
8
8
  "scripts": {},