This tutorial will explore how you can log Prisma Client events in your NestJs application.
Prisma Client provides two patterns in which you can log events in your app:
- Logging to stdout (default)
- Event-based logging
stdout
=== standard output
This guide will use the typescript/rest-nestjs
↗ starter project from the Prisma examples repository. Follow the instructions in the example README to get up and running with the example on your computer.
Logging to stdout
Logging events to the stdout is the easiest way you can start logging events to the console. To get started logging, make the following changes to your PrismaService
:
- Define the constructor method
- Use the
super
keyword to define thelog
property with an empty array ([]
) - Define the type of logs in the array — e.g.,
error
,info
,query
, andwarn
import {
Injectable,
OnModuleInit,
INestApplication,
Logger,
} from "@nestjs/common";
import { PrismaClient } from "@prisma/client";
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
constructor() {
super({
log: ["error", "info", "query", "warn"],
});
}
async onModuleInit() {
await this.$connect();
}
}
When the app reloads, you should see some logs from Prisma Client printed on the console:
prisma:info Starting a sqlite pool with 9 connections.
[Nest] 34583 - 13/11/2022, 22:24:14 LOG [NestApplication] Nest application successfully started +6ms
[Nest] 75445 - 17/11/2022, 22:21:05 LOG [NestApplication] Nest application successfully started +81ms
prisma:query SELECT 1
prisma:query SELECT `main`.`Post`.`id`, `main`.`Post`.`createdAt`, `main`.`Post`.`updatedAt`, `main`.`Post`.`title`, `main`.`Post`.`content`, `main`.`Post`.`published`, `main`.`Post`.`viewCount`, `main`.`Post`.`authorId` FROM `main`.`Post` WHERE (`main`.`Post`.`id` = ? AND 1=1) LIMIT ? OFFSET ?
The
prisma:query
is output to the console after making aGET
request tohttp://localhost:3000/post/1
The extended version of the above snippet is defining an array of objects. Each object will have emit
and level
properties for every type of event you would like to log. The emit
property should have a value of stdout
to log the events to the stdout:
import { Injectable, OnModuleInit, INestApplication } from "@nestjs/common";
import { Prisma, PrismaClient } from "@prisma/client";
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
constructor() {
super({
log: [
{
emit: "stdout",
level: "query",
},
{
emit: "stdout",
level: "error",
},
{
emit: "stdout",
level: "info",
},
{
emit: "stdout",
level: "warn",
},
],
});
}
async onModuleInit() {
await this.$connect();
}
}
Event-based logging
Another logging pattern that Prisma Client supports is event-logging. This means that you can subscribe to different kind of events, and then have custom logic of how you would like to handle the event.
You can use the built in Logger
provided by NestJs in your PrismaService
to provide a consistent behavior and formatting across both Nest system logging. It’s considered a good practice to instantiate a new logger for each service in your Nest.
To get started subscribing to Prisma Client events, make the following changes:
- Define a Prisma Client generic that will accept two parameters:
Prisma.PrismaClientOptions
: a type from Prisma that specifies the options available in Prisma Client’s constructor methodPrisma.LogLevel
: another type from Prisma specifying the logs types (emit
, andlevel
)
- Instantiate a logger for the service with service name as the
context
argument in the constructor - Define the logs, setting the
emit
property’s value asevent
. Prisma Client will subscribe to the events - Use the
$this.on()
method to subscribe to the events defined in the constructor using the logger defined in 2
Feel free to adjust the log levels.
import {
Injectable,
OnModuleInit,
INestApplication,
Logger,
} from "@nestjs/common";
import { Prisma, PrismaClient } from "@prisma/client";
@Injectable()
// 1.
export class PrismaService
extends PrismaClient<Prisma.PrismaClientOptions, Prisma.LogLevel>
implements OnModuleInit
{
//2.
private readonly logger = new Logger(PrismaService.name);
constructor() {
super({
// 3.
log: [
{
emit: "event",
level: "query",
},
{
emit: "event",
level: "error",
},
{
emit: "event",
level: "info",
},
{
emit: "event",
level: "warn",
},
],
});
}
async onModuleInit() {
//4.
this.$on("error", (event) => {
this.logger.error(event);
});
this.$on("warn", (event) => {
this.logger.warn(event);
});
this.$on("info", (event) => {
this.logger.verbose(event);
});
this.$on("query", (event) => {
this.logger.log(event);
});
await this.$connect();
}
}
Once you’re done updating the service, you should be able to see the [PrismaService]
context printed out to the console
[Nest] 77319 - 17/11/2022, 22:39:34 LOG [PrismaService] Object:
{
"timestamp": "2022-11-17T21:39:34.431Z",
"message": "Starting a sqlite pool with 9 connections.",
"target": "quaint::pooled"
}
[Nest] 77319 - 17/11/2022, 22:39:34 LOG [NestApplication] Nest application successfully started +2ms
If you make a GET
request to http://localhost:3000/post/1
[Nest] 77319 - 17/11/2022, 22:39:45 LOG [PrismaService] Object:
{
"timestamp": "2022-11-17T21:39:45.597Z",
"query": "SELECT `main`.`Post`.`id`, `main`.`Post`.`createdAt`, `main`.`Post`.`updatedAt`, `main`.`Post`.`title`, `main`.`Post`.`content`, `main`.`Post`.`published`, `main`.`Post`.`viewCount`, `main`.`Post`.`authorId` FROM `main`.`Post` WHERE (`main`.`Post`.`id` = ? AND 1=1) LIMIT ? OFFSET ?",
"params": "[1,1,0]",
"duration": 0,
"target": "quaint::connector::metrics"
}
The logs now look a little prettier 🌈 and more consistent with the NestJs logging style.
To learn more about NestJs logging, take a look at their docs ↗ for an in-depth guide.
Refer to Prisma’s docs ↗ to learn more about logging in Prisma Client
If you run into any issues in the guide, feel free to tweet at me or message me on Twitter ↗.