Skip to content

Monitoring, Tracing & Logging

The project uses Sentry (or a Sentry-compatible alternative like Bugsink) for error reporting, tracing, and structured logging. Everything is already configured and ready to use.

Debugging in production can be painful. How many times have you been stuck in a situation where:

  • The client / manager has a hard time giving you reproduction steps or context
  • You can’t reproduce the issue locally
  • You can’t access or find the right logs (because they’re in a container that gets erased after deployment)

Good monitoring tools can save you hours, days, or even weeks of debugging. Thanks to OpenTelemetry, they’re now easier to set up and use.

Sentry is the de facto standard for error reporting with great UX and integrations with many frameworks. It can also be self-hosted. Plus, if you want to use an open source alternative, you can use Bugsink which is a Sentry-compatible alternative (without logging and tracing though).

OpenTelemetry has become the standard for logging and tracing. Sentry is compatible with it, and you can switch to alternatives like Signoz thanks to OTEL compatibility.

The monitoring stack provides three main capabilities:

  1. Error Reporting: Automatic capture of unhandled exceptions with full stack traces and context****
  2. Distributed Tracing: Performance monitoring with OpenTelemetry, tracking request flows across your application
  3. Structured Logging: Pino logs are automatically sent to Sentry for centralized log management

The monitoring system is initialized in apps/api/src/instrument.ts before the NestJS application starts. Under the hood, Sentry leverages OpenTelemetry to capture traces, its own SDK to track errors and Pino for logging.

We also use amplication/opentelemetry-nestjs to automatically trace controllers, services, database queries, etc. so we have complete traces.

The Sentry initialization in instrument.ts provides several important options:

src/instrument.ts
Sentry.init({
dsn: config.sentry.dsn,
environment: config.env,
release: config.version,
// Capture 100% of traces - reduce in production!
// Setting this above 0 automatically enables OpenTelemetry tracing.
tracesSampleRate: 1.0,
// Send default PII data (e.g., IP addresses)
sendDefaultPii: true,
// Enable structured logging via Pino
enableLogs: true,
integrations: [Sentry.pinoIntegration()],
// Prevents Sentry from claiming global provider, allowing shared TracerProvider
skipOpenTelemetrySetup: true,
})

Since Sentry is already configured in the boilerplate, you only need to:

  1. Create a project in Sentry (or your self-hosted instance) and get your DSN.

  2. Add the following to your apps/api/.env file:

    SENTRY_DSN=https://<your-dsn>@sentry.io/<your-project>
  3. That’s it! Errors, traces, and logs are automatically captured.

Unhandled exceptions are automatically captured and sent to Sentry with:

  • Full stack traces
  • Request context (URL, headers, body)
  • User information (if authenticated)
  • Environment and release information

Traces show the full request flow through your application:

Sentry traces

By default, OpenTelemetry doesn’t capture all method calls (only guards, controllers, DB queries, etc.). For more granular details, add the @Traceable() decorator at the class level:

import { Traceable } from '@amplication/opentelemetry-nestjs'
@Traceable()
@Injectable()
export class UserService {
constructor(private readonly userRepository: UserRepository) {}
}

Sentry traces with Traceable

On the left, you can see the added granularity (service methods are now visible), which makes performance analysis much easier.

Structured logs from Pino are automatically sent to Sentry:

Sentry logs

  1. Delete apps/api/src/instrument.ts

  2. Remove the Sentry and OpenTelemetry imports and configuration:

    src/app.module.ts
    // Remove these imports
    import { OpenTelemetryModule } from '@amplication/opentelemetry-nestjs'
    import { APP_FILTER } from '@nestjs/core'
    import { SentryGlobalFilter, SentryModule } from '@sentry/nestjs/setup'
    @Module({
    imports: [
    // Remove these two lines:
    OpenTelemetryModule.forRoot(),
    SentryModule.forRoot(),
    ],
    providers: [
    // Remove this provider:
    {
    provide: APP_FILTER,
    useClass: SentryGlobalFilter,
    },
    ],
    })
    export class AppModule {}
  3. If you added @Traceable() decorators to any services for granular tracing, remove them:

    // Remove @Traceable() and its import
    import { Traceable } from '@amplication/opentelemetry-nestjs'
    @Traceable() // Remove this line
    @Injectable()
    export class YourService {}
  4. Remove the following from apps/api/package.json:

    "@amplication/opentelemetry-nestjs",
    "@opentelemetry/sdk-trace-node",
    "@sentry/nestjs",
    "@sentry/opentelemetry",

    Then run pnpm install to update the lockfile.

  5. Remove from apps/api/.env and apps/api/.env.example:

    SENTRY_DSN=
  6. Remove the Sentry configuration from apps/api/src/config/env.config.ts:

    // Remove from configValidationSchema:
    SENTRY_DSN: z.string().optional(),
    // Remove from config object:
    sentry: {
    dsn: configParsed.data.SENTRY_DSN,
    },