Skip to content

OtelHelper — Examples

📖 EXAMPLES.md — Detailed description of each check, internals, and Grafana queries

Three services demonstrating complete instrumentation with OtelHelper:

┌─────────────────────────────────────────────────────────────────────┐
│ dotnet-process (Worker, .NET 8)                                      │
│   ├── ApiHealthWorker: calls dotnet-api every 1 min                  │
│   └── HeavyProcessWorker: CPU/memory stress every 2 min             │
└──────────────────────────┬──────────────────────────────────────────┘
                           │ HTTP :8080
                           ▼
┌─────────────────────────────────────────────────────────────────────┐
│ dotnet-api (Minimal API, .NET 10)                                    │
│   8 endpoints REST → calls Backend via gRPC                         │
└──────────────────────────┬──────────────────────────────────────────┘
                           │ gRPC :5100 (HTTP/2)
                           ▼
┌─────────────────────────────────────────────────────────────────────┐
│ dotnet-backend (gRPC Server, .NET 8)                                 │
│   3 RPCs: ProcessOrder, CancelOrder, SlowOperation                  │
│   Simulates: DB queries + external call (httpbin.org)                │
└─────────────────────────────────────────────────────────────────────┘

How to Use the Lib (pattern demonstrated)

1. Register telemetry (Program.cs)

// Minimal — everything via env vars
services.AddOtelHelper();

// With overrides
services.AddOtelHelper(opts =>
{
    opts.ResourceAttributes = new Dictionary<string, object>
    {
        ["app.component"] = "api-gateway"
    };
    opts.AdditionalActivitySources = new List<string>
    {
        "my-app.module-x"
    };
});

After registration, available via DI: - ActivitySource — for creating manual spans (name = ServiceName) - Meter — for creating custom metrics (name = ServiceName)

2. Create spans in APIs (child of request)

using var activity = activitySource.StartActivity("api.get-order");
activity?.SetTag("order.id", id);

3. Create spans in Workers (independent root)

using OtelHelper;

using var span = activitySource.StartRootActivity("process-api-order");
span?.SetTag("endpoint", "/order/1");

4. Metrics

var counter = meter.CreateCounter<long>("orders.received_total", "orders");
counter.Add(1, new KeyValuePair<string, object?>("source", "http-get"));

Environment Variables

dotnet-api

Variable Required Description Example
SERVICE_NAME Yes Service name dotnet-api
ENVIRONMENT Yes Environment (controls log level) DEV
OTEL_EXPORTER_OTLP_ENDPOINT No Collector host http://localhost
BACKEND_URL Yes Backend URL (gRPC) http://dotnet-backend:5100

dotnet-backend

Variable Required Description Example
SERVICE_NAME Yes Service name dotnet-backend
ENVIRONMENT Yes Environment DEV
OTEL_EXPORTER_OTLP_ENDPOINT No Collector host http://localhost

dotnet-process

Variable Required Description Example
SERVICE_NAME Yes Service name dotnet-process
ENVIRONMENT Yes Environment DEV
OTEL_EXPORTER_OTLP_ENDPOINT No Collector host http://localhost
API_URL Yes dotnet-api URL http://dotnet-api:8080

Optional (all)

Variable Description Default
OTEL_HELPER_DEBUG_LEVEL Debug mode: Debug log + 100% sampling + tracestate debug=true false
OTEL_HELPER_EXTRA_INSTRUMENTATION Legacy extra instrumentations (SQL, AWS). Prefer the OtelHelper.AWS / OtelHelper.Sql subpackages. SQL

Build Images

From the dotnet/ directory:

docker build -f example/dotnet-api/Dockerfile -t sample/dotnet-api:latest .
docker build -f example/dotnet-backend/Dockerfile -t sample/dotnet-backend:latest .
docker build -f example/dotnet-process/Dockerfile -t sample/dotnet-process:latest .

Local Test (Docker)

# Create network
docker network create sample-net

# Backend (gRPC on port 5100)
docker run -d --name dotnet-backend --network sample-net \
  -e SERVICE_NAME=dotnet-backend \
  -e ENVIRONMENT=DEV \
  sample/dotnet-backend:latest

# API (HTTP on port 8080, connects to Backend via gRPC)
docker run -d --name dotnet-api --network sample-net \
  -e SERVICE_NAME=dotnet-api \
  -e ENVIRONMENT=DEV \
  -e BACKEND_URL=http://dotnet-backend:5100 \
  -p 8080:8080 \
  sample/dotnet-api:latest

# Test
curl http://localhost:8080/order/42
# {"orderId":42,"status":"completed","enriched":true,"processedBy":"dotnet-backend"}

# Cleanup
docker rm -f dotnet-api dotnet-backend && docker network rm sample-net

Endpoints — dotnet-api

Method Endpoint Span Name What it does
GET / — Simple health check (no Backend)
GET /order/{id} api.get-order gRPC → Backend ProcessOrder (DB + external)
POST /order api.create-order JSON Body → gRPC → Backend ProcessOrder
GET /order/{id}/cancel api.cancel-order gRPC → Backend CancelOrder (DB)
GET /slow api.slow-operation gRPC → Backend SlowOperation (4-7s)
GET /batch api.batch-orders 5× gRPC → Backend ProcessOrder (sequential)
GET /error api.error-simulated Throws exception → span with ERROR
GET /health/ready api.readiness-check gRPC → Backend as probe

Minimal Example — Individual Project

For a real service (own repo), the complete setup is:

Program.cs:

using OtelHelper;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOtelHelper();
var app = builder.Build();

// Your endpoints here
app.Run();

Dockerfile:

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY *.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app

FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "MyService.dll"]

Result: traces, metrics, and logs exported automatically. Zero instrumentation code beyond AddOtelHelper().


File Structure (this example)

example/
├── Protos/
│   └── order.proto                         # Shared proto (5 RPCs, namespace OtelHelper.Grpc)
├── dotnet-api/
│   ├── dotnet-api.csproj                   # net10.0, Grpc.Net.Client
│   ├── Program.cs                          # 8 endpoints, gRPC client, metrics
│   ├── Dockerfile                          # SDK 10.0, aspnet 10.0, port 8080
│   └── Dockerfile.demo                     # CI: NuGet package instead of ProjectReference
├── dotnet-backend/
│   ├── dotnet-backend.csproj               # net8.0, Grpc.AspNetCore
│   ├── Program.cs                          # Minimal — registers gRPC service
│   ├── Services/
│   │   └── OrderGrpcService.cs             # 5 RPCs, manual spans, metrics
│   ├── appsettings.json                    # Kestrel HTTP/2 on port 5100
│   ├── Dockerfile                          # SDK 8.0, aspnet 8.0, port 5100
│   └── Dockerfile.demo                     # CI
└── dotnet-process/
    ├── dotnet-process.csproj               # net8.0, Worker SDK
    ├── Program.cs                          # Registers workers + HttpClient
    ├── ApiHealthWorker.cs                  # 6 checks/min, StartRootActivity
    ├── HeavyProcessWorker.cs              # CPU/memory stress, StartRootActivity
    ├── QueueConsumerWorker.cs             # Queue consumer pattern
    ├── ScheduledJobWorker.cs              # Scheduled job with circuit breaker
    └── Dockerfile                          # SDK 10.0, aspnet 10.0, DOTNET_ROLL_FORWARD