diff --git a/docker-compose.yml b/docker-compose.yml index d311691..d1e7e0e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -67,6 +67,8 @@ services: Ai__Mode: ${AI_MODE:-Disabled} # Points at the compose 'ollama' service when the ai profile is up; harmless otherwise. Ai__OllamaBaseUrl: ${OLLAMA_BASE_URL:-http://ollama:11434} + # OTLP export activates only when set (e.g. http://lgtm:4317 with the observability profile). + OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_ENDPOINT:-} # Dev mode shows the dev banner and caps the initial sync. Set DEV_MODE=true # and MAX_MESSAGES=1000 in deploy/.env to exercise it in this Docker setup. App__DevMode: ${DEV_MODE:-false} @@ -109,6 +111,16 @@ services: # count: all # capabilities: [gpu] + # Observability (RECOMMENDATIONS #5): all-in-one Grafana+Tempo+Prometheus+Loki. + # Enable with: docker compose --profile observability up -d + # then set OTEL_ENDPOINT=http://lgtm:4317 in deploy/.env and restart the api. + # Grafana UI: http://localhost:3000 (admin/admin on first run). + lgtm: + image: grafana/otel-lgtm + profiles: ["observability"] + ports: + - "127.0.0.1:3000:3000" + # Optional reverse proxy. Enable with: docker compose --profile proxy up nginx: image: nginx:alpine diff --git a/src/InboxIntel.Api/InboxIntel.Api.csproj b/src/InboxIntel.Api/InboxIntel.Api.csproj index 8b126b4..42a0fe2 100644 --- a/src/InboxIntel.Api/InboxIntel.Api.csproj +++ b/src/InboxIntel.Api/InboxIntel.Api.csproj @@ -15,6 +15,11 @@ + + + + + diff --git a/src/InboxIntel.Api/Program.cs b/src/InboxIntel.Api/Program.cs index d1d58ce..38a1592 100644 --- a/src/InboxIntel.Api/Program.cs +++ b/src/InboxIntel.Api/Program.cs @@ -16,6 +16,10 @@ using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.RateLimiting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; +using Npgsql; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; using Serilog; var builder = WebApplication.CreateBuilder(args); @@ -122,6 +126,28 @@ builder.Services.AddAuthentication(options => builder.Services.AddAuthorization(); +// RECOMMENDATIONS #5: OpenTelemetry traces + metrics (ASP.NET, outbound HTTP, Npgsql). +// The OTLP exporter only activates when Otel:Endpoint (or the standard +// OTEL_EXPORTER_OTLP_ENDPOINT env var) is configured — zero overhead otherwise. +// Logs stay on Serilog. Pair with the compose "observability" profile (grafana/otel-lgtm). +var otlpEndpoint = builder.Configuration["Otel:Endpoint"] + ?? Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT"); +if (!string.IsNullOrWhiteSpace(otlpEndpoint)) +{ + builder.Services.AddOpenTelemetry() + .ConfigureResource(r => r.AddService("inboxintel-api")) + .WithTracing(t => t + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddNpgsql() + .AddOtlpExporter(o => o.Endpoint = new Uri(otlpEndpoint))) + .WithMetrics(m => m + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddNpgsqlInstrumentation() + .AddOtlpExporter(o => o.Endpoint = new Uri(otlpEndpoint))); +} + builder.Services.AddApiVersioning(o => { o.DefaultApiVersion = new ApiVersion(1, 0);