apm-agent-nodejs icon indicating copy to clipboard operation
apm-agent-nodejs copied to clipboard

Support for nextjs

Open jooj123 opened this issue 6 years ago • 2 comments

I am having trouble figuring out how to get elastic apm working with a hybrid express + nextjs app in an effective way.

It seems to pick up requests fine (to specific URLs) and it also picks up error codes and they show up in the elastic UI "transactions" tab. But I cant seem to get errors to show up in the "errors" tab.

If i throw an error in a next page - eg: ~/page/test.js Say I have:

function Test() {
  throw new Error('Blah');
  return (
    <p>
      Hello World
    </p>
  )
}

export default Test

It does not show up in the elastic UI.

Describe the solution you'd like I need exceptions that occur in nextjs to show up in the "error" tab in the elastic apm UI.

A hybrid express + nextjs app would look like this as an example: https://github.com/jooj123/test-nextjs

Describe alternatives you've considered

I have tired to send errors manually in the next page, via captureError: https://www.elastic.co/guide/en/apm/agent/nodejs/3.x/agent-api.html#apm-capture-error

But then i get a compile error like:

./node_modules/elastic-apm-node/lib/instrumentation/async-hooks.js
Module not found: Can't resolve 'async_hooks' in '/Users/martin.jujou/Documents/projects/fe-server-auction-results/node_modules/elastic-apm-node/lib/instrumentation'

> Build error occurred
Error: > Build failed because of webpack errors

This I believe is because nextjs pages are built for client and server and because elastic apm is a node client it blows up with the async_hooks error, much like: https://github.com/zeit/next.js/issues/4348

Not sure if you have any suggestions ...

For any exceptions at the express side of things i'm ok, but if an exception occurs in nextjs context im blind at the moment which is far from ideal

jooj123 avatar Feb 03 '20 11:02 jooj123

I ended up working around this by doing these steps:

In next.config.js:

webpack: (config, { isServer, webpack }) => {
      if (!isServer) {
        config.node = {
          dgram: 'empty',
          fs: 'empty',
          net: 'empty',
          tls: 'empty',
          child_process: 'empty',
        };

        // ignore apm (might use in nextjs code but dont want it in client bundles)
        config.plugins.push(
          new webpack.IgnorePlugin(/^(elastic-apm-node)$/),
        );
      }

      return config;
} 

Then in _error.js render func you can call captureError manually:


function Error({ statusCode, message, err }) {

const serverSide = typeof window === 'undefined';

  // only run this on server side as APM only works on server
  if (serverSide) {
    // my apm instance (imports elastic-apm-node and returns  captureError)
    const { captureError } = require('../src/apm'); 

    if (err) {
      captureError(err);
    } else {
      captureError(`Message: ${message}, Status Code: ${statusCode}`);
    }
  }

}

Not very elegant - if there is any other suggestions that would be helpful - thanks.

jooj123 avatar Feb 03 '20 13:02 jooj123

I've spent a little time learning Next.js finally. We'd have to add the basic instrumentation of the next module and docs around this as a first step to trace the next server-side actions. I think that should be relatively straightforward.

As stretch work, or for later/separate work, it might be interesting to see if we can use our newish Lambda support to support tracing of Next.js apps deployed to Vercel.

trentm avatar Jul 21 '22 23:07 trentm

I know this has been a while in coming. I'm getting close on my PR for this and came back here to sanity check that this case is covered. My PR has an example Next.js app setup with APM (https://github.com/elastic/apm-agent-nodejs/tree/trentm/feat-nextjs/examples/nextjs/).

  1. I added "pages/test.js" as above:
% cat pages/test.js
function Test() {
  throw new Error('Blah');
  return (
    <p>
      Hello World
    </p>
  )
}

export default Test
  1. I configured APM with my APM server URL (in "elastic-apm-node.js"). I ran the dev server, npm run dev, and then visited http://localhost:3000/test:
Screen Shot 2022-10-19 at 4 38 45 PM

The error is captured, and here is what I see in the Kibana APM app:

Screenshot 2022-10-19 at 16-40-26 GET _test - Transactions - my-app - Services - APM - Observability - Elastic

And here is the error group page:

Screen Shot 2022-10-19 at 4 41 39 PM

trentm avatar Oct 19 '22 23:10 trentm

Hi all, I know it has been a long time since this issue was opened. If you are a Next.js user who uses the Elastic APM agent (or is interested in doing so), I would love to hear feedback on the new Next.js server instrumentation in [email protected]. Thanks.

trentm avatar Nov 01 '22 18:11 trentm

@trentm Can you, please, tell me, how to connect it to nextjs in standalone mode? Without standalone mode, it worked well. With him - does not find the package, since standalone does not drag it into the bundle

mindyourlifeguide avatar May 09 '23 10:05 mindyourlifeguide

Can you, please, tell me, how to connect it to nextjs in standalone mode?

@mindyourlifeguide Hi! I assume you mean https://nextjs.org/docs/pages/api-reference/next-config-js/output

I was able to get it to work by making the following changes to the small example Next.js app here: https://github.com/elastic/apm-agent-nodejs/tree/main/examples/nextjs

diff --git a/examples/nextjs/next.config.js b/examples/nextjs/next.config.js
index ae887958..7eb7101a 100644
--- a/examples/nextjs/next.config.js
+++ b/examples/nextjs/next.config.js
@@ -2,6 +2,7 @@
 const nextConfig = {
   reactStrictMode: true,
   swcMinify: true,
+  output: 'standalone',
 }

 module.exports = nextConfig
diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json
index 45168c85..6d94b1a4 100644
--- a/examples/nextjs/package.json
+++ b/examples/nextjs/package.json
@@ -9,8 +9,8 @@
     "lint": "next lint"
   },
   "dependencies": {
-    "elastic-apm-node": "elastic/apm-agent-nodejs#main",
-    "next": "12.3.1",
+    "elastic-apm-node": "^3.45.0",
+    "next": "^12.3.1",
     "react": "18.2.0",
     "react-dom": "18.2.0"
   },
diff --git a/examples/nextjs/pages/api/hello.js b/examples/nextjs/pages/api/hello.js
index df63de88..10526995 100644
--- a/examples/nextjs/pages/api/hello.js
+++ b/examples/nextjs/pages/api/hello.js
@@ -1,5 +1,7 @@
 // Next.js API route support: https://nextjs.org/docs/api-routes/introduction

+import 'elastic-apm-node'
+
 export default function handler(req, res) {
   res.status(200).json({ name: 'John Doe' })
 }

The run:

npm install
npm run build
cd .next/standalone
node -r elastic-apm-node/start-next.js server.js

The trick was in adding import 'elastic-apm-node' in one of my page handlers. This resulted in Next.js's dependency scanning including elastic-apm-node and all of its modules.


However, a warning: when I tried this with [email protected] the built .next/standalone was totally bogus. Is standalone mode possibly broken in next@13? I didn't try [email protected] or [email protected] because elastic-apm-node doesn't currently support those versions (see https://github.com/elastic/apm-agent-nodejs/issues/3263).

trentm avatar May 09 '23 23:05 trentm

@trentm I have version 13.2.1. Without standalone mode, everything worked as it should. It won't start with it.

mindyourlifeguide avatar May 11 '23 11:05 mindyourlifeguide

I managed to get it to work via API route but I am getting an unknown route. usePathAsTransactionName: true does not add readability

/pages/api/hello.js

import apm from 'elastic-apm-node';

apm.start({
   ... my config from elastic-apm-node.js
});

I use: "elastic-apm-node": "3.45.0", "next": "13.2.1",

mindyourlifeguide avatar May 11 '23 20:05 mindyourlifeguide

See https://github.com/elastic/apm-agent-nodejs/pull/3073 for supporting usePathAsTransactionName: true.

I'd need more detail to try to understand why you are getting an unknown route.

/pages/api/hello.js

import apm from 'elastic-apm-node';

apm.start({
   ... my config from elastic-apm-node.js
});

You don't want to be starting the apm agent (apm.start(...)) twice. The agent is started in the -r elastic-apm-node/start-next.js module.

(At this point, please open a separate issue, otherwise this discussion is likely to get lost on a closed issue.)

trentm avatar May 11 '23 20:05 trentm