Skip to content

Local Development

Locally, every service exports OTLP/gRPC to a .NET Aspire Dashboard container. The dashboard is included in the backend and monitoring Docker Compose profiles, so it starts automatically with pnpm dev:backend. See the component map for the full topology.

Prerequisites

This page assumes you have the local stack running. See Local Development — Getting Started for the pnpm dev:* scripts and Local Development — Docker Compose for the underlying service topology.

Configuration

Each service has its OTel env in docker-compose.yml:

yaml
environment:
  OTEL_EXPORTER_OTLP_ENDPOINT: http://aspire-dashboard:18889
  OTEL_EXPORTER_OTLP_PROTOCOL: grpc
  OTEL_SERVICE_NAME: <service-name>
  OTEL_RESOURCE_ATTRIBUTES: service.namespace=feyenoord
  OTEL_TRACES_SAMPLER_ARG: "1.0" # 100% locally — opt out per service if noisy

Aspire runs unsecured (DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS=true) so the local OTLP endpoint accepts plaintext with no auth. Don't expose it beyond the local Docker network.

Using the dashboard

Open http://localhost:18888. Three tabs matter:

TabWhat you'll find
StructuredPino logs from every service, plus exception log records emitted by recordException
TracesDistributed traces grouped by trace ID, with each span's service, duration, and status
MetricsEmpty by design — metrics are not exported

Filter by service with the Resource dropdown. Free-text search over message and attributes is in the Filter box (exception.type=BaseError, etc.). For exception records, the trace link in each row jumps to the matching distributed trace.

Troubleshooting

Resource shows unknown_service:node — the service was started without the shared Resource from @repo/observability. Make sure the app imports its local instrumentation.ts before application modules and was rebuilt after pulling.

No traces from a service — most likely OTEL_TRACES_SAMPLER_ARG=0 is set in docker-compose.yml for that container. Logs and exceptions still flow (they're not sampled), but traces are dropped at the root. Set OTEL_TRACES_SAMPLER_ARG: "1.0" to see traces locally.

No data at all — verify the container can reach Aspire:

bash
docker exec feyenoord-api-1 node -e "
  const s = new (require('net').Socket)();
  s.on('connect', () => { console.log('OK'); s.end() });
  s.on('error', e => console.log('ERR', e.message));
  s.connect(18889, 'aspire-dashboard');
"

If this prints OK, the network is fine — check docker logs feyenoord-aspire-dashboard-1 for rejected exports.

Aspire data resets on restart — by design, the dashboard stores telemetry in memory.