import type { getPrismaClient, PrismaClientOptions } from '@prisma/client/runtime/client.d.ts' import env from 'env-var' import type { LoggerOptions } from 'pino' import type { SleetClient } from 'sleetcord' import { baseLogger, LOG_LEVEL } from './logging.ts' import { Sentry } from './sentry.ts' type PrismaClient = ReturnType extends new (optionsArg: PrismaClientOptions) => infer T ? T : never type PrismaClientWith$on = Pick const USE_PINO_PRETTY = env.get('USE_PINO_PRETTY').required().asBool() const loggerOptions: LoggerOptions = { level: LOG_LEVEL, } if (USE_PINO_PRETTY) { loggerOptions.transport = { target: 'pino-dev', } } const prismaLogger = baseLogger.child({ module: 'prisma' }) /** Warn if a query took longer than X ms */ const QUERY_TOO_LONG_WARNING = 2_000 /** * Inits DB logging using a prisma client, logs queries, info, warnings, errors + warnings for queries that take too long * @param prisma The prisma client to use * @param sleet The Sleet client to use */ export function initDBLogging(prisma: PrismaClientWith$on, sleet: SleetClient) { prisma.$on('query', (e) => { const fixedDuration = e.duration.toFixed(4) prismaLogger.debug( { ...moduleName(sleet), type: 'query', duration: e.duration }, `${e.query}; (Took ${fixedDuration}ms)`, ) if (e.duration > QUERY_TOO_LONG_WARNING) { const context = { ...moduleName(sleet), type: 'query-too-long', duration: e.duration, } const message = `${e.query}; (Took too long ${fixedDuration}ms)` prismaLogger.warn(context, message) Sentry.captureMessage(message, { level: 'warning', extra: context, }) } }) prisma.$on('info', (e: { message: string; target: string }) => { prismaLogger.info( { ...moduleName(sleet), type: 'prisma-info' }, `${e.message} (Target ${e.target})`, ) Sentry.captureMessage(e.message, { level: 'info', extra: { ...moduleName(sleet), }, tags: { target: e.target, type: 'prisma-info', }, }) }) prisma.$on('warn', (e: { message: string; target: string }) => { prismaLogger.warn( { ...moduleName(sleet), type: 'prisma-warn' }, `${e.message} (Target ${e.target})`, ) Sentry.captureMessage(e.message, { level: 'warning', extra: { ...moduleName(sleet), }, tags: { target: e.target, type: 'prisma-warn', }, }) }) prisma.$on('error', (e: { message: string; target: string }) => { prismaLogger.error( { ...moduleName(sleet), type: 'prisma-error' }, `${e.message} (Target ${e.target})`, ) Sentry.captureMessage(e.message, { level: 'error', extra: { ...moduleName(sleet), }, tags: { target: e.target, type: 'prisma-error', }, }) }) } function moduleName(sleet: SleetClient): { name: string } | undefined { const module = sleet.runningModuleStore.getStore() if (module) { return { name: module.name } } return undefined }