Skip to content

Observability Optional

next-authnextjsprismadocker

Observability is essential for operating web services effectively. This template connects Next.js with OpenTelemetry, enabling you to trace through to the database layer using Prisma. In the local environment, the setup includes routing through an OpenTelemetry Collector and visualizing the traces in Jaeger.

For production environments, it is recommended to use the OpenTelemetry Collector as a sidecar and integrate with platforms such as Datadog for advanced monitoring and analysis.

The Tracing data of Top Page

otel

The SQL and Prisma

otel

OpenTelemetry

ts
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-grpc";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc";
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
import { Resource } from "@opentelemetry/resources";
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
import { NodeSDK } from "@opentelemetry/sdk-node";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
import { PrismaInstrumentation } from "@prisma/instrumentation";

const resource = new Resource({
  [ATTR_SERVICE_NAME]: "web-app-template",
  "deployment.environment": process.env.NODE_ENV,
});

const url = process.env.TRACE_EXPORTER_URL || /* for local */ undefined;

const traceExporter = new OTLPTraceExporter({
  url,
});

const spanProcessor = new BatchSpanProcessor(traceExporter);

const instrumentations = [
  new HttpInstrumentation(),
  new PrismaInstrumentation(),
];

const metricReader = new PeriodicExportingMetricReader({
  exporter: new OTLPMetricExporter(),
});

const sdk = new NodeSDK({
  resource,
  traceExporter,
  spanProcessor,
  instrumentations,
  metricReader,
});

sdk.start();
yml
volumes:
  db-data:

services:
  db:
    image: postgres:17
    ports:
      - ${DATABASE_PORT:-5432}:5432
    environment:
      - POSTGRES_USER=${DATABASE_USER}
      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
      - POSTGRES_DB=${DATABASE_DB}
  # start: otel #
  jaeger:
    image: jaegertracing/all-in-one
    ports:
      # https://www.jaegertracing.io/docs/1.6/getting-started/#all-in-one-docker-image
      # frontend
      - 16686:16686
      - 14268
      - 14250
    environment:
      - METRICS_STORAGE_TYPE=prometheus
      - PROMETHEUS_QUERY_SUPPORT_SPANMETRICS_CONNECTOR=true
      - COLLECTOR_OTLP_ENABLED=true
  # don't use contrib on prod
  # https://opentelemetry.io/blog/2024/otel-collector-anti-patterns/#3--not-using-the-right-collector-distribution-or-not-building-your-own-distribution
  otel-collector:
    image: otel/opentelemetry-collector-contrib
    command: ["--config=/conf/otel-collector-config.yml"]
    volumes:
      - ./otel-collector-config.yml:/conf/otel-collector-config.yml
    ports:
      - 13133:13133 # health_check extension
      - 4317:4317 # OTLP gRPC receiver
    depends_on:
      - jaeger
  # end: otel #