|
| 1 | +# OpenTelemetry Correlated Signals — Minimal .NET 10 Companion |
| 2 | + |
| 3 | +> Full tutorial: [.NET 8 Observability with OpenTelemetry: Tracing, Metrics & Structured Logging](https://www.dotnet-guide.com/tutorials/dotnet-8-essentials/observability-opentelemetry/) |
| 4 | +
|
| 5 | +## What this sample demonstrates |
| 6 | + |
| 7 | +- ASP.NET Core Minimal API with OpenTelemetry 1.17.0 |
| 8 | +- Custom `ActivitySource` for manual tracing via `Checkout.Process` spans |
| 9 | +- Custom `Meter` with a `counter` (`checkout.requests`) and a `histogram` (`checkout.duration`) |
| 10 | +- Low-cardinality metric tags (`checkout.channel`, `checkout.outcome`) |
| 11 | +- Source-generated structured `ILogger` events via `LoggerMessage` |
| 12 | +- Automatic log-to-trace correlation from the active `Activity` |
| 13 | +- Learning-only OpenTelemetry Console Exporter output |
| 14 | +- Input validation with rejection telemetry |
| 15 | +- `ActivityListener` and `MeterListener` unit tests |
| 16 | +- Deterministic API smoke tests without external backends |
| 17 | + |
| 18 | +## Architecture |
| 19 | + |
| 20 | +``` |
| 21 | +POST /checkout |
| 22 | + ↓ |
| 23 | +ASP.NET Core request Activity |
| 24 | + ↓ |
| 25 | +custom Checkout.Process Activity |
| 26 | + ↓ |
| 27 | +structured ILogger event |
| 28 | + ↓ |
| 29 | +checkout.requests counter |
| 30 | + ↓ |
| 31 | +checkout.duration histogram |
| 32 | + ↓ |
| 33 | +OpenTelemetry SDK |
| 34 | + ↓ |
| 35 | +Console Exporter |
| 36 | +``` |
| 37 | + |
| 38 | +## File structure |
| 39 | + |
| 40 | +``` |
| 41 | +dotnet-8-essentials/observability-opentelemetry/ |
| 42 | +├── OpenTelemetrySignalsMinimal.slnx |
| 43 | +├── README.md |
| 44 | +├── src/OpenTelemetrySignalsMinimal/ |
| 45 | +│ ├── OpenTelemetrySignalsMinimal.csproj |
| 46 | +│ ├── Program.cs |
| 47 | +│ ├── Models/ |
| 48 | +│ │ └── CheckoutModels.cs |
| 49 | +│ └── Telemetry/ |
| 50 | +│ ├── CheckoutLog.cs |
| 51 | +│ └── CheckoutTelemetry.cs |
| 52 | +└── tests/OpenTelemetrySignalsMinimal.Tests/ |
| 53 | + ├── OpenTelemetrySignalsMinimal.Tests.csproj |
| 54 | + └── OpenTelemetrySignalsTests.cs |
| 55 | +``` |
| 56 | + |
| 57 | +## Prerequisites |
| 58 | + |
| 59 | +- .NET 10.0 SDK or later |
| 60 | +- No Docker, collector, or external backend required |
| 61 | + |
| 62 | +## Run |
| 63 | + |
| 64 | +```bash |
| 65 | +cd dotnet-8-essentials/observability-opentelemetry |
| 66 | +dotnet restore OpenTelemetrySignalsMinimal.slnx |
| 67 | +dotnet build --configuration Release |
| 68 | +dotnet run --project src/OpenTelemetrySignalsMinimal --configuration Release --urls http://127.0.0.1:5099 |
| 69 | +``` |
| 70 | + |
| 71 | +## Verify |
| 72 | + |
| 73 | +```bash |
| 74 | +# Root endpoint — fixed metadata |
| 75 | +curl --silent http://127.0.0.1:5099/ |
| 76 | +# {"sample":"correlated-opentelemetry-signals","exporter":"console","activitySource":"DotNetGuide.Observability.Checkout","meter":"DotNetGuide.Observability.Checkout"} |
| 77 | + |
| 78 | +# Successful checkout — returns trace ID |
| 79 | +curl --silent --request POST \ |
| 80 | + --header "Content-Type: application/json" \ |
| 81 | + --data '{"channel":"WEB","itemCount":2}' \ |
| 82 | + http://127.0.0.1:5099/checkout |
| 83 | +# {"status":"accepted","channel":"web","itemCount":2,"traceId":"<32 hex chars>"} |
| 84 | + |
| 85 | +# Invalid channel — returns 400 |
| 86 | +curl --silent --request POST \ |
| 87 | + --header "Content-Type: application/json" \ |
| 88 | + --data '{"channel":"customer-92823","itemCount":2}' \ |
| 89 | + http://127.0.0.1:5099/checkout |
| 90 | + |
| 91 | +# Out-of-range item count — returns 400 |
| 92 | +curl --silent --request POST \ |
| 93 | + --header "Content-Type: application/json" \ |
| 94 | + --data '{"channel":"mobile","itemCount":11}' \ |
| 95 | + http://127.0.0.1:5099/checkout |
| 96 | +``` |
| 97 | + |
| 98 | +## Tests |
| 99 | + |
| 100 | +```bash |
| 101 | +dotnet test --configuration Release --no-build |
| 102 | +``` |
| 103 | + |
| 104 | +## Important boundary |
| 105 | + |
| 106 | +- **Console Exporter** is for learning and debugging only. It is **not** recommended for production use, and its text output is not a standardized transport contract. |
| 107 | +- OTLP, Collector, Docker Compose, Jaeger, Prometheus, Grafana, and cloud exporters are intentionally excluded. |
| 108 | +- EF Core, HttpClient instrumentation, external service calls, queues, sampling, baggage, and health checks are not included. |
| 109 | +- The full tutorial at [dotnet-guide.com](https://www.dotnet-guide.com/tutorials/dotnet-8-essentials/observability-opentelemetry/) covers production observability architecture with OTLP → Collector → backends. |
| 110 | + |
| 111 | +## Verification table |
| 112 | + |
| 113 | +| Item | Value | |
| 114 | +|------|-------| |
| 115 | +| Target framework | `net10.0` | |
| 116 | +| OpenTelemetry.Extensions.Hosting | 1.17.0 | |
| 117 | +| OpenTelemetry.Exporter.Console | 1.17.0 | |
| 118 | +| OpenTelemetry.Instrumentation.AspNetCore | 1.17.0 | |
| 119 | +| External services | None | |
| 120 | +| Last reviewed | 2026-08-08 | |
| 121 | + |
| 122 | +## License |
| 123 | + |
| 124 | +This sample is provided for educational purposes as part of the [DOTNET GUIDE](https://www.dotnet-guide.com) tutorial series. |
0 commit comments