-
Notifications
You must be signed in to change notification settings - Fork 341
Description
Tracer Version(s)
dd-trace-js#29704cf1d38586821323c4702e9d70ffca2250b6
Node.js Version(s)
22.15.0
Bug Report
Datadog APM Plugins Not Working in Next.js 15 - Missing Database & AWS Spans

Problem Summary
We're experiencing an issue where Datadog APM tracing only shows 2 spans (web.request
and next.request
) but completely misses database operations (Prisma) and AWS SDK calls (S3 pre-signed URLs). The plugins appear to not be hooking into the middleware properly.
Environment & Architecture Details
Next.js Configuration
- Next.js Version:
15.4.0-canary.84
- Node.js Version:
>=22.15.0
- Package Manager:
[email protected]
- Module Type: ESM (
"type": "module"
in package.json) - TypeScript:
5.8.3
with"moduleResolution": "Bundler"
Key Build Configuration
// next.config.ts
export default {
experimental: {
nodeMiddleware: true,
},
serverExternalPackages: [
'@datadog/native-metrics',
'@datadog/pprof',
'@datadog/native-appsec',
'@datadog/native-iast-taint-tracking',
'@datadog/native-iast-rewriter',
'graphql/language/visitor',
'graphql/language/printer',
'graphql/utilities'
],
webpack: (config, { isServer }) => {
if (isServer) {
config.plugins = [...config.plugins, new PrismaPlugin()];
}
if (!isServer) {
config.resolve.fallback = {
...config.resolve.fallback,
'dd-trace': false, // Excluded from client bundle
};
}
return config;
}
};
Datadog Dependencies
{
"dd-trace": "github:DataDog/dd-trace-js#29704cf1d38586821323c4702e9d70ffca2250b6",
"@prisma/instrumentation": "6.10.1",
"@opentelemetry/api": "1.8.0" // Overridden for compatibility
}
Database Setup (ZenStack + Prisma)
- Database: PostgreSQL via Prisma
- ORM: Prisma Client
6.10.1
with ZenStack2.16.0
enhancement layer - Schema: Git submodule (
prisma/
directory from separate repo) - Connection: Enhanced Prisma client with access control policies
// src/server/db/prisma.ts
const createBasePrismaClient = () => {
const client = new PrismaClient({
log: [{ emit: "event", level: "query" }],
});
// Cache invalidation extension applied
return client.$extends({
query: {
$allModels: {
async $allOperations({ operation, model, args, query }) {
const result = await query(args);
// Cache invalidation logic for user permissions
return result;
},
},
},
});
};
Instrumentation Setup
// instrumentation.ts
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
const isTracingEnabled = process.env.DD_TRACE_ENABLED === 'true' ||
process.env.DATADOG_TRACE_ENABLED === 'true' ||
process.env.NODE_ENV === 'production';
if (isTracingEnabled) {
const ddTrace = await import('dd-trace');
const tracerInstance = ddTrace.default.init({
service: process.env.DD_SERVICE || 'patrol6-web',
env: process.env.DD_ENV || process.env.NODE_ENV || 'development',
version: process.env.DD_VERSION || process.env.APP_VERSION || 'unknown',
sampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
runtimeMetrics: true,
logInjection: process.env.DD_LOGS_INJECTION === 'true',
startupLogs: process.env.DD_TRACE_STARTUP_LOGS === 'true',
plugins: true, // Auto-discovery enabled
// Agent configuration omitted for brevity
});
// Explicit plugin configuration
tracerInstance.use('http', { service: process.env.DD_SERVICE, splitByDomain: true });
tracerInstance.use('next', { service: process.env.DD_SERVICE });
tracerInstance.use('pg', { service: `${process.env.DD_SERVICE}-db`, dbmPropagationMode: 'full' });
tracerInstance.use('prisma', { service: `${process.env.DD_SERVICE}-db` });
tracerInstance.use('fetch', { service: process.env.DD_SERVICE, splitByDomain: true });
tracerInstance.use('aws-sdk', { service: `${process.env.DD_SERVICE}-aws` });
tracerInstance.use('undici'); // Node.js 18+ fetch
}
// Prisma instrumentation (OpenTelemetry)
const { PrismaInstrumentation, registerInstrumentations } = await import('@prisma/instrumentation');
registerInstrumentations({
instrumentations: [
new PrismaInstrumentation({
enabled: true,
middleware: true
}),
],
});
}
}
Build Process
- Build Command:
WEBPACK_CACHE_MEMORY_LIMIT=100000000 next build
- Experimental Build Modes: Using
--experimental-build-mode compile/generate
- Asset Building: Custom webpack configuration with Prisma plugin
- ESM Stages: Full ESM with
"moduleResolution": "Bundler"
What We've Tried
-
Multiple dd-trace initialization strategies:
- Auto-discovery (
plugins: true
) - Explicit plugin configuration
- Both OpenTelemetry + dd-trace hybrid approach
- Auto-discovery (
-
Different import patterns:
- Dynamic imports in
instrumentation.ts
- Static imports with conditional initialization
- Tried both
require()
andimport()
syntax - Using NODE_OPTIONS='--require dd-trace/init'
- Dynamic imports in
-
Plugin configuration variations:
- Enabled/disabled auto-discovery
- Explicit service naming per plugin
- Different sampling rates and configuration options
-
Prisma instrumentation approaches:
@prisma/instrumentation
with OpenTelemetry- dd-trace
prisma
plugin (using the latest commit https://github.com/DataDog/dd-trace-js/pull/5605/files?short_path=b575909#diff-b57590968a12cee85a37c1b91d8cc7092cd8b68e50b242c39a178121db82a797) - Both simultaneously
Expected vs Actual Behavior
Expected: Traces should show spans for:
- HTTP requests (
web.request
) ✅ - Next.js routing (
next.request
) ✅ - Database queries via Prisma (
prisma.*
orpg.*
) ❌ - AWS S3 operations (
aws-sdk.*
) ❌ - External HTTP calls (
fetch.*
orhttp.*
) ❌
Actual: Only getting the first two spans, missing all database and external service calls.
Questions for the Community
-
ESM Compatibility: Are there known issues with dd-trace plugin auto-discovery in Next.js 15 with full ESM?
-
Instrumentation Timing: Could the dynamic imports in
instrumentation.ts
be causing plugins to initialize after the modules they need to instrument? -
ZenStack Interference: Could the Prisma client extensions/enhancements be preventing dd-trace from properly hooking into database operations?
-
Next.js 15 Canary: Are there specific compatibility issues with the canary build we're using?
-
Module Resolution: Could
"moduleResolution": "Bundler"
be affecting how dd-trace discovers and loads plugins?
Any insights on what might be preventing the plugins from working would be greatly appreciated! We're particularly interested in understanding if this is a configuration issue, a compatibility problem, or if there are additional steps needed for Next.js 15 + ESM + complex database setups.
This post provides all the technical details someone would need to understand your specific setup and help troubleshoot the plugin issues. The combination of Next.js 15 canary, full ESM, ZenStack enhancements, and the specific build configuration creates a complex environment that might be causing the plugin discovery/hooking issues.
Reproduction Code
No response
Error Logs
No response
Tracer Config
No response
Operating System
No response
Bundling
Unsure