Skip to content
This repository has been archived by the owner on Aug 30, 2023. It is now read-only.

Out-of-box SSR support #246

Closed
kachkaev opened this issue Oct 18, 2022 · 13 comments Β· Fixed by #248
Closed

Out-of-box SSR support #246

kachkaev opened this issue Oct 18, 2022 · 13 comments Β· Fixed by #248
Assignees

Comments

@kachkaev
Copy link

kachkaev commented Oct 18, 2022

πŸ‘‹ folks! Alfa tester here πŸ§‘β€πŸ”¬ Curious to hear your thoughts on this one!

Environment

"@sentry/replay": "0.6.4-0"
"next": "12.3.1"

Steps to Reproduce

  1. Configure Sentry in a Next.js app using a wizard

  2. Reuse one sentryConfig in sentry.client.ts and sentry.server.ts for simplicity (e.g. by importing sentryConfig from sentry.client.ts to sentry.server.ts)

  3. Add new Replay() to a shared config

  4. Try next dev or next build

Expected Result

It just worksβ„’, i.e. new Replay() is a noop on the server

Actual Result

πŸ’₯

ReferenceError: window is not defined
    at isInternal (/path/to/project/node_modules/@sentry/replay/dist/index.js:158:56)
    at captureInternalException (/path/to/project/node_modules/@sentry/replay/dist/index.js:497:10)
    at new Replay (/path/to/project/node_modules/@sentry/replay/dist/index.js:1219:13)
    at eval (webpack-internal:///./sentry.client.config.ts:18:9)
    at Object../sentry.client.config.ts (/path/to/project/.next/server/pages/_app.js:142:1)
    at __webpack_require__ (/path/to/project/.next/server/webpack-runtime.js:33:42)
    at eval (webpack-internal:///./sentry.server.config.ts:4:79)
    at Object../sentry.server.config.ts (/path/to/project/.next/server/pages/_app.js:1319:1)
    at __webpack_require__ (/path/to/project/.next/server/webpack-runtime.js:33:42)
    at __webpack_exec__ (/path/to/project/.next/server/pages/_app.js:2402:39)
[LATE_SETUP_CALL] {}

Workaround

Do not reuse Sentry config, thus ensuring that new Replay() is only called in the client bundle. E.g.:

 integrations:
    typeof window === "undefined"
      ? []
      : [
          new Replay({
            // options
          }),
        ],

I’m fiddling with sentry-replay in blockprotocol/blockprotocol#682 – any tips welcome!

@ryan953
Copy link
Member

ryan953 commented Oct 18, 2022

Hey @kachkaev. This is on our radar for sure. I was trying to configure a Gatsby app on friday and this is one of the issues I hit as well. We'll bump the priority and work to get you unblocked!

@ryan953 ryan953 self-assigned this Oct 19, 2022
ryan953 added a commit that referenced this issue Oct 19, 2022
Guard @sentry/replay setup if `window` is not defined.

**Test Notes:**

I setup replays on my gatsby test app and was able to recreate the issue:

The config:
```typescript
#gatsby-config.ts

const config: GatsbyConfig = {
  ...
  plugins: [
    {
      resolve: "@sentry/gatsby",
    },
  ],
};
export default config;
```

```typescript
#sentry.config.ts

import * as Sentry from '@sentry/gatsby';
import { Replay } from "@sentry/replay";

Sentry.init({
  dsn: "https://[email protected]/6622945",
  sampleRate: 1.0, // Adjust this value in production
  integrations: [
    new Replay(),
  ],
});
```

The failing output:
```bash
There was an error compiling the html.js component for the development server.
See our docs page on debugging HTML builds for help https://gatsby.dev/debug-html ReferenceError: window is not defined


  1226 |         var _this = this;
  1227 |         // XXX: See method comments above
> 1228 |         window.setTimeout(function () { return _this.start(); });
       |         ^
  1229 |     };
  1230 |     /**
  1231 |      * Initializes the plugin.


  WebpackError: ReferenceError: window is not defined
```

After the fix we're able to build and run the app, and record replays!

| Test App | Replay |
| --- | --- |
| <img width="1156" alt="Screen Shot 2022-10-18 at 5 38 11 PM"
src="https://user-images.githubusercontent.com/187460/196570601-daf68ea5-25e4-4804-9365-9dab03ddf3a1.png">
| <img width="438" alt="Screen Shot 2022-10-18 at 5 37 09 PM"
src="https://user-images.githubusercontent.com/187460/196570323-85e7d204-b445-4457-aa2b-8af8ea613688.png">
|

In a gatsby app, or most of these other frameworks, it is possible to have a node-only or browser-only config. With atsby, for example, as a work around I would remove `new Replay()` from `sentry.config.ts` and instead create `gatsby-browser.tsx` with the following content which will to manually start the replay session:

```typescript
# gatsby-browser.tsx
const { Replay } = require("@sentry/replay");

exports.wrapRootElement = ({ element, props }) => {
  const replay = new Replay({
    blockAllMedia: false,
    maskAllText: false,
  });
  replay.start();

  return element;
};
```

Fixes #246
@kachkaev
Copy link
Author

I just upgraded from 0.6.4-0 to 0.6.4 but this did not help. Still getting ReferenceError: window is not defined when calling new Replay() in sentry.server.config.ts

kachkaev added a commit to blockprotocol/blockprotocol that referenced this issue Oct 19, 2022
@ryan953
Copy link
Member

ryan953 commented Oct 20, 2022

@kachkaev I think we got the wrong commit when we cut the release. Sorry about that.

The release is cut from 4eb6879

But that commit isn't on the main branch, on main we've got 77aed39 instead.

@billyvg Lets bump it again

@billyvg
Copy link
Member

billyvg commented Oct 20, 2022

@kachkaev I just published 0.6.5, let us know if that works.

@kachkaev
Copy link
Author

Thanks folks! I tried 0.6.5 in blockprotocol/blockprotocol#694 but it did not work. Not sure why, but when I removed typeof window !== "undefined" from my Sentry config, API routes broke on Vercel. See PR commits and corresponding CI checks / Vercel deployments. Maybe there is something else with 0.6.5 which breaks lambdas, IDK. I kept typeof window !== "undefined" and 0.6.4 for now.

@kachkaev
Copy link
Author

kachkaev commented Oct 20, 2022

I tried 0.6.5 in hashintel/hash#1232 without typeof window !== "undefined" and managed to see a proper stack trace on Vercel:

RequestId: a50261f7-38f0-4472-9ee8-29e369e66aaa Error: Runtime exited with error: exit status 1
Runtime.ExitError
2022-10-20T18:58:02.838Z	fcbab5bf-9c98-4c29-8173-e6b1c259d1b5	ERROR	ReferenceError: window is not defined
    at isInternal (/var/task/node_modules/@sentry/replay/dist/index.js:158:56)
    at captureInternalException (/var/task/node_modules/@sentry/replay/dist/index.js:497:10)
    at new Replay (/var/task/node_modules/@sentry/replay/dist/index.js:1220:13)
    at Object.6029 (/var/task/packages/hash/frontend/.next/server/pages/api/ory/[...paths].js:28:9)
    at __webpack_require__ (/var/task/packages/hash/frontend/.next/server/webpack-api-runtime.js:25:42)
    at __webpack_exec__ (/var/task/packages/hash/frontend/.next/server/pages/api/ory/[...paths].js:80:39)
    at /var/task/packages/hash/frontend/.next/server/pages/api/ory/[...paths].js:81:28
    at Object.&lt;anonymous&gt; (/var/task/packages/hash/frontend/.next/server/pages/api/ory/[...paths].js:84:3)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
RequestId: fcbab5bf-9c98-4c29-8173-e6b1c259d1b5 Error: Runtime exited with error: exit status 1

Pointing here:

export function isInternal() {
return ['sentry.io', 'dev.getsentry.net'].includes(window.location.host);
}

@billyvg
Copy link
Member

billyvg commented Oct 20, 2022

@ryan953 Ah

sentry-replay/src/index.ts

Lines 202 to 208 in 35903b7

if (_initialized) {
const error = new Error(
'Multiple Sentry Session Replay instances are not supported'
);
captureInternalException(error);
throw error;
}
-- that's not covered by isBrowser

@ryan953
Copy link
Member

ryan953 commented Oct 20, 2022

Yeah damnit. I quickly added a check to cover that spot: #253

I'm going to see if we can change the tsconfig and eslint to not allow browser api calls like this, and then we can opt-in each time. That way we'll have a chance to add these guards

@ryan953
Copy link
Member

ryan953 commented Oct 21, 2022

hashintel/hash#1232 is using 0.6.6, if that PR merges & deploys without issue i'll close this too.

@kachkaev
Copy link
Author

kachkaev commented Oct 21, 2022

I tried 0.6.6 in blockprotocol/blockprotocol#698 but had to bring back typeof window !== "undefined".

If new Replay() is called unconditionally, yarn build && yarn start + open /api/something gives this:

ready - started server on 0.0.0.0:3000, url: http://localhost:3000
info  - Loaded env from /path/to/app/.env.local
Error: Multiple Sentry Session Replay instances are not supported
    at new Replay (/path/to/app/node_modules/@sentry/replay/dist/index.js:1221:25)
    at Object.4074 (/path/to/app/.next/server/chunks/4486.js:282:9)
    at __webpack_require__ (/path/to/app/.next/server/webpack-api-runtime.js:25:42)
    at __webpack_exec__ (/path/to/app/.next/server/pages/api/me.js:215:39)
    at /path/to/app/.next/server/pages/api/me.js:216:94
    at Function.__webpack_require__.X (/path/to/app/.next/server/webpack-api-runtime.js:177:21)
    at /path/to/app/.next/server/pages/api/me.js:216:47
    at Object.<anonymous> (/path/to/app/.next/server/pages/api/me.js:219:3)
    at Module._compile (node:internal/modules/cjs/loader:1155:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1209:10)

@ryan953
Copy link
Member

ryan953 commented Oct 21, 2022

I'm glad it's a new error!

I'll spin up a nextjs app and dig into why it's instantiating the class twice. There could be implications for our regular sdk as well.

@ryan953
Copy link
Member

ryan953 commented Oct 24, 2022

So my basic nextjs site seems to work fine with the shared config across sentry.server.config.js and sentry.client.config.js

I tried checking out [email protected]:blockprotocol/blockprotocol.git and running the app. After setting up everything and running yarn build && yarn start I visited http://localhost:3000/api/me/api-keys on master branch and saw:

{"errors":[{"msg":"You must be logged in to perform this request"}]}

Checking out this branch I reproduced the issue. Seems like next.js imports the config all the time, so we're going to work on some possible solutions ranging from 'really naive' to 'too crazy'.

@kachkaev
Copy link
Author

kachkaev commented Oct 26, 2022

Thanks for trying out our repo @ryan953! I bumped @sentry/replay to 0.6.8 in blockprotocol/blockprotocol#698 and it worked! Not sure if there was an issue with 0.6.6 or I just did something wrong in the PR, but I guess it no longer matters.

It’s great that typeof window !== "undefined" is no longer needed πŸŽ‰

mydea pushed a commit to getsentry/sentry-javascript that referenced this issue Nov 23, 2022
…eplay#248)

Guard @sentry/replay setup if `window` is not defined.

**Test Notes:**

I setup replays on my gatsby test app and was able to recreate the issue:

The config:
```typescript
getsentry/sentry-replay#gatsby-config.ts

const config: GatsbyConfig = {
  ...
  plugins: [
    {
      resolve: "@sentry/gatsby",
    },
  ],
};
export default config;
```

```typescript
getsentry/sentry-replay#sentry.config.ts

import * as Sentry from '@sentry/gatsby';
import { Replay } from "@sentry/replay";

Sentry.init({
  dsn: "https://[email protected]/6622945",
  sampleRate: 1.0, // Adjust this value in production
  integrations: [
    new Replay(),
  ],
});
```

The failing output:
```bash
There was an error compiling the html.js component for the development server.
See our docs page on debugging HTML builds for help https://gatsby.dev/debug-html ReferenceError: window is not defined


  1226 |         var _this = this;
  1227 |         // XXX: See method comments above
> 1228 |         window.setTimeout(function () { return _this.start(); });
       |         ^
  1229 |     };
  1230 |     /**
  1231 |      * Initializes the plugin.


  WebpackError: ReferenceError: window is not defined
```

After the fix we're able to build and run the app, and record replays!

| Test App | Replay |
| --- | --- |
| <img width="1156" alt="Screen Shot 2022-10-18 at 5 38 11 PM"
src="https://user-images.githubusercontent.com/187460/196570601-daf68ea5-25e4-4804-9365-9dab03ddf3a1.png">
| <img width="438" alt="Screen Shot 2022-10-18 at 5 37 09 PM"
src="https://user-images.githubusercontent.com/187460/196570323-85e7d204-b445-4457-aa2b-8af8ea613688.png">
|

In a gatsby app, or most of these other frameworks, it is possible to have a node-only or browser-only config. With atsby, for example, as a work around I would remove `new Replay()` from `sentry.config.ts` and instead create `gatsby-browser.tsx` with the following content which will to manually start the replay session:

```typescript
getsentry/sentry-replay# gatsby-browser.tsx
const { Replay } = require("@sentry/replay");

exports.wrapRootElement = ({ element, props }) => {
  const replay = new Replay({
    blockAllMedia: false,
    maskAllText: false,
  });
  replay.start();

  return element;
};
```

Fixes getsentry/sentry-replay#246
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants