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

Compatibility of @sentry/astro with Cloudflare Pages #13217

Closed
mariusbolik opened this issue Aug 3, 2024 · 30 comments
Closed

Compatibility of @sentry/astro with Cloudflare Pages #13217

mariusbolik opened this issue Aug 3, 2024 · 30 comments
Assignees
Labels
Package: astro Issues related to the Sentry Astro SDK Package: cloudflare Issues related to the Sentry Cloudflare Workers SDK

Comments

@mariusbolik
Copy link

mariusbolik commented Aug 3, 2024

Problem Statement

I installed the @sentry/astro integration in my Astro Project:

import { defineConfig } from "astro/config";
import cloudflare from "@astrojs/cloudflare";
import sentry from "@sentry/astro";

export default defineConfig({
  output: "server",
  experimental: {
    actions: true,
  },
  adapter: cloudflare({}),
  integrations: [
    sentry({
      dsn: "https://18aba3b773c3ef1891e719e73a311ec4@o4505279164252160.ingest.us.sentry.io/4507712395214848",
      sourceMapsUploadOptions: {
        project: "pwa-astro",
        authToken: import.process.env.SENTRY_AUTH_TOKEN,
      },
    }),
  ],
});

But on build I receive this error:

11:16:47 [ERROR] [vite] x Build failed in 1.49s
[commonjs--resolver] [plugin vite:resolve] Cannot bundle Node.js built-in "node:http" imported from "node_modules/@sentry/node/build/esm/integrations/spotlight.js". Consider disabling ssr.noExternal or remove the built-in dependency.

So I extendet my astro config:

  vite: {
    ssr: {
      external: ["node:http"]
    },
  },

But then I get the next error:

[commonjs--resolver] [plugin vite:resolve] Cannot bundle Node.js built-in "node:fs" imported from "node_modules/@sentry/node/build/esm/integrations/contextlines.js". Consider disabling ssr.noExternal or remove the built-in dependency.

Solution Brainstorm

Cloudflare provides a sentry plugin: https://developers.cloudflare.com/pages/functions/plugins/sentry/
Is it possible that to use this plugin to make the Sentry/Astro integration compatible with CF Pages?
Maybe releated to: #12620

@andreiborza
Copy link
Member

Hello, thanks for reaching out.

Adding Cloudflare Pages support for Astro is on our roadmap and is actively being worked on. You can follow the progress in #12620.

As for making the plugin work, have you already tried it? It's not maintained by us so a bit hard to verify 😅. Maybe my colleague @AbhiPrasad has a bit more insight into this.

@mariusbolik
Copy link
Author

Thank you for coming back to me, @andreiborza. I tried the plugin, but it's not really compatible with the Astro middleware. The sentry plugin provided by cloudflare returns a response format that is not compatible with the response format that the astro middleware needs.

But I tried @sentry/cloudflare. It seems promissing so far. Sadly I also receive a typescript error. I don't know exactly how to change the requestHandlerOptions:

Image

Reproduction: https://stackblitz.com/edit/github-h3zdcy?file=src%2Fmiddleware.ts&on=stackblitz

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Aug 7, 2024
@andreiborza
Copy link
Member

@AbhiPrasad seems like astro's middleware does not provide waitUntil and passThroughOnException functions, see https://docs.astro.build/en/guides/middleware/#the-context-object. Can we fall back to our own flush?

@mariusbolik this might be something to file with Astro as well.

@AbhiPrasad
Copy link
Member

This should be passed through https://github.com/withastro/adapters/blob/cd4c0842aadc58defc67f4ccf6d6ef6f0401a9ac/packages/cloudflare/src/entrypoints/server.ts#L70

I guess it's called locals.runtime.ctx instead of context?

I'll be writing up a guide on this soon!

@mariusbolik
Copy link
Author

Types are still a problem here, but this seems to work:

  const requestHandlerOptions = {
    options: {
      dsn: '',
      tracesSampleRate: 1.0,
    },
    request: context.request,
    context: (context.locals as any).runtime.ctx,
  };
  return Sentry.wrapRequestHandler(requestHandlerOptions, () => next());

Thanks @AbhiPrasad!
Hopefully we can use the Astro/Sentry integration soon! I really like that it uploads the sourcemap files automatically :D

@mlafeldt
Copy link

@mariusbolik Thanks for the workaround!

const sentry = defineMiddleware(async (context, next) => {
  return wrapRequestHandler(
    {
      options: {
        dsn: import.meta.env.SENTRY_DSN,
        environment: import.meta.env.MODE,
        // tracesSampleRate: 1.0,
      },
      request: context.request,
      context: context.locals.runtime.ctx,
    },
    () => next()
  )
})

This works great using astro dev. 💥

However, astro build won't find waituntil when creating a production build (prior to deploying to Cloudflare Pages).

Cannot read properties of undefined (reading 'waitUntil')
  Stack trace:
    at file:///Users/mathias/devel/myapp/dist/_worker.js/_astro-internal_middleware.mjs:10133:23
    at async callMiddleware
...

Would you happen to have a solution for that too?

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Aug 26, 2024
@mlafeldt
Copy link

Ok, it looks like adding something like this does the trick:

if (typeof context.locals.runtime.ctx?.waitUntil !== 'function') {
  return next()
}

@AbhiPrasad
Copy link
Member

Hmm waitUntil should always be defined in the cloudflare runtime - let me dig in further to see why it's not being defined.

@mlafeldt
Copy link

mlafeldt commented Aug 27, 2024

Hmm waitUntil should always be defined in the cloudflare runtime - let me dig in further to see why it's not being defined.

It looks like context.locals.runtime.ctx is null when running astro build, which seems to be either a limitation of the Cloudflare adapter or Astro itself.

Another weirdness is that context.locals.runtime.env will include OS env vars with astro build, while it will only contain the variables from wrangler.toml and .dev.vars with astro dev. This leads to the unfortunate situation where you sometimes have to define env vars twice for some middlewares to work.

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Aug 27, 2024
@andreiborza
Copy link
Member

We're still looking into this, thanks for the extra info.

@mlafeldt
Copy link

mlafeldt commented Sep 6, 2024

@Lms24 Speaking of waitUntil, I noticed that Sentry.captureException won't work inside of it because the Sentry client injected via middleware might not be available in that scope. This happens, for example, when writing to D1:

Example:

Astro.locals.runtime.ctx.waitUntil(
  (async () => {
    const db = drizzle(Astro.locals.runtime.env.DB, { logger: true })
    await db.insert(usersTable).values(...)
      .catch((err) => {
        console.error(err)
        Sentry.captureException(err) // never captured because there's no Sentry client
      })
  })()
)

Sentry.captureException(...) // works fine outside of waitUntil

Logs:

Error: D1_ERROR: no such table: users: SQLITE_ERROR
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async eval (/Users/mathias/devel/myproject/src/pages/me.astro:31:9) {
  [cause]: Error: no such table: users: SQLITE_ERROR
      at D1Database._sendOrThrow (cloudflare-internal:d1-api:67:24)
      at async D1PreparedStatement.run (cloudflare-internal:d1-api:178:29) {
    [cause]: undefined
  }
}
Sentry Logger [warn]: No client configured on scope - will not capture exception!

What would be the best way to make the client available? Do I need to to construct a new one inside waitUntil perhaps?

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Sep 6, 2024
@Lms24
Copy link
Member

Lms24 commented Sep 6, 2024

Hmm @AbhiPrasad any ideas? I'm not familiar enough with waitUntil unfortunately

@mlafeldt
Copy link

Hmm @AbhiPrasad any ideas? I'm not familiar enough with waitUntil unfortunately

I would still appreciate any help here. @AbhiPrasad 🙏

Circling back to the original issue, I found out that @sentry/astro actually works on Cloudflare Pages incl. source map uploads with the following config:

export default defineConfig({
  integrations: [
    sentry({
      dsn: process.env.SENTRY_DSN,
      environment: import.meta.env.MODE,
      tracesSampleRate: 1.0,
      sourceMapsUploadOptions: {
        project: 'myproject',
        authToken: process.env.SENTRY_AUTH_TOKEN,
      },
      debug: true,
    }),
  ],
  output: 'hybrid',
  adapter: cloudflare({
    platformProxy: {
      enabled: true,
    },
    imageService: 'passthrough',
  }),
  vite: {
    ssr: {
      external: [
        'async_hooks',
        'diagnostics_channel',
        'events',
        'module',
        'node:child_process',
        'node:fs',
        'node:http',
        'node:https',
        'node:inspector',
        'node:net',
        'node:os',
        'node:path',
        'node:readline',
        'node:stream',
        'node:tls',
        'node:util',
        'node:worker_threads',
        'node:zlib',
        'path',
        'url',
        'util',
        'worker_threads',
      ],
    },
    build: {
      minify: false,
      sourcemap: true,
    },
  },
})

However, I had to patch node_modules/@sentry/browser/build/npm/esm/transports/fetch.js to remove referrerPolicy: 'origin' to fix this error:

Sentry Logger [error]: Error while sending event: Error: The 'referrerPolicy' field on 'RequestInitializerDict' is not implemented.

This suggests that the Sentry code somehow thinks it's running in a browser and not on the server.

Anyway, capturing traces and exceptions works, so proper CF support seems pretty close.

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Sep 11, 2024
@mlafeldt
Copy link

Last but not least, you can also combine these two options:

  • Use Sentry.wrapRequestHandler from @sentry/cloudflare in middleware.ts
  • Set up the Sentry Vite plugin for source map uploads (simply add this to your Astro config)

Which doesn't involve any hacks at all.

@andreiborza
Copy link
Member

@mlafeldt thanks for the workarounds/discussion, we'll take a look at get back to you.

@rodilo
Copy link

rodilo commented Oct 1, 2024

Last but not least, you can also combine these two options:

  • Use Sentry.wrapRequestHandler from @sentry/cloudflare in middleware.ts
  • Set up the Sentry Vite plugin for source map uploads (simply add this to your Astro config)

Which doesn't involve any hacks at all.

How did you manage to setup @sentry/cloudflare with Astro? Here is how i did it https://github.com/rodilo/sentry-cloudflare-middleware-test/blob/main/src/middleware/sentry-middleware.ts

App is built and deployed on cloudflare, but sentry doesn't seem to work this way. I've added a console.log in the middleware to check if ctx object has waitUntil and passThroughOnException but they were not there, so i guess it has something to do with that.
When i build application locally on my computer, those functions are there. I've also deployed locally build app to cloudflare with wrangler and sentry integration worked.

Any help is appreciated.

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Oct 1, 2024
@mlafeldt
Copy link

mlafeldt commented Oct 1, 2024

@rodilo Your middleware looks fine to me. I'd double-check the setup locally using wrangler pages dev - see https://docs.astro.build/en/guides/deploy/cloudflare/#enabling-preview-locally-with-wrangler (that whole guide is worth a read). I can't tell where you deploy the project (Cloudflare Pages?), but I'd check that ASTRO_SERVER_SENTRY_DSN is set in CI too.

@rodilo
Copy link

rodilo commented Oct 2, 2024

@rodilo Your middleware looks fine to me. I'd double-check the setup locally using wrangler pages dev - see https://docs.astro.build/en/guides/deploy/cloudflare/#enabling-preview-locally-with-wrangler (that whole guide is worth a read). I can't tell where you deploy the project (Cloudflare Pages?), but I'd check that ASTRO_SERVER_SENTRY_DSN is set in CI too.

In the end it was ASTRO_SERVER_SENTRY_DSN missing in my original repo, forgot to add it to astro config 🙈. Thanks for helping me realize that.

@AbhiPrasad
Copy link
Member

So we do have https://docs.sentry.io/platforms/javascript/guides/cloudflare/frameworks/astro/, which is not ideal because there's a bunch of manual steps but it gets stuff working for you.

We'll keep improving this, but closing this for now.

@AbhiPrasad AbhiPrasad added Package: astro Issues related to the Sentry Astro SDK Package: cloudflare Issues related to the Sentry Cloudflare Workers SDK labels Nov 1, 2024
@masylum
Copy link

masylum commented Dec 4, 2024

@AbhiPrasad I tried following the documentation with no success. How are you supposed to get that middleware working? AFAIK, Astro Cloudflare adapter does not support functions. Do you have a working repo?

@rodilo
Copy link

rodilo commented Dec 4, 2024

@masylum this works for me:

// eslint-disable-next-line import/no-extraneous-dependencies
import { wrapRequestHandler } from '@sentry/cloudflare';
import { defineMiddleware } from 'astro/middleware';

export const sentryMiddleware = defineMiddleware(async (context, next) =>
	wrapRequestHandler(
		{
			options: {
				dsn: import.meta.env.SENTRY_DSN,
				environment: import.meta.env.PUBLIC_ENVIRONMENT,
			},
			// @ts-expect-error Type for request from Astro and Cloudflare differ slightly as not all cf properties are available in Astro's type
			request: context.request,
			context: (context.locals as any).runtime.ctx,
		},
		() => next()
	)
);

@masylum
Copy link

masylum commented Dec 4, 2024

Awesome! I tried it and it works. Perhaps it would be worth updating that Sentry documentation since it's misleading. Next step: upload the source maps 😆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Package: astro Issues related to the Sentry Astro SDK Package: cloudflare Issues related to the Sentry Cloudflare Workers SDK
Projects
Archived in project
Development

No branches or pull requests

8 participants