- Lab
-
Libraries: If you want this lab, consider one of these libraries.
Guided: Instrumenting an ASP.NET Core 10 App with OpenTelemetry
In this Code Lab, you'll instrument an ASP.NET Core 10 application with OpenTelemetry. You'll configure tracing, metrics, and logging, instrument HTTP requests and dependencies, export telemetry data, and validate instrumentation. When finished, you'll have a fully observable application with correlated traces ready for production monitoring.
Lab Info
Table of Contents
-
Challenge
Introduction
Welcome to the Instrumenting an ASP.NET Core 10 App with OpenTelemetry Code Lab. In this hands-on lab, you add OpenTelemetry to an existing Web API --- configuring distributed tracing, metrics collection, and structured logging with trace context correlation. By the end of this lab, you will have a fully observable application with correlated traces ready for production monitoring.
What You Will Learn
- Tracing configuration --- Configure distributed tracing with automatic instrumentation for incoming HTTP requests and outgoing dependencies.
- Metrics collection --- Collect framework-level HTTP metrics and custom application counters using OpenTelemetry's Meter API.
- Logging integration --- Connect
ILoggerto OpenTelemetry so every log entry carriesTraceIdandSpanIdfor trace-to-log correlation. - Telemetry export and validation --- Configure exporters to emit telemetry and validate the instrumentation end-to-end.
The Scenario
You are a platform engineer at CarvedRock building a payment processing API. The operations team needs visibility into request latency, dependency failures, and error rates to maintain SLAs. Your task is to instrument the application with OpenTelemetry.
The Application (Click to expand)
This lab uses an ASP.NET Core 10 Web API that processes payments:
PaymentsController--- endpoints for processing payments, retrieving history, and health checksPaymentProcessingService--- business logic where you add custom tracing and metricsExternalPaymentService--- simulated external gateway usingHttpClientfor dependency instrumentationRequestLoggingMiddleware--- logs request details with trace context for correlation
Familiarizing with the Program Structure (Click to expand)
Key files:
src/Configuration/TelemetrySettings.cs--- Telemetry constants (you complete this)src/Program.cs--- Tracing, metrics, logging, and exporter configuration (you complete this)src/Services/PaymentProcessingService.cs--- CustomActivitySourceinstrumentation (you complete this)src/Controllers/PaymentsController.cs--- API controller (pre-built)src/Services/ExternalPaymentService.cs--- External dependency viaHttpClient(pre-built)src/Middleware/RequestLoggingMiddleware.cs--- Request logging middleware (pre-built)tests/validate-telemetry.sh--- Validation script (you complete this)
Build the solution using:
dotnet build src/CarvedRockPayments.csprojRun with:
dotnet run --project src/CarvedRockPayments.csprojThe API starts on
http://localhost:4200.Your Mission
- Define service identity constants that tag all telemetry data
- Configure the OpenTelemetry resource builder and tracing pipeline
- Add ASP.NET Core instrumentation for tracing and metrics
- Integrate OpenTelemetry with the logging pipeline for trace correlation
- Instrument outgoing HTTP dependencies and register custom trace sources
- Configure console exporters across all three pipelines
- Add custom
ActivitySourcespans with business attributes to payment processing - Validate the complete instrumentation end-to-end
Note: Complete tasks in order. Build frequently with
dotnet build src/CarvedRockPayments.csprojto catch errors early.info> If you get stuck on a task, you can find solution files with the completed code for each task in the
solutionfolder of your filetree. -
Challenge
Configuring the OpenTelemetry Foundation
Understanding Resources and Tracing
In OpenTelemetry, a Resource is a set of attributes that identify the entity producing telemetry --- typically
service.name,service.version, andservice.namespace. You build the resource once withResourceBuilderand pass it to each pipeline so traces, metrics, and logs share the same identity.CreateDefault()includes SDK and runtime metadata;CreateEmpty()produces a blank resource.The tracing pipeline is registered via
AddOpenTelemetry().WithTracing(). Inside it, you add instrumentation libraries that hook into framework pipelines to create spans automatically.AddAspNetCoreInstrumentation()creates a span per incoming HTTP request.SetResourceBuilder()on each pipeline ties telemetry data back to your service. -
Challenge
Collecting Metrics and Integrating Logging
Understanding Metrics and Logging in OpenTelemetry
The
.WithMetrics()pipeline collects numerical measurements.AddAspNetCoreInstrumentation()captures request duration histograms, request counts, and active connections.AddMeter()registers a customMeterso OpenTelemetry collects its instruments --- without registration, counter and histogram data is silently discarded.OpenTelemetry logging enhances
ILoggerby enriching each log entry with the activeTraceIdandSpanId.IncludeScopespreserves contextual data from logging scopes, andIncludeFormattedMessagerenders the full message text rather than just the template. -
Challenge
Instrumenting Dependencies and Configuring Exporters
Understanding Dependencies and Exporters
AddHttpClientInstrumentation()hooks intoHttpClientto create child spans for outgoing calls, linked to the parent via W3C trace context. This completes the request flow picture: incoming request, business logic, and downstream dependencies all appear in one trace.Registering a custom
ActivitySourcewithAddSource()is required for OpenTelemetry to record spans from that source. Without registration,StartActivity()returnsnull.An exporter sends telemetry to a backend. The console exporter writes to stdout for development visibility. In production, you would swap in
AddOtlpExporter()for backends like Jaeger or Grafana Tempo. Each pipeline needs its own exporter call. -
Challenge
Custom Instrumentation and End-to-End Validation
Understanding Custom Instrumentation
Automatic instrumentation captures HTTP boundaries but cannot see inside business logic.
ActivitySourceis the .NET API for creating custom spans. You declare it as astatic readonlyfield, register its name withAddSource(), and callStartActivity()to create spans linked to the current trace context. Tags added viaSetTag()become searchable attributes in tracing backends.StartActivity()returnsnullwhen no listener is registered (e.g., in unit tests), so use the null-conditional operator when callingSetTag(). -
Challenge
Conclusion
Congratulations on completing the Instrumenting an ASP.NET Core 10 App with OpenTelemetry lab! You have implemented a complete observability solution using the industry-standard OpenTelemetry framework.
What You Have Accomplished
- Defined service identity constants — Centralized
service.name,service.version, andservice.namespaceacross all telemetry pipelines. - Configured the resource builder and tracing pipeline — Built a resource with
CreateDefault().AddService()and attached it to the tracing pipeline withSetResourceBuilder(). - Added tracing and metrics instrumentation — Configured
AddAspNetCoreInstrumentation()on both pipelines and registered the customMeterfor payment counters and histograms. - Integrated logging with trace context — Connected
ILoggerto OpenTelemetry with scope preservation, formatted messages, and log-level filtering. - Instrumented outgoing dependencies — Added
HttpClientinstrumentation and registered the customActivitySourceto complete the request flow picture. - Configured console exporters — Added exporters to all three pipelines for development-time visibility.
- Added custom business tracing — Created
ActivitySourcespans withpayment.id,payment.amount, andpayment.currencytags for searchable business telemetry. - Validated end-to-end — Ran a test suite confirming correlated traces, metrics, and logs across all code paths.
Key Takeaways
- Automatic instrumentation (
AddAspNetCoreInstrumentation(),AddHttpClientInstrumentation()) captures HTTP boundaries without code changes. - Custom instrumentation (
ActivitySource,Meter) fills the gaps for business-specific operations. - OpenTelemetry logging enriches
ILoggerwithTraceId/SpanIdautomatically — no changes to existing log statements needed. - Exporters are pluggable: swap
AddConsoleExporter()forAddOtlpExporter()when moving to production backends.
Experiment Before You Go
- Send multiple requests and observe unique
TraceIdvalues in the console - Add a second span for a "fraud check" operation in
PaymentProcessingService - Create a new counter that tracks payment amounts by currency
- Set
activity?.SetStatus(ActivityStatusCode.Error)on payment failure and observe the span status - Examine how
RequestLoggingMiddlewarelog entries includeTraceIdvalues matching trace spans
- Defined service identity constants — Centralized
About the author
Real skill practice before real-world application
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Learn by doing
Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.
Follow your guide
All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.
Turn time into mastery
On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.