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);