Monitoring & Alerting
This guide covers the baseline observability practices we use across projects. Different projects have different monitoring needs — Sentry and structured logging are our standard everywhere, while more advanced tooling (APM, custom dashboards, detailed alerting) is set up per project based on its requirements and is not documented here.
Error Tracking
Sentry
Sentry is our primary error tracking tool for both backend and frontend applications. It's present in almost every project.
Backend Setup (NestJS)
// main.ts
import * as Sentry from '@sentry/node'
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.2 : 1.0,
})
// shared/filters/sentry-exception.filter.ts
@Catch()
export class SentryExceptionFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
Sentry.captureException(exception)
// ... pass to default exception handler
}
}
Frontend Setup (Vue / Nuxt)
// plugins/sentry.client.ts
import * as Sentry from '@sentry/vue'
export default defineNuxtPlugin((nuxtApp) => {
Sentry.init({
app: nuxtApp.vueApp,
dsn: useRuntimeConfig().public.sentryDsn,
environment: useRuntimeConfig().public.environment,
tracesSampleRate: 0.2,
})
})
Best Practices
- Set meaningful
environmentandreleasetags - Add user context when available (
Sentry.setUser({ id, email })) - Use breadcrumbs to trace the sequence of events leading to an error
- Filter out expected errors (4xx responses, validation errors) to reduce noise
Logging
We use Pino for structured JSON logging. Pino is fast, low-overhead, and works well with NestJS.
Setup
// main.ts
import { LoggerModule } from 'nestjs-pino'
@Module({
imports: [
LoggerModule.forRoot({
pinoHttp: {
level: process.env.LOG_LEVEL || 'info',
transport: process.env.NODE_ENV !== 'production'
? { target: 'pino-pretty' }
: undefined,
},
}),
],
})
export class AppModule {}
Key Guidelines
- Use structured JSON logging in production (Pino does this by default)
- Include correlation/request IDs for tracing requests across services
- Log at appropriate levels:
errorfor failures,warnfor degraded behavior,infofor significant events,debugfor development - Never log sensitive data (passwords, tokens, PII)
- Use
pino-prettyin development for readable output
Log Aggregation
- GCP projects: Logs are automatically collected by Google Cloud Logging when output as JSON to stdout
- DO projects: Use Digital Ocean's built-in log viewer or forward logs to a centralized service
See Error Handling & Logging for more patterns on structured logging and error context.
Health Checks
Every application exposes a health check endpoint:
// health/health.controller.ts
@Controller('health')
export class HealthController {
constructor(
private health: HealthCheckService,
private db: TypeOrmHealthIndicator,
) {}
@Get()
check() {
return this.health.check([
() => this.db.pingCheck('database'),
])
}
}
Health checks are used by:
- Load balancers to route traffic to healthy instances
- Container orchestrators to restart unhealthy containers
- Uptime monitors to detect outages
Alerting
Alert Channels
- Slack — Primary channel for all alerts
- Email — Backup for critical alerts
What to Alert On
- Application errors spiking above baseline (Sentry alerts)
- Health check failures
- High response time sustained over several minutes
- Resource usage thresholds (CPU, memory)
Avoiding Alert Fatigue
- Only alert on actionable conditions — if no one needs to act, it's a log, not an alert
- Group related errors in Sentry (deduplication)
- Set reasonable thresholds — occasional 500 errors happen, only alert on sustained issues
- Review and tune alert rules periodically