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

@sentry/nextjs not reporting server side errors on Netlify #3880

Closed
4 of 9 tasks
Yegair opened this issue Aug 8, 2021 · 4 comments
Closed
4 of 9 tasks

@sentry/nextjs not reporting server side errors on Netlify #3880

Yegair opened this issue Aug 8, 2021 · 4 comments
Labels
Package: nextjs Issues related to the Sentry Nextjs SDK Type: Bug

Comments

@Yegair
Copy link

Yegair commented Aug 8, 2021

Package + Version

  • @sentry/browser
  • @sentry/node
  • raven-js
  • raven-node (raven for node)
  • other: @sentry/nextjs

Version:

6.11.0

Description

I am currently trying to integrate Sentry into a Next.js application (version 11.0.1) running on Netlify
(using the official @netlify/plugin-nextjs in version 3.8.0).

I know that Netlify is not the "official" runtime for Next.js, but I think there are a lot of people out there running Next.js on Netlify.

On the client side everything works as expected, but when it comes to server side rendering or API routes, it seems that no events are sent to the Sentry API at all. On first glance it looked like the same issue as #3643, but that is only partially the case, as far as I can tell.

API Routes

In the case of API routes the problem seems to be very similar to #3643. The cause seems to be that Sentry.flush() is not getting called.

I spent some time trying to find the cause of this, and it seems like Sentry expects the ServerResponse.end method to always get called (see https://github.com/getsentry/sentry-javascript/blob/6.11.0/packages/nextjs/src/utils/withSentry.ts#L22). This seems to hold for Vercel deployments, but it is not the case for Netlify deployments.

From what I can tell, @netlify/plugin-nextjs wraps the Next.js API routes into a compatibility layer, so they can be executed within AWS Lambda. To achieve this the req and res parameters which are passed to the API route handler function are just wrappers for the underlying event that was received by the AWS lambda handler. There is no code which ensures the end method gets called on the response. Thus the Sentry events are not flushed before the function invocation terminates.

Have a look at the relevant code of @netlify/plugin-nextjs for more details: https://github.com/netlify/netlify-plugin-nextjs/blob/v3.8.0/src/lib/templates/getHandlerFunction.js#L166

Server Side Rendering (getServerSideProps)

It took me a while to understand, that instrumenting getServerSideProps is not supported as of now, but maybe someone having the same issue might benefit from what I discovered. In any case the documentation should be updated to reflect this, because server side rendering is an integral part of Next.js. People expect this to work, because otherwise the Sentry integration with Next.js is not nearly as useful as it could be.

The main problem is that the sentry.server.config.js snippet is not being injected into the code that is emitted by Next.js/webpack for SSR functions. As you can see here, it will only ever be injected into API routes and the custom App override: https://github.com/getsentry/sentry-javascript/blob/6.11.0/packages/nextjs/src/config/webpack.ts#L230.

I am not sure whether this also is a Netlify specific problem, because Netlify packages SSR functions just like API routes as standalone chunks of code with a small AWS lambda compatibility layer. Since the sentry.server.config.js snippet is not being injected there, these chunks of code can not call the Sentry API out of the box.

This can be fixed by a separate call to Sentry.init, but IMHO it would be better if the sentry.server.config.js would also be injected there.

Apart from that everything that I have written about API routes also applies to SSR functions on Netlify, because the AWS lambda compatibility layer that is being used is identical.

My Workaround

Unfortunately I can't just switch to Vercel, so I had to somehow get it to work on Netlify. I think I found a "dirty workaround" and I would be really glad if someone could provide some feedback, so I know whether it contains some major flaws that need to be fixed. After all I want to catch and handle the errors I already have and not create new ones.

1. Clear sentry.server.config.js

It doesn't fully work with Netlify, so I had to remove it. However, the Sentry webpack plugin complains if this file is missing, so I had to keep it, but I removed all content from it.

2. Write some code which catches errors and sends them to Sentry

The following code is based on https://github.com/getsentry/sentry-javascript/blob/6.11.0/packages/nextjs/src/utils/withSentry.ts but without relying on ServerResponse.end. I assume this code has some major issues, because otherwise ServerResponse.end wouldn't be used by @sentry/nextjs.

Also I removed all the tracing support, because all I need is error tracking.

import * as Sentry from '@sentry/nextjs';
import * as SentryUtils from '@sentry/utils';
import * as domain from 'domain';
import type { GetServerSideProps, NextApiHandler, NextApiRequest, NextApiResponse } from 'next';
import type { IncomingMessage, ServerResponse } from 'http';

// this file is not included on the client side, so we can do the server side Sentr initialization here
Sentry.init({
    dsn: process.env.SENTRY_DSN,
    tracesSampleRate: 1.0,
});

// a wrapper for getServerSideProps which uses Sentry to track errors
export function withSentrySSR(getServerSideProps: GetServerSideProps): GetServerSideProps {
    return async ctx => {
        return runWithSentry(ctx.req, ctx.res, async () => {
            return await getServerSideProps(ctx);
        });
    };
}

// a wrapper for API route handlers which uses Sentry to track errors
export function withSentry(handler: NextApiHandler): NextApiHandler {
    return async (req, res) => {
        return runWithSentry(req, res, async () => {
            return await handler(req, res);
        });
    };
}

async function runWithSentry<R>(
    req: IncomingMessage | NextApiRequest,
    res: ServerResponse | NextApiResponse,
    handler: () => Promise<R>,
): Promise<R> {
    const local = domain.create();
    local.add(req);
    local.add(res);

    const boundHandler = local.bind(async () => {
        const currentScope = Sentry.getCurrentHub().getScope();

        if (currentScope) {
            currentScope.addEventProcessor(event => Sentry.Handlers.parseRequest(event, req));
        }

        try {
            return await handler();
        } catch (err) {
            if (currentScope) {
                currentScope.addEventProcessor(event => {
                    SentryUtils.addExceptionMechanism(event, {
                        handled: false,
                    });
                    return event;
                });
                Sentry.captureException(err);
            }
            throw err;
        }
    });

    try {
        return await boundHandler();
    } finally {
        try {
            await Sentry.flush(2000);
        } catch (err) {
            console.warn(`Error while flushing Sentry events`, err);
        }
    }
}

Using this code I am now able to track errors which are thrown from API routes

import type { NextApiHandler } from 'next';
import { withSentry } from '../../sentry/withSentry';

const handler: NextApiHandler = async () => {
    throw new Error('API route failed');
};

export default withSentry(handler);

as well as errors that are thrown from getServerSideProps

import { withSentrySSR } from '../sentry/withSentry';

export const getServerSideProps = withSentrySSR(async () => {
    throw new Error('getServerSideProps failed');
});
@onurtemizkan
Copy link
Collaborator

onurtemizkan commented Aug 8, 2021

Thanks @Yegair, for reporting this. I'll try to reproduce your issue on Netlify.

Possibly related issue with Vercel: #3869 [Nope]

@Yegair
Copy link
Author

Yegair commented Aug 13, 2021

Small update:

previously I had written

There is no code which ensures the end method gets called on the response.

However, that is not true. The Netlify compatibility layer uses the end method to determine when Next.js is done, so they can return their response to the the AWS lambda runtime. So I think hooking into the end method is the best way to go after all. Still, I have no idea why it doesn't work 🤔

@lforst
Copy link
Member

lforst commented Jan 7, 2023

Is this still an issue with newer versions of the SDK?

@github-actions
Copy link
Contributor

This issue has gone three weeks without activity. In another week, I will close it.

But! If you comment or otherwise update it, I will reset the clock, and if you label it Status: Backlog or Status: In Progress, I will leave it alone ... forever!


"A weed is but an unloved flower." ― Ella Wheeler Wilcox 🥀

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Feb 6, 2023
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 Type: Bug
Projects
None yet
Development

No branches or pull requests

4 participants