Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tracing): Add Prisma ORM integration. #4931

Merged
merged 11 commits into from Apr 21, 2022
1 change: 1 addition & 0 deletions packages/node-integration-tests/.gitignore
@@ -0,0 +1 @@
node_modules
4 changes: 4 additions & 0 deletions packages/node-integration-tests/package.json
Expand Up @@ -7,14 +7,18 @@
},
"private": true,
"scripts": {
"clean": "rimraf -g **/node_modules",
"prisma:init": "(cd suites/tracing/prisma-orm && ./setup.sh)",
"lint": "run-s lint:prettier lint:eslint",
"lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish",
"lint:prettier": "prettier --check \"{suites,utils}/**/*.ts\"",
"type-check": "tsc",
"pretest": "run-s --silent prisma:init",
"test": "jest --runInBand --forceExit",
"test:watch": "yarn test --watch"
},
"dependencies": {
"@prisma/client": "^3.12.0",
"@types/mongodb": "^3.6.20",
"@types/mysql": "^2.15.21",
"@types/pg": "^8.6.5",
Expand Down
@@ -0,0 +1,13 @@
version: '3.9'

services:
db:
image: postgres:13
restart: always
container_name: integration-tests-prisma
ports:
- '5433:5432'
environment:
POSTGRES_USER: prisma
POSTGRES_PASSWORD: prisma
POSTGRES_DB: tests
@@ -0,0 +1,22 @@
{
"name": "sentry-prisma-test",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's move this out into another PR so we can discuss the integration test separately? In the mean time, we can add sanity check unit tests to this PR (similar to what we have for mongo for ex.)

"version": "1.0.0",
"description": "",
"main": "index.js",
"engines": {
"node": ">=12"
},
"scripts": {
"db-up": "docker-compose up -d",
"generate": "prisma generate",
"migrate": "prisma migrate dev -n sentry-test",
"setup": "run-s --silent db-up generate migrate"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@prisma/client": "3.12.0",
"prisma": "^3.12.0"
}
}
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
@@ -0,0 +1,12 @@
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"email" TEXT NOT NULL,
"name" TEXT,

CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
@@ -0,0 +1,15 @@
datasource db {
url = "postgresql://prisma:prisma@localhost:5433/tests"
provider = "postgresql"
}

generator client {
provider = "prisma-client-js"
}

model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
email String @unique
name String?
}
@@ -0,0 +1,48 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { PrismaClient } from '@prisma/client';
import * as Sentry from '@sentry/node';
import * as Tracing from '@sentry/tracing';
import { randomBytes } from 'crypto';

const client = new PrismaClient();

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
release: '1.0',
tracesSampleRate: 1.0,
integrations: [new Tracing.Integrations.Prisma({ client })],
});

async function run(): Promise<void> {
const transaction = Sentry.startTransaction({
name: 'Test Transaction',
op: 'transaction',
});

Sentry.configureScope(scope => {
scope.setSpan(transaction);
});

try {
await client.user.create({
data: {
name: 'Tilda',
email: `tilda_${randomBytes(4).toString('hex')}@sentry.io`,
},
});

await client.user.findMany();

await client.user.deleteMany({
where: {
email: {
contains: 'sentry.io',
},
},
});
} finally {
if (transaction) transaction.finish();
}
}

void run();
10 changes: 10 additions & 0 deletions packages/node-integration-tests/suites/tracing/prisma-orm/setup.sh
@@ -0,0 +1,10 @@
#!/bin/bash

NODE_MAJOR=$(node -v | cut -c2- | cut -d. -f1)

if [ "$NODE_MAJOR" -lt "12" ]; then
echo "Skipping Prisma tests on Node: $NODE_MAJOR"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think going forward we would like everything to be node scripts. Can we convert this to a TS script (we have ts-node in the repo)?

exit 0
fi

yarn && yarn setup
17 changes: 17 additions & 0 deletions packages/node-integration-tests/suites/tracing/prisma-orm/test.ts
@@ -0,0 +1,17 @@
import { assertSentryTransaction, conditionalTest, getEnvelopeRequest, runServer } from '../../../utils';

conditionalTest({ min: 12 })('Prisma ORM Integration', () => {
test('should instrument Prisma client for tracing.', async () => {
const url = await runServer(__dirname);
const envelope = await getEnvelopeRequest(url);

assertSentryTransaction(envelope[2], {
transaction: 'Test Transaction',
spans: [
{ description: 'User create', op: 'db.prisma' },
{ description: 'User findMany', op: 'db.prisma' },
{ description: 'User deleteMany', op: 'db.prisma' },
],
});
});
});
@@ -0,0 +1,27 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"@prisma/client@3.12.0":
version "3.12.0"
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.12.0.tgz#a0eb49ffea5c128dd11dffb896d7139a60073d12"
integrity sha512-4NEQjUcWja/NVBvfuDFscWSk1/rXg3+wj+TSkqXCb1tKlx/bsUE00rxsvOvGg7VZ6lw1JFpGkwjwmsOIc4zvQw==
dependencies:
"@prisma/engines-version" "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980"

"@prisma/engines-version@3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980":
version "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980"
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980.tgz#829ca3d9d0d92555f44644606d4edfd45b2f5886"
integrity sha512-o+jo8d7ZEiVpcpNWUDh3fj2uPQpBxl79XE9ih9nkogJbhw6P33274SHnqheedZ7PyvPIK/mvU8MLNYgetgXPYw==

"@prisma/engines@3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980":
version "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980"
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980.tgz#e52e364084c4d05278f62768047b788665e64a45"
integrity sha512-zULjkN8yhzS7B3yeEz4aIym4E2w1ChrV12i14pht3ePFufvsAvBSoZ+tuXMvfSoNTgBS5E4bolRzLbMmbwkkMQ==

prisma@^3.12.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/prisma/-/prisma-3.12.0.tgz#9675e0e72407122759d3eadcb6d27cdccd3497bd"
integrity sha512-ltCMZAx1i0i9xuPM692Srj8McC665h6E5RqJom999sjtVSccHSD8Z+HSdBN2183h9PJKvC5dapkn78dd0NWMBg==
dependencies:
"@prisma/engines" "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980"
1 change: 1 addition & 0 deletions packages/tracing/src/hubextensions.ts
@@ -1,3 +1,4 @@
/* eslint-disable max-lines */
import { getMainCarrier, Hub } from '@sentry/hub';
import {
CustomSamplingContext,
Expand Down
1 change: 1 addition & 0 deletions packages/tracing/src/integrations/index.ts
Expand Up @@ -2,6 +2,7 @@ export { Express } from './node/express';
export { Postgres } from './node/postgres';
export { Mysql } from './node/mysql';
export { Mongo } from './node/mongo';
export { Prisma } from './node/prisma';

// TODO(v7): Remove this export
// Please see `src/index.ts` for more details.
Expand Down
99 changes: 99 additions & 0 deletions packages/tracing/src/integrations/node/prisma.ts
@@ -0,0 +1,99 @@
import { Hub } from '@sentry/hub';
import { EventProcessor, Integration } from '@sentry/types';
import { isThenable, logger } from '@sentry/utils';

import { IS_DEBUG_BUILD } from '../../flags';

type PrismaAction =
| 'findUnique'
| 'findMany'
| 'findFirst'
| 'create'
| 'createMany'
| 'update'
| 'updateMany'
| 'upsert'
| 'delete'
| 'deleteMany'
| 'executeRaw'
| 'queryRaw'
| 'aggregate'
| 'count'
| 'runCommandRaw';

interface PrismaMiddlewareParams {
model?: any;
action: PrismaAction;
args: any;
dataPath: string[];
runInTransaction: boolean;
}

type PrismaMiddleware<T = any> = (
params: PrismaMiddlewareParams,
next: (params: PrismaMiddlewareParams) => Promise<T>,
) => Promise<T>;

interface PrismaClient {
$use: (cb: PrismaMiddleware) => void;
}

/** Tracing integration for @prisma/client package */
export class Prisma implements Integration {
/**
* @inheritDoc
*/
public static id: string = 'Prisma';

/**
* @inheritDoc
*/
public name: string = Prisma.id;

/**
* Prisma ORM Client Instance
*/
private readonly _client?: PrismaClient;

/**
* @inheritDoc
*/
public constructor(options: { client?: PrismaClient } = {}) {
this._client = options.client;
}

/**
* @inheritDoc
*/
public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
if (!this._client) {
IS_DEBUG_BUILD && logger.error('PrismaIntegration is missing a Prisma Client Instance');
return;
}

this._client.$use((params: PrismaMiddlewareParams, next: (params: PrismaMiddlewareParams) => Promise<unknown>) => {
const scope = getCurrentHub().getScope();
const parentSpan = scope?.getSpan();

const action = params.action;
const model = params.model;

const span = parentSpan?.startChild({
description: model ? `${model} ${action}` : action,
op: 'db.prisma',
});

const rv = next(params);

if (isThenable(rv)) {
return rv.then((res: unknown) => {
span?.finish();
return res;
});
}

span?.finish();
return rv;
});
}
}
12 changes: 12 additions & 0 deletions yarn.lock
Expand Up @@ -3292,6 +3292,18 @@
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.12.tgz#431ec342a7195622f86688bbda82e3166ce8cb28"
integrity sha512-6RglhutqrGFMO1MNUXp95RBuYIuc8wTnMAV5MUhLmjTOy78ncwOw7RgeQ/HeymkKXRhZd0s2DNrM1rL7unk3MQ==

"@prisma/client@^3.12.0":
version "3.12.0"
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.12.0.tgz#a0eb49ffea5c128dd11dffb896d7139a60073d12"
integrity sha512-4NEQjUcWja/NVBvfuDFscWSk1/rXg3+wj+TSkqXCb1tKlx/bsUE00rxsvOvGg7VZ6lw1JFpGkwjwmsOIc4zvQw==
dependencies:
"@prisma/engines-version" "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980"

"@prisma/engines-version@3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980":
version "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980"
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980.tgz#829ca3d9d0d92555f44644606d4edfd45b2f5886"
integrity sha512-o+jo8d7ZEiVpcpNWUDh3fj2uPQpBxl79XE9ih9nkogJbhw6P33274SHnqheedZ7PyvPIK/mvU8MLNYgetgXPYw==

"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
Expand Down