Prisma metrics give you a detailed insight into how Prisma Client interacts with your database. You can use this insight to help diagnose performance issues with your application.

About metrics

You can export metrics in JSON or Prometheus formats and view them in a console log, or integrate them into an external metrics system, such as StatsD or Prometheus. If you integrate them into an external metrics system, then you can view the metrics data over time. For example, you can use metrics to help diagnose how your application's number of idle and active connections changes.

Prisma Client provides the following metrics:

  • Counters (always increase):

    • query_total_queries: The total number of database queries executed.
    • query_total_operations: The total number of Prisma operations executed.
      • A Prisma operation is a statement like await prisma.user({ include: { posts: true }}), which Prisma Client translates into one or more SQL queries.
      • The value returned by query_total_operations can be less than query_total_queries, because some operations create multiple queries.
  • Gauges (can increase or decrease):

    • pool_active_connections: The number of active connections in use.
    • pool_idle_connections: The number of connections that are not being used.
    • pool_wait_count: The number of workers waiting for a connection because all connections are in use.
    • query_active_transactions: The number of active transactions.
  • Histograms (metrics data divided into a collection of values; we call each container in the collection a "bucket"):

    • pool_wait_duration_ms: The wait time for a worker to get a connection.
    • query_total_elapsed_time_ms: The execution time for all database queries executed.
    • query_operation_total_elapsed_time_ms: The execution time for all operations. This includes the time taken to execute all database queries, and to carry out all database engine activities, such as joining data and transforming data to the correct format.

You can add global labels to your metrics data to help you group and separate your metrics, for example by infrastructure region or server.

Prerequisites

To use Prisma metrics, you must do the following:

  1. Install the appropriate dependencies.
  2. Enable the metrics feature flag in your Prisma schema file.

Step 1. Install up-to-date Prisma dependencies

Use version 3.15.0 or higher of the prisma and @prisma/client npm packages.

$npm install prisma@latest --save-dev
$npm install @prisma/client@latest --save

Step 2: Enable the feature flag in the Prisma schema file

In the generator block of your schema.prisma file, enable the metrics feature flag:

generator client {
provider = "prisma-client-js"
previewFeatures = ["metrics"]
}

Retrieve metrics in JSON format

When you retrieve metrics in JSON format, you can use them in the format they are returned, or send them to StatSD to visualize how they change over time.

To retrieve metrics in JSON format, add the following lines to your application code:

let metrics = await prisma.$metrics.json()
console.log(metrics)

This returns metrics as follows:

{
"counters": [
{
"key": "query_total_operations",
"labels": {},
"value": 0,
"description": "The total number of operations executed"
},
{
"key": "query_total_queries",
"labels": {},
"value": 0,
"description": "The total number of queries executed"
}
],
"gauges": [
{
"key": "pool_active_connections",
"labels": {},
"value": 0,
"description": "The number of active connections in use"
},
{
"key": "pool_idle_connections",
"labels": {},
"value": 21,
"description": "The number of connections that are not being used"
},
{
"key": "pool_wait_count",
"labels": {},
"value": 0,
"description": "The number of workers waiting for a connection"
},
{
"key": "query_active_transactions",
"labels": {},
"value": 0,
"description": "The number of active transactions"
}
],
"histograms": [
{
"key": "pool_wait_duration_ms",
"labels": {},
"value": {
"buckets": [
[0, 0],
[1, 0],
[5, 0],
[10, 0],
[50, 0],
[100, 0],
[500, 0],
[1000, 0],
[5000, 0],
[50000, 0]
],
"sum": 0,
"count": 0
},
"description": "The wait time for a worker to get a connection."
},
{
"key": "query_operation_total_elapsed_time_ms",
"labels": {},
"value": {
"buckets": [
[0, 0],
[1, 1],
[5, 1],
[10, 0],
[50, 0],
[100, 0],
[500, 0],
[1000, 0],
[5000, 0],
[50000, 0]
],
"sum": 1.367708,
"count": 2
},
"description": ""
},
{
"key": "query_total_elapsed_time_ms",
"labels": {},
"value": {
"buckets": [
[0, 0],
[1, 5],
[5, 0],
[10, 0],
[50, 0],
[100, 0],
[500, 0],
[1000, 0],
[5000, 0],
[50000, 0]
],
"sum": 1.113832,
"count": 5
},
"description": ""
}
]
}

Histograms in JSON data

Each histogram "bucket" has two values. The first one is the upper bound of the bucket, and the second one is the count (the number of data values that fall into that bucket). In the following example, there are two instances of values between 11 and 20, and five instances of values between 21 and 30:

...
[20, 2],
[30, 5],
...

Use Prisma metrics with StatsD

You can send JSON-formatted metrics to StatsD to visualize your metrics data over time.

Note: You must provide counter metrics to StatsD as a series of values that are incremented or decremented from a previous retrieval of the metrics. However, Prisma counter metrics return absolute values. Therefore, you must convert your counter metrics to a series of incremented and decremented values and send them to StatsD as gauge data. In the code example below, we convert counter metrics into incremented and decremented gauge data in diffHistograms.

In the following example, we send metrics to StatsD every 10 seconds. This timing aligns with the default 10s flush rate of StatsD.

import StatsD from 'hot-shots'
let statsd = new StatsD({
port: 8125,
})
let diffMetrics = (metrics: any) => {
return metrics.map((metric: any) => {
let prev = 0
let diffBuckets = metric.value.buckets.map((values: any) => {
let [bucket, value] = values
let diff = value - prev
prev = value
return [bucket, diff]
})
metric.value.buckets = diffBuckets
return metric
})
}
let previousHistograms: any = null
let statsdSender = async () => {
let metrics = await prisma.$metrics.json()
metrics.counters.forEach((counter: any) => {
statsd.gauge('prisma.' + counter.key, counter.value, (...res) => {})
})
metrics.gauges.forEach((counter: any) => {
statsd.gauge('prisma.' + counter.key, counter.value, (...res) => {})
})
if (previousHistograms === null) {
previousHistograms = diffMetrics(metrics.histograms)
return
}
let diffHistograms = diffMetrics(metrics.histograms)
diffHistograms.forEach((diffHistograms: any, histogramIndex: any) => {
diffHistograms.value.buckets.forEach((values: any, bucketIndex: any) => {
let [bucket, count] = values
let [_, prev] =
previousHistograms[histogramIndex].value.buckets[bucketIndex]
let change = count - prev
for (let sendTimes = 0; sendTimes < change; sendTimes++) {
statsd.timing('prisma.' + diffHistograms.key, bucket)
}
})
})
previousHistograms = diffHistograms
}
setInterval(async () => await statsdSender(), 10000)

Retrieve metrics in Prometheus format

When you retrieve Prisma metrics in Prometheus format, you can use them in the format they are returned, or send them to the Prometheus metrics system to visualize how they change over time.

To retrieve metrics in Prometheus format, add the following lines to your application code:

let metrics = prisma.$metrics.prometheus()
console.log(metrics)

This returns metrics as follows:

# HELP query_total_operations
# TYPE query_total_operations counter
query_total_operations 2
# HELP query_total_queries
# TYPE query_total_queries counter
query_total_queries 5
# HELP pool_active_connections The number of active connections in use.
# TYPE pool_active_connections gauge
pool_active_connections 0
# HELP pool_idle_connections The number of connections that are not being used
# TYPE pool_idle_connections gauge
pool_idle_connections 21
# HELP pool_wait_count The number of workers waiting for a connection.
# TYPE pool_wait_count gauge
pool_wait_count 0
# HELP query_active_transactions
# TYPE query_active_transactions gauge
query_active_transactions 0
# HELP pool_wait_duration_ms The wait time for a worker to get a connection.
# TYPE pool_wait_duration_ms histogram
pool_wait_duration_ms_bucket{le="0"} 0
pool_wait_duration_ms_bucket{le="1"} 3
pool_wait_duration_ms_bucket{le="5"} 3
pool_wait_duration_ms_bucket{le="10"} 3
pool_wait_duration_ms_bucket{le="50"} 3
pool_wait_duration_ms_bucket{le="100"} 3
pool_wait_duration_ms_bucket{le="500"} 3
pool_wait_duration_ms_bucket{le="1000"} 3
pool_wait_duration_ms_bucket{le="5000"} 3
pool_wait_duration_ms_bucket{le="50000"} 3
pool_wait_duration_ms_bucket{le="+Inf"} 3
pool_wait_duration_ms_sum 0.023208
pool_wait_duration_ms_count 3
# HELP query_operation_total_elapsed_time_ms
# TYPE query_operation_total_elapsed_time_ms histogram
query_operation_total_elapsed_time_ms_bucket{le="0"} 0
query_operation_total_elapsed_time_ms_bucket{le="1"} 1
query_operation_total_elapsed_time_ms_bucket{le="5"} 2
query_operation_total_elapsed_time_ms_bucket{le="10"} 2
query_operation_total_elapsed_time_ms_bucket{le="50"} 2
query_operation_total_elapsed_time_ms_bucket{le="100"} 2
query_operation_total_elapsed_time_ms_bucket{le="500"} 2
query_operation_total_elapsed_time_ms_bucket{le="1000"} 2
query_operation_total_elapsed_time_ms_bucket{le="5000"} 2
query_operation_total_elapsed_time_ms_bucket{le="50000"} 2
query_operation_total_elapsed_time_ms_bucket{le="+Inf"} 2
query_operation_total_elapsed_time_ms_sum 3.197624
query_operation_total_elapsed_time_ms_count 2
# HELP query_total_elapsed_time_ms
# TYPE query_total_elapsed_time_ms histogram
query_total_elapsed_time_ms_bucket{le="0"} 0
query_total_elapsed_time_ms_bucket{le="1"} 5
query_total_elapsed_time_ms_bucket{le="5"} 5
query_total_elapsed_time_ms_bucket{le="10"} 5
query_total_elapsed_time_ms_bucket{le="50"} 5
query_total_elapsed_time_ms_bucket{le="100"} 5
query_total_elapsed_time_ms_bucket{le="500"} 5
query_total_elapsed_time_ms_bucket{le="1000"} 5
query_total_elapsed_time_ms_bucket{le="5000"} 5
query_total_elapsed_time_ms_bucket{le="50000"} 5
query_total_elapsed_time_ms_bucket{le="+Inf"} 5
query_total_elapsed_time_ms_sum 1.8407059999999997
query_total_elapsed_time_ms_count 5

Use Prisma metrics with the Prometheus metrics system

In the majority of cases, Prometheus must scrape an endpoint to retrieve metrics. The following example shows how to send data with Express.js:

import { PrismaClient } from "@prisma/client";
import express, { Request, Response } from "express";
const app = express();
const port = 4000;
const prisma = new PrismaClient();
app.get("/metrics", async (_req, res: Response) => {
let metrics = await prisma.$metrics.prometheus();
res.end(metrics);
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
})

The following example shows how to combine Prisma metrics with other Prometheus client libraries that are also served with a REST API endpoint in conjunction with Express.js:

import { PrismaClient } from "@prisma/client";
import express, { Request, Response } from "express";
import prom from "prom-client";
const app = express();
const port = 4000;
const prisma = new PrismaClient();
const register = new prom.Registry();
prom.collectDefaultMetrics({ register });
app.get('/metrics', async (_req, res: Response) => {
let prismaMetrics = await prisma.$metrics.prometheus();
let appMetrics = await register.metrics();
res.end(prismaMetrics + appMetrics);
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
})

Global labels

You can add global labels to your metrics to help you group and separate your metrics. Each instance of Prisma Client adds these labels to the metrics that it generates. For example, you can group your metrics by infrastructure region, or by server, with a label like { server: us_server1', 'app_version': 'one' }.

Global labels work with JSON and Prometheus-formatted metrics.

For example, to add global labels to JSON-format metrics, add the following code to your application:

let metrics = prisma.$metrics.json({
globalLabels: { server: 'us_server1', app_version: 'one' },
})
console.log(metrics)

This returns information in the following format:

{
"counters": [
{
"key": "query_total_operations",
"labels": { "server": "us_server1", "app_version": "one" },
"value": 0,
"description": "The total number of operations executed"
},
{
"key": "query_total_queries",
"labels": { "server": "us_server1", "app_version": "one" },
"value": 0,
"description": "The total number of queries executed"
}
],
"gauges": [
{
"key": "pool_active_connections",
"labels": { "server": "us_server1", "app_version": "one" },
"value": 0,
"description": "The number of active connections in use"
},
{
"key": "pool_idle_connections",
"labels": { "server": "us_server1", "app_version": "one" },
"value": 21,
"description": "The number of connections that are not being used"
},
{
"key": "pool_wait_count",
"labels": { "server": "us_server1", "app_version": "one" },
"value": 0,
"description": "The number of workers waiting for a connection"
},
{
"key": "query_active_transactions",
"labels": { "server": "us_server1", "app_version": "one" },
"value": 0,
"description": "The number of active transactions"
}
],
"histograms": [
{
"key": "pool_wait_duration_ms",
"labels": { "server": "us_server1", "app_version": "one" },
"value": {
"buckets": [
[0, 0],
[1, 0],
[5, 0],
[10, 0],
[50, 0],
[100, 0],
[500, 0],
[1000, 0],
[5000, 0],
[50000, 0]
],
"sum": 0,
"count": 0
},
"description": "The wait time for a worker to get a connection."
}
]
}
Edit this page on GitHub