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

Cannot use session replays with tunneling in NextJS #7302

Closed
osdiab opened this issue Feb 28, 2023 · 6 comments
Closed

Cannot use session replays with tunneling in NextJS #7302

osdiab opened this issue Feb 28, 2023 · 6 comments
Labels
Package: nextjs Issues related to the Sentry Nextjs SDK Package: replay Issues related to the Sentry Replay SDK

Comments

@osdiab
Copy link

osdiab commented Feb 28, 2023

Environment

SaaS (https://sentry.io/)

Version

7.37.2

Link

doesn't show up on sentry at all

DSN

https://[email protected]/5714558

Steps to Reproduce

  • We set up Sentry tunneling with NextJS, it seems to work for all sentry requests except Replays calls.
  • we get an error {"detail":"invalid event envelope","causes":["missing newline after header or payload"]} when session replays are enabled and our site loads.
  • workaround is to set useCompression: false when setting up Replays.

the next.config.js relevant details:

const { withSentryConfig } = require("@sentry/nextjs");
const config = {
  // ...
  sentry: {
    hideSourceMaps: true,
    widenClientFileUpload: true,
    tunnelRoute: "/api/sentry",
  },
};

// ...

module.exports = withSentryConfig(
  nextTranspileModules(nextTypeSafePages(nextTranslate(config))),
  {
    silent: true,
    org: process.env.SENTRY_ORG,
    project: process.env.SENTRY_PROJECT,
    authToken: process.env.SENTRY_AUTH_TOKEN,
  }
);

the sentry.client.config.js:

import { init as sentryInit, getCurrentHub } from "@sentry/nextjs";
import { ExtraErrorData } from "@sentry/integrations";

import {
  runtimeEnvironment,
  RuntimeEnvironment,
} from "src/utilityruntime-environment";

const SENTRY_DSN = process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN;

sentryInit({
  dsn: SENTRY_DSN,
  tracesSampleRate: 0.2,

  // This sets the sample rate to be 10% for production, and 100% on dev.
  replaysSessionSampleRate:
    runtimeEnvironment === RuntimeEnvironment.DEV ? 1.0 : 0.1,
  // If the entire session is not sampled, use the below sample rate to sample
  // sessions when an error occurs.
  replaysOnErrorSampleRate: 1.0,

  environment: runtimeEnvironment,
  integrations: [new ExtraErrorData({ depth: 6 })],
  normalizeDepth: 6,
});

async function lazyLoadReplay() {
  const { Replay, Breadcrumbs } = await import("@sentry/nextjs");
  const client = getCurrentHub().getClient();
  if (!client.addIntegration) {
    console.log("unexpected, client had no addIntegration fn");
  }
  client.addIntegration(new Replay({ 
    // THIS IS THE WORKAROUND
    useCompression: false // necessary since Replays currently don't work with tunneling 
  }));
  if (runtimeEnvironment === RuntimeEnvironment.DEV) {
    client.addIntegration(new Breadcrumbs({ console: false }));
  }
}
void lazyLoadReplay().catch((error) =>
  console.log("could not lazy load replay", error)
);

the code in pages/api/sentry.ts:

import { jsonStringSchema } from "src/utility/json"; // a zod schema for identifying json strings
import type { NextApiHandler } from "next";
import { z } from "zod";

const handler: NextApiHandler = async (request, response) => {
  const host = "sentry.io";
  const projectId = process.env.SENTRY_PROJECT_ID;
  if (!projectId) {
    console.error({ message: "No sentry project set, not forwarding sentry" });
    return;
  }

  try {
    const body = String(request.body);
    const data = jsonStringSchema
      .pipe(
        z.object({
          dsn: z
            .string()
            .url()
            .transform((v) => new URL(v)),
        })
      )
      .parse(body.slice(0, body.indexOf("\n")));
    const foundProjectId = data.dsn.pathname.replace(/^\//, "");
    if (projectId !== foundProjectId) {
      console.error({
        message: "Not forwarding a log to Sentry from a bad DSN",
        data: { dsn: data.dsn.toString(), foundProjectId, projectId },
      });
      return;
    }
    const result = await fetch(`https://${host}/api/${projectId}/envelope/`, {
      method: "POST",
      headers: { "content-type": "application/x-sentry-envelope" },
      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
      body: request.body,
    });
    response.status(result.status);
    response.send(result.body);
  } catch (error) {
    console.error({
      message: "Could not parse Sentry envelope, could not proxy to sentry",
      error,
    });
    return;
  }
};
export default handler;

Expected Result

It doesn't error

Actual Result

network logs show Sentry is replying with {"detail":"invalid event envelope","causes":["missing newline after header or payload"]}

@getsantry
Copy link

getsantry bot commented Feb 28, 2023

Assigning to @getsentry/support for routing, due by Tuesday, February 28th at 5:00 pm (sfo). ⏲️

@getsantry
Copy link

getsantry bot commented Feb 28, 2023

Routing to @getsentry/team-web-sdk-frontend for triage, due by Thursday, March 2nd at 10:20 am (sfo). ⏲️

@lforst lforst transferred this issue from getsentry/sentry Feb 28, 2023
@lforst
Copy link
Member

lforst commented Feb 28, 2023

Hi, this probably doesn't work because replay uses a binary format for it's payloads and nextjs automatically bodyparses.

I recommend just using the tunnelRoute option. It will even allow you to get rid of that proxy route.

@lforst lforst added Status: Needs Information Package: nextjs Issues related to the Sentry Nextjs SDK Package: replay Issues related to the Sentry Replay SDK and removed Status: Untriaged labels Feb 28, 2023
@osdiab
Copy link
Author

osdiab commented Mar 1, 2023

Hi, are you saying that we don’t need to have the tunneling implementation in our code at all? Eg the /pages/api/sentry.ts file above, I should just delete that and it will work?

I’m just a little confused because the tunneling documentation gives all of these sample implementations for various languages for the proxying, so I thought that I needed to do that here too (and found it odd that it is up to the user to port Ruby code to the equivalent NextJS code lol). I guess it’s built into the NextJS library? If that’s the case it would be helpful for that to be in the documentation. Thanks!

@lforst
Copy link
Member

lforst commented Mar 1, 2023

Yeah, it makes the API route obsolete and it should just work. We built this for the Next.js SDK because the framework allows us to do it and we thought it was cool and useful.

@osdiab
Copy link
Author

osdiab commented Mar 2, 2023

got it, that's great! Yeah a note in the documentation would be nice, but it does seem to be working on my end. Closing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Package: nextjs Issues related to the Sentry Nextjs SDK Package: replay Issues related to the Sentry Replay SDK
Projects
None yet
Development

No branches or pull requests

2 participants