OpenTelemetry setup in NextJS with AppRouter and Azure Monitor Application Insights

OpenTelemetry setup in NextJS with AppRouter and Azure Monitor Application Insights

OpenTelemetry is an open-source observability framework for generating, collecting, and exporting telemetry data (metrics, logs, and traces) for analysis to understand your software's performance and behavior. You can analyze them using Prometheus, Jaeger, and other observability tools. It's a CNCF-incubated project and is the second most contributed project in the CNCF landscape, second only to Kubernetes.

The main advantage of using OpenTelemetry is that it provides a vendor-neutral way to collect telemetry data. This means you can switch your observability platform without having to change your application code.

In this post, I will guide you through the process of setting up OpenTelemetry in a NextJS application with AppRouter and exporting the telemetry data to Azure Monitor Application Insights.

Setting up OpenTelemetry in NextJS with AppRouter

It is easiest to set up using the @vercel/otel library because it supports exporting to 3rd parties and results in the least amount of code. This will also work with self-hosted setups in Docker (e.g. Azure Container Apps), Azure App Service, PM2, etc.

Read the official docs at https://vercel.com/docs/observability/otel-overview.

Installing packages

As per the official docs, you need to install @vercel/otel and @opentelemetry/api. To support Azure Monitor Application Insights, you also need to install @azure/monitor-opentelemetry-exporter.

Notes:

  • The @azure/monitor-opentelemetry-exporter is in beta
  • The @vercel/otel package relies on older versions of @opentelemetry/api, so you may need to control the versions by overriding or installing the relied-upon version.

In my case, I run the following command (versions may change later):

pnpm install @vercel/otel @opentelemetry/api@1.7.0 @azure/monitor-opentelemetry-exporter@next --save-exact

Instrumentation file

To set up OpenTelemetry in NextJS, you need to create an instrumentation.ts file at the root of your project. Here's how your instrumentation.ts file should look like:

import { AzureMonitorTraceExporter } from '@azure/monitor-opentelemetry-exporter';
import { registerOTel } from '@vercel/otel';
 
export async function register() {
  registerOTel({
    serviceName: 'your-project-name',
    traceExporter: new AzureMonitorTraceExporter({
      connectionString: 'InstrumentationKey=<...>;IngestionEndpoint=https://...;LiveEndpoint=https://...',
      // you can read from ENV if you prefer to
      // connectionString: process.env.APP_INSIGHTS_CONNECTION_STRING,
    }),
  });
}

In this file, we're calling registerOTel which handles the setup of OpenTelemetry for NodeJS and specific elements for NextJS. Setting the traceExporter becomes the next important thing.

Remember to set the correct value for connectionString as copied from the Azure Portal.

Enabling instrumentation

As of the time of this writing, instrumentation using the instrumentation.ts file is experimental (see docs). You can enable it in your next.config.ts by setting experimental.instrumentationHook = true

/** @type {import('next').NextConfig} */
const config = {
  experimental: {
    instrumentationHook: true,
  },
  // other config
};
 
export default config;

Nuances of the setup

At this point, you will start to get errors on the build. The first one will read Module not found: Can't resolve 'os' then subsequently be followed by fs, child_process, and path modules. These are Webpack warnings and my suspicion is they will disappear when @azure/monitor-opentelemetry-exporter is out of beta or when we start using turbopack in place of Webpack.

Sentry guides us on how to solve this at https://sentry.io/answers/module-not-found-nextjs/#how-to-resolve, but we need to fix this on the server side because that is where the telemetry is collected. I changed my next.config.js file to have the following Webpack config.

/** @type {import('next').NextConfig} */
const config = {
  experimental: {
    instrumentationHook: true,
  },
  webpack: (config, { isServer }) => {
    if (isServer) {
      // required for @azure/monitor-opentelemetry-exporter to work
      config.resolve.fallback ??= {};
      config.resolve.fallback.os = false;
      config.resolve.fallback.fs = false;
      config.resolve.fallback.child_process = false;
      config.resolve.fallback.path = false;
    }
    return config;
  },
  // other config
};
 
export default config;

Conclusion

With this setup, your NextJS application with AppRouter is now ready to export telemetry data to Azure Monitor Application Insights. Run your app and navigate across several pages for telemetry to be sent to Azure. Remember that it can take a few minutes for the data to show up on Azure Portal, usually two minutes.

If you like this work, feel free to sponsor my work here.