Sentry is an application monitoring platform that helps developers track errors, performance issues, and application health in real-time. It provides:

  1. Error Monitoring: Captures uncaught exceptions and manually reported errors with detailed stack traces, context, and breadcrumbs.
  2. Performance Monitoring: Tracks performance metrics like request latency, transaction durations, and slow API calls using distributed tracing.
  3. Session Replay: Records user sessions to help reproduce issues (not supported in Cloudflare Workers due to runtime limitations).
  4. Logging: Captures logs and custom events for debugging.
  5. Source Maps: Maps minified production code to your source code for readable stack traces.
  6. Release Tracking: Associates errors with specific code releases for easier debugging.

Sentry supports JavaScript environments, including Node.js, browsers, and serverless platforms like Cloudflare Workers, through dedicated SDKs.


Key Concepts of Sentry

  1. DSN (Data Source Name): A unique identifier for your Sentry project, used to send events to Sentry. It looks like https://<key>@o<org_id>.ingest.sentry.io/<project_id>.
  2. Breadcrumbs: A trail of events (e.g., user actions, HTTP requests) leading up to an error, providing context.
  3. Integrations: Plugins like browserTracingIntegration or consoleLoggingIntegration that extend Sentry’s functionality.
  4. Tracing: Tracks performance by creating spans for operations like HTTP requests or database queries.
  5. Source Maps: Maps minified code to source code, crucial for debugging in production.
  6. Crons Monitoring: Monitors scheduled tasks (e.g., Cloudflare Workers’ cron triggers).
  7. Experiments: Features like logging (enableLogs) that are in beta.

For Cloudflare Workers, Sentry provides the @sentry/cloudflare SDK, designed to work with the Workers runtime, which differs from Node.js and browser environments due to its V8-based, serverless nature.


Challenges with Next.js + Cloudflare Workers

Cloudflare Workers use the workerd runtime, which has limitations compared to Node.js:

  • No full Node.js API: Only a subset of Node.js APIs is available via compatibility flags (nodejs_compat or nodejs_als).
  • Event Loop Constraints: Asynchronous tasks (e.g., Sentry’s event sending) may be canceled if not handled properly, requiring event.waitUntil() to ensure completion.
  • Middleware Limitations: Next.js middleware running on Workers has issues with top-level async operations (e.g., randomUUID), which can conflict with Sentry’s initialization.
  • Source Maps: Workers’ minified code requires source maps for readable stack traces, which need special handling.

The @sentry/nextjs SDK is optimized for Node.js-based Next.js deployments (e.g., Vercel), but it’s not fully compatible with Cloudflare Workers due to runtime differences. Instead, you’ll use @sentry/cloudflare and configure it to work with Next.js.


Step-by-Step Integration Guide

Here’s how to integrate Sentry with a Next.js + TypeScript project deployed on Cloudflare Workers using the @opennextjs/cloudflare adapter.

1. Set Up a Sentry Project

  • Log in to Sentry.io and create a new project.
  • Choose JavaScript as the platform (Cloudflare Workers isn’t listed explicitly but is supported via the @sentry/cloudflare SDK).
  • Note the DSN provided by Sentry for your project.

2. Install Dependencies

In your Next.js project, install the Sentry Cloudflare SDK and TypeScript types:

npm install @sentry/cloudflare @cloudflare/workers-types

If you’re using the nodejs_compat flag in your wrangler.toml, also install Node.js types:

npm install -D @types/node

3. Configure Wrangler for TypeScript and Compatibility

Ensure your wrangler.toml is set up to support TypeScript and Sentry’s requirements:

name = "your-worker-name"
compatibility_date = "2024-09-23" # Or later
compatibility_flags = ["nodejs_als"] # Required for Sentry's AsyncLocalStorage
main = "worker.js" # Entry point for your Worker

[build]
command = "npx @opennextjs/cloudflare"

[build.upload]
format = "modules"
  • Run wrangler types to generate TypeScript types for your Worker’s bindings and runtime, which will create a worker-configuration.d.ts file.
  • Update your tsconfig.json to include the generated types:
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "strict": true,
    "types": ["@cloudflare/workers-types", "./worker-configuration.d.ts"]
  }
}

4. Initialize Sentry in Your Worker

Since Next.js on Cloudflare Workers uses the @opennextjs/cloudflare adapter, you’ll wrap your Worker’s handler with Sentry. Create or modify the Worker entry point (e.g., worker.ts) to initialize Sentry.

Example worker.ts:

import * as Sentry from '@sentry/cloudflare';
import { withSentry } from '@sentry/cloudflare';
import { createRequestHandler } from '@opennextjs/cloudflare';

// Initialize your Next.js app
const app = createRequestHandler({
  // Your OpenNext configuration
});

// Sentry configuration
const sentryOptions = {
  dsn: process.env.SENTRY_DSN || 'https://<your-dsn>@o0.ingest.sentry.io/<project-id>',
  tracesSampleRate: 1.0, // Capture 100% of transactions for performance monitoring
  sendDefaultPii: true, // Include request headers and IP (be cautious with PII)
  release: process.env.CF_VERSION_METADATA?.id, // Use Cloudflare's version metadata
  _experiments: {
    enableLogs: true, // Enable logging to Sentry
  },
};

// Wrap the Next.js handler with Sentry
export default withSentry(sentryOptions, app);
  • DSN: Replace with your Sentry project’s DSN.
  • tracesSampleRate: Set to 1.0 for full tracing or lower (e.g., 0.2) to sample fewer transactions.
  • release: Use Cloudflare’s version metadata for release tracking.
  • enableLogs: Enables console logs to be sent to Sentry.

5. Handle Middleware (Optional)

If your Next.js app uses middleware (e.g., middleware.ts), you may encounter issues with Sentry’s initialization due to top-level async operations. To work around this, use wrapRequestHandler instead of sentryPagesPlugin.

Example middleware.ts:

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import * as Sentry from '@sentry/cloudflare';
import { getRequestContext } from '@cloudflare/next-on-pages';

export async function middleware(request: NextRequest) {
  const context = getRequestContext();
  const requestHandlerOptions = {
    options: {
      dsn: context.env.SENTRY_DSN,
      tracesSampleRate: 1.0,
    },
    request,
    context: context.platform.ctx,
  };

  return Sentry.wrapRequestHandler(requestHandlerOptions, () => NextResponse.next());
}
  • getRequestContext: Accesses Cloudflare’s environment and context.
  • wrapRequestHandler: Ensures Sentry captures errors in middleware without top-level async issues.

6. Upload Source Maps

To make stack traces readable, upload source maps to Sentry. Configure your wrangler.toml to enable source map uploading:

[build.upload]
source_maps = true

Use the Sentry CLI to upload source maps during deployment:

npm install -g @sentry/cli
SENTRY_AUTH_TOKEN=<your-auth-token> sentry-cli sourcemaps upload --org <your-org> --project <your-project> ./dist
  • Get your SENTRY_AUTH_TOKEN from Sentry’s settings.
  • Ensure your build process generates source maps (e.g., set sourceMap: true in your Webpack config if using a custom setup).

7. Capture Manual Events

You can manually capture errors, messages, or breadcrumbs in your Next.js components or API routes:

import * as Sentry from '@sentry/cloudflare';

// In an API route or server-side function
export async function GET(request: Request) {
  try {
    // Your logic
    Sentry.addBreadcrumb({ message: 'Fetching data', level: 'info' });
    throw new Error('Something went wrong');
  } catch (error) {
    Sentry.captureException(error);
    return new Response('Error occurred', { status: 500 });
  }
}

8. Instrument Cloudflare Bindings

If your app uses Cloudflare bindings (e.g., D1 database, R2 storage), instrument them with Sentry:

import * as Sentry from '@sentry/cloudflare';

// Instrument D1 database
const db = Sentry.instrumentD1WithSentry(env.DB);
await db.prepare('SELECT * FROM table WHERE id = ?').bind(1).run();

9. Monitor Cron Jobs

If your Worker uses scheduled tasks, use withMonitor for cron monitoring:

export default {
  async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext) {
    ctx.waitUntil(
      Sentry.withMonitor('my-cron-job', async () => {
        // Your cron logic
      }, {
        schedule: { type: 'crontab', value: '* * * * *' },
        checkinMargin: 2,
        maxRuntime: 10,
      })
    );
  },
};

10. Deploy and Test

  • Deploy your Worker using wrangler deploy.
  • Test Sentry by throwing an intentional error (e.g., add throw new Error('Test error') in an API route).
  • Check Sentry’s dashboard to verify errors, performance traces, and logs.

11. Troubleshooting

  • Errors not captured: Ensure nodejs_als or nodejs_compat is set in wrangler.toml. Use event.waitUntil() for async Sentry operations.
  • Middleware issues: Avoid top-level async code in middleware.ts. Use wrapRequestHandler as shown above.
  • Unreadable stack traces: Verify source maps are uploaded and match your deployed code.
  • Performance: Set tracesSampleRate to a lower value (e.g., 0.2) in production to reduce overhead.

Best Practices

  • Environment Variables: Store SENTRY_DSN in wrangler.toml or Cloudflare’s dashboard to avoid hardcoding.
  • Minimal PII: Disable sendDefaultPii if you don’t need user IPs or headers to comply with privacy regulations.
  • Optimize Tracing: Adjust tracesSampleRate based on your traffic to balance monitoring and cost.
  • CI/CD Integration: Automate source map uploads in your deployment pipeline using Sentry CLI.
  • Testing Locally: Use wrangler dev to test your Worker locally and verify Sentry integration.

Limitations

  • Session Replay: Not supported in Cloudflare Workers due to runtime constraints.
  • Middleware Compatibility: @sentry/nextjs middleware integration may not work due to randomUUID issues. Use @sentry/cloudflare’s wrapRequestHandler.
  • Tail Workers: Cloudflare’s Sentry integration via Tail Workers is not supported for Pages projects and is limited to error reporting.

References

Comments