getsentry / sentry-javascript

Official Sentry SDKs for JavaScript
https://sentry.io
MIT License
7.88k stars 1.55k forks source link

Prisma not tracking spans with Sentry Node.js integration #10680

Closed SheaBelsky closed 7 months ago

SheaBelsky commented 7 months ago

Is there an existing issue for this?

How do you use Sentry?

Sentry Saas (sentry.io)

Which SDK are you using?

@sentry/node

SDK Version

7.101.0

Framework Version

Node 18.18.2 Prisma 5.9.1

Link to Sentry event

https://mentra-ec.sentry.io/discover/leto:f128265a11dc4a19aa16092121985b6d/?field=title&field=event.type&field=project&field=user.display&field=timestamp&field=replayId&name=All+Events&project=6509483&query=&sort=-timestamp&statsPeriod=7d&yAxis=count%28%29

SDK Setup

import { extraErrorDataIntegration } from "@sentry/integrations";
import { IS_AZURE_PRODUCTION, IS_STAGING } from "./constants";
import { ProfilingIntegration } from "@sentry/profiling-node";
import { SENTRY_DSN } from "../models/sentry/data";
import * as Sentry from "@sentry/node";
import prisma from "../client";

type CaptureContext = Parameters<typeof Sentry.captureException>[1];

if (IS_AZURE_PRODUCTION || IS_STAGING) {
  Sentry.init({
    debug: true,
    dsn: SENTRY_DSN,
    environment: IS_AZURE_PRODUCTION ? "production" : "staging",
    integrations: [
      new ProfilingIntegration(),
      extraErrorDataIntegration({
        depth: 4
      }),
      Sentry.anrIntegration({ captureStackTrace: true }),
      Sentry.consoleIntegration(),
      new Sentry.Integrations.GraphQL(),
      new Sentry.Integrations.Prisma({ client: prisma })
    ],
    profilesSampleRate: 1.0,
    tracesSampleRate: 1.0
  });
  console.log(`🔦 Sentry initialized`);
}

Steps to Reproduce

  1. Set up the Prisma integration with Sentry as per the setup guide (https://docs.sentry.io/platforms/node/performance/database/opt-in/#prisma-orm-integration)
  2. Execute an operation that makes a call to Prisma and records the span to Sentry

Expected Result

I should see information about the Prisma operation in the recorded Sentry span

Actual Result

I do not see anything about Prisma in the recorded Sentry span. In this case, Prisma executes before the http.client call in the below picture.

Screenshot 2024-02-15 at 10 37 27 AM

mydea commented 7 months ago

We are currently working on v8 of the SDK which has a completely new prisma integration based on OpenTelemetry, which should support this much better out of the box.

What version of Prisma are you using? Maybe we have some incompatibility with a new Prisma version... 🤔

SheaBelsky commented 7 months ago

We are currently working on v8 of the SDK which has a completely new prisma integration based on OpenTelemetry, which should support this much better out of the box.

What version of Prisma are you using? Maybe we have some incompatibility with a new Prisma version... 🤔

Sorry, should have included that in my original message. I am using Prisma 5.9.1

lforst commented 7 months ago

Hi, for my side project I am using the prisma integration on the latest version and everything seems to work. Would you mind turning on debug: true in your init options. We have a log message we print when you have a prisma client we don't support.

SheaBelsky commented 7 months ago

Hi, for my side project I am using the prisma integration on the latest version and everything seems to work. Would you mind turning on debug: true in your init options. We have a log message we print when you have a prisma client we don't support.

@lforst

Here's the output I get after loading Sentry and when I load a page which has a Prisma query:

Sentry Logger [log]: Integration installed: InboundFilters
Sentry Logger [log]: Integration installed: FunctionToString
Sentry Logger [log]: Integration installed: LinkedErrors
Sentry Logger [log]: Integration installed: RequestData
Sentry Logger [log]: Integration installed: Console
Sentry Logger [log]: Integration installed: Http
Sentry Logger [log]: Integration installed: Undici
Sentry Logger [log]: Integration installed: OnUncaughtException
Sentry Logger [log]: Integration installed: OnUnhandledRejection
Sentry Logger [log]: Integration installed: ContextLines
Sentry Logger [log]: Integration installed: LocalVariables
Sentry Logger [log]: Integration installed: Context
Sentry Logger [log]: Integration installed: Modules
Sentry Logger [log]: Integration installed: ProfilingIntegration
Sentry Logger [log]: Integration installed: ExtraErrorData
Sentry Logger [log]: Integration installed: GraphQL
Sentry Logger [log]: Integration installed: Prisma
🔦 Sentry initialized
⏰ Starting server....
🚀 Server ready at http://localhost:8080/graphql
📙 Redis is connected!
📙 Redis is READY!
📅 Agenda up and running
info: [LaunchDarkly] Opened LaunchDarkly stream connection
🎰 LaunchDarkly initialized
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "https://test.stytch.com/v1/b2b/sessions/jwks/project-test-3fa200d4-094d-4960-93b4-f9f77322d2d6": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "https://events.launchdarkly.com/bulk": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/sales-demo-shortlisted-user-2-0": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/sales-demo-shortlisted-user-1-0": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/sales-demo-invited-user-0": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/sales-demo-recommended-user-3-0": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/sales-demo-recommended-user-1-0": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/sales-demo-interested-user-0": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/sales-demo-recommended-user-2-0": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/test-candidateonethousandtwo-1-0": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/test-candidateonethousandfour-1-0": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/test-candidateonethousandthree-1-0": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/sales-demo-shortlisted-user-2-1": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/sales-demo-shortlisted-user-1-1": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/sales-demo-invited-user-1": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/sales-demo-recommended-user-3-1": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/sales-demo-recommended-user-1-1": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/sales-demo-interested-user-1": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/sales-demo-recommended-user-2-1": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/test-candidateonethousandtwo-1-1": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/test-candidateonethousandfour-1-1": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "temp_url/test-candidateonethousandthree-1-1": 
Sentry Logger [log]: [Tracing] Adding sentry-trace header 857e56d4b9b64ec58761668831a2a04a-8224816a328501f0 to outgoing request to "https://events.launchdarkly.com/bulk": 

This is using Sentry 7.101 and Prisma 5.9.1. Do I need to do anything else other than the setup below?

Sentry.init({
    debug: !!process.env.SENTRY_DEBUG,
    dsn: SENTRY_DSN,
    environment: IS_AZURE_PRODUCTION ? "production" : "staging",
    ignoreErrors: [
    ],
    integrations: [
      new ProfilingIntegration(),
      extraErrorDataIntegration({
        depth: 4
      }),
      // Sentry.anrIntegration({ captureStackTrace: true }),
      Sentry.consoleIntegration(),
      new Sentry.Integrations.GraphQL(),
      new Sentry.Integrations.Prisma({ client: prisma })
    ],
    profilesSampleRate: 1.0,
    tracesSampleRate: 1.0
  });
lforst commented 7 months ago

The only thing that could be happening is that you are passing a different instance of the prisma client to sentry than you end up using. To avoid this, I am doing the following in my app:

import { PrismaClient } from '@prisma/client';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
declare const globalThis: Record<string, any>;

const registerService = <T>(name: string, initFn: () => T): T => {
  if (!(name in global)) {
    globalThis[name] = initFn();
  }

  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
  return globalThis[name];
};

const basePrismaClient = registerService(
  'base-prisma-client',
  () =>
    new PrismaClient({
      datasources: {
        db: {
          url: process.env.DATABASE_CONNECTION_STRING || 'DID_NOT_SET_CONNECTION_STRING',
        },
      },
    })
);

export { basePrismaClient };

It is a bit overly complex because it caters to my use-case but maybe it helps solve your case?

SheaBelsky commented 7 months ago

Are you using a serverless setup or a regular server? I'm on a regular server setup, and I'm just following the Prisma documentation for instantiating a Prisma client once and reusing it.

https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/databases-connections#prismaclient-in-long-running-applications

I think you bring up a point though that perhaps Sentry is being initialized before Prisma is. I updated my Sentry setup file to be a function that is passed the Prisma client, and now I can see the Prisma span.

Screenshot 2024-02-16 at 8 45 40 AM

Is this kind of pattern OK? Should I expect any issues with Sentry if I do Sentry.init inside of a function instead of right in the file itself?

export function initializeSentry(prisma: PrismaClient) {

    Sentry.init({
      debug: !!process.env.SENTRY_DEBUG,
      dsn: SENTRY_DSN,
      environment: IS_AZURE_PRODUCTION ? "production" : "staging",
      ignoreErrors: [
      ],
      integrations: [
        new ProfilingIntegration(),
        new Sentry.Integrations.Prisma({ client: prisma }),
        extraErrorDataIntegration({
          depth: 4
        }),
        Sentry.consoleIntegration(),
        new Sentry.Integrations.GraphQL()
      ],
      profilesSampleRate: 1.0,
      tracesSampleRate: 0.5
    });    
    console.log(`🔦 Sentry initialized`);

}
lforst commented 7 months ago

Interesting. Generally, it shouldn't matter from where you call Sentry.init() but you should call it as early as possible in your code because it can only collect errors that happen after it was called.

I am using Next.js deployed to vercel, so kinda serverless.

SheaBelsky commented 7 months ago

Interesting. Generally, it shouldn't matter from where you call Sentry.init() but you should call it as early as possible in your code because it can only collect errors that happen after it was called.

I am using Next.js deployed to vercel, so kinda serverless.

I'll keep playing with it, but my immediate issue is solved. Going to close this now. Thanks for your help!