Trovella Wiki

Observability Overview

Structured logging, error tracking, health checks, and production debugging in Trovella.

Trovella's observability stack has three pillars: structured logging via Pino, error tracking via Sentry, and health checks via the /api/health endpoint. Each pillar has a distinct role and they intentionally avoid duplicating each other's work.

Architecture

Browser               Server (Next.js)              Cloud
┌──────────┐         ┌─────────────────┐          ┌───────────────────┐
│ Sentry   │         │ Pino (stdout)   │────JSON──│ Cloud Logging     │
│ Replay   │         │   @repo/logger  │          │ (50 GB/mo free)   │
│ Client   │         ├─────────────────┤          ├───────────────────┤
│ SDK      │─/monitoring tunnel──────────────────>│ Sentry            │
│          │         │ Sentry Server   │────────> │ (5k errors/mo)    │
│          │         │ SDK             │          │                   │
└──────────┘         ├─────────────────┤          └───────────────────┘
                     │ /api/health     │
                     │ (DB+Redis+      │<──── Docker healthcheck
                     │  Typesense)     │<──── Uptime monitors
                     └─────────────────┘

Pillar Responsibilities

PillarToolWhat It CapturesFree Tier
Structured loggingPino -> Cloud LoggingAll server-side application logs (debug through error)50 GB/month
Error trackingSentryUnhandled exceptions, crashes, performance traces, session replays5,000 errors/month
Health checks/api/health endpointDatabase, Redis, and Typesense connectivity with latencyN/A

Division of Labor

The most important rule is: don't duplicate between Pino and Sentry.

  • Pino logs operational events you explicitly choose to record: request timing, state transitions, external API calls, background job lifecycle.
  • Sentry captures errors that escape your handling: unhandled exceptions, error boundary catches, request failures via onRequestError.
  • If an error will reach Sentry automatically (unhandled exception, error boundary), you do not need logger.error for it.
  • Use logger.error when you are handling an error and want operational visibility. Use Sentry.captureException() only when you handle an error but still want Sentry to know about it.

The @repo/logger Package

All logging flows through the @repo/logger wrapper package. No other package may import pino directly -- this is enforced by ESLint no-restricted-imports. The package sits in the infra layer of the dependency hierarchy:

app (apps/web)
  -> service (@repo/api, @repo/mcp)
    -> infra (@repo/cache, @repo/ai, @repo/auth, @repo/logger, @repo/search)
      -> core (@repo/db)
        -> leaf (@repo/utils, @repo/validators, @repo/design-tokens)

In tRPC routers, use ctx.logger rather than importing from @repo/logger directly. This is also ESLint-enforced in @repo/api.

  • Structured Logging -- Pino configuration, logger factories, Cloud Logging integration, and when to log what
  • Error Tracking -- Sentry SDK setup, instrumentation hooks, session replay, and the /monitoring tunnel
  • Health Checks -- the /api/health endpoint, Docker healthcheck integration, and degraded-state behavior
  • Local Debugging -- pino-pretty output, local Sentry behavior, and inspecting logs during development

Cross-Domain References

On this page