import { env } from "~/env.server"; import { logger } from "~/services/logger.server"; export interface QueryPerformanceConfig { verySlowQueryThreshold?: number; // ms maxQueryLogLength: number; } export class QueryPerformanceMonitor { private config: QueryPerformanceConfig; constructor(config: Partial = {}) { this.config = { maxQueryLogLength: 1000, ...config, }; } onQuery( clientType: "writer" | "replica", log: { duration: number; query: string; params: string; target: string; timestamp: Date; } ) { if (this.config.verySlowQueryThreshold === undefined) { return; } const { duration, query, params, target, timestamp } = log; // Slow queries are an observability signal (DB load, missing indexes, // etc.), not an application error. Logged at warn so it lands in stdout // without flowing to Sentry — track via metrics/dashboards instead. if (duration > this.config.verySlowQueryThreshold) { const truncatedQuery = query.length > this.config.maxQueryLogLength ? query.substring(0, this.config.maxQueryLogLength) + "..." : query; logger.warn("Prisma: very slow database query", { clientType, durationMs: duration, query: truncatedQuery, target, timestamp, paramCount: this.countParams(query), hasParams: params !== "[]" && params !== "", }); } } private countParams(query: string): number { // Count the number of $1, $2, etc. parameters in the query const paramMatches = query.match(/\$\d+/g); return paramMatches ? paramMatches.length : 0; } } export const queryPerformanceMonitor = new QueryPerformanceMonitor({ verySlowQueryThreshold: env.VERY_SLOW_QUERY_THRESHOLD_MS, });