Skip to content

nimisha-gj/capillary-test

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Base14Scout Loyalty Platform API — OpenTelemetry Instrumentation Demo

This is a demo application that shows how to instrument an existing .NET Framework 4.6.2 application with OpenTelemetry to collect traces and metrics. The focus is on the exact changes required to go from a standard ASP.NET WebAPI/MVC application to a fully observable one exporting telemetry via OTLP.

Application Overview

The base application is a Loyalty Platform API built on ASP.NET WebAPI (MVC 5) targeting .NET Framework 4.6.2. It exposes a REST API for managing loyalty clubs and includes a legacy Web Forms project with SOAP web services.

Projects in the solution:

Project Framework Type Description
LoyaltyPlatform.Api .NET Framework 4.6.2 ASP.NET WebAPI/MVC REST API — instrumented with OpenTelemetry
Base14Scout .NET Framework 4.6.2 ASP.NET Web Forms Legacy SOAP web services (Member, Offer)

API Endpoints:

  • GET /api/clubs — Returns all loyalty clubs
  • GET /api/clubs/{id} — Returns a single club by ID

What Was Changed to Instrument the Application

The instrumentation was done via manual SDK integration (not the CLR Profiler auto-instrumentation agent). Below is every change made to the base application, file by file.

1. Added NuGet Packages (packages.config)

The following OpenTelemetry packages were added:

Core SDK:

Package Version
OpenTelemetry 1.9.0
OpenTelemetry.Api 1.9.0
OpenTelemetry.Api.ProviderBuilderExtensions 1.9.0
OpenTelemetry.Extensions.Hosting 1.9.0

Exporter:

Package Version
OpenTelemetry.Exporter.OpenTelemetryProtocol 1.9.0

Trace Instrumentation Libraries:

Package Version What It Captures
OpenTelemetry.Instrumentation.AspNet 1.9.0-beta.1 Incoming HTTP requests to the API
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule 1.9.0-beta.1 ASP.NET pipeline integration (HTTP module)
OpenTelemetry.Instrumentation.Http 1.9.0 Outgoing HTTP calls made by HttpClient / HttpWebRequest
OpenTelemetry.Instrumentation.SqlClient 1.9.0-beta.1 SQL database queries

Metric Instrumentation Libraries:

Package Version What It Captures
OpenTelemetry.Instrumentation.Runtime 1.9.0 GC collections, assemblies loaded, exception count
OpenTelemetry.Instrumentation.Process 0.5.0-beta.6 CPU time, memory usage, thread count

Transitive Dependencies (required by the above): Google.Protobuf, Grpc.Core.Api, Microsoft.Bcl.AsyncInterfaces, Microsoft.Extensions.Configuration, Microsoft.Extensions.DependencyInjection, Microsoft.Extensions.Logging.Abstractions, Microsoft.Extensions.Options, System.Diagnostics.DiagnosticSource, System.Memory, System.Buffers, System.Runtime.CompilerServices.Unsafe, System.Threading.Tasks.Extensions

2. Created OpenTelemetryConfig.cs (new file)

File: LoyaltyPlatform.Api/App_Start/OpenTelemetryConfig.cs

This is the central instrumentation configuration. It sets up both a TracerProvider (for distributed traces) and a MeterProvider (for metrics), and exports them via OTLP over HTTP/Protobuf.

public static class OpenTelemetryConfig
{
    private static TracerProvider _tracerProvider;
    private static MeterProvider _meterProvider;

    public static void Initialize()
    {
        var serviceName = Environment.GetEnvironmentVariable("OTEL_SERVICE_NAME") ?? "Base14Scout";
        var otlpEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318";

        var resourceBuilder = ResourceBuilder.CreateDefault()
            .AddService(serviceName: serviceName, serviceVersion: "1.0.0")
            .AddAttributes(new[] {
                new KeyValuePair<string, object>("environment",
                    Environment.GetEnvironmentVariable("OTEL_ENVIRONMENT") ?? "development")
            });

        // Traces: ASP.NET incoming requests + HttpClient outgoing + SQL queries → OTLP
        _tracerProvider = Sdk.CreateTracerProviderBuilder()
            .SetResourceBuilder(resourceBuilder)
            .AddAspNetInstrumentation()
            .AddHttpClientInstrumentation()
            .AddSqlClientInstrumentation(options => {
                options.SetDbStatementForText = true;
                options.RecordException = true;
            })
            .AddOtlpExporter(options => {
                options.Endpoint = new Uri(otlpEndpoint + "/v1/traces");
                options.Protocol = OtlpExportProtocol.HttpProtobuf;
            })
            .Build();

        // Metrics: ASP.NET + HttpClient + runtime + process → OTLP
        _meterProvider = Sdk.CreateMeterProviderBuilder()
            .SetResourceBuilder(resourceBuilder)
            .AddAspNetInstrumentation()
            .AddHttpClientInstrumentation()
            .AddRuntimeInstrumentation()
            .AddProcessInstrumentation()
            .AddOtlpExporter(options => {
                options.Endpoint = new Uri(otlpEndpoint + "/v1/metrics");
                options.Protocol = OtlpExportProtocol.HttpProtobuf;
            })
            .Build();
    }

    public static void Shutdown()
    {
        _tracerProvider?.Dispose();
        _meterProvider?.Dispose();
    }
}

Key design decisions:

  • Environment variables (OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_ENVIRONMENT) control all configuration — no hardcoded endpoints.
  • Resource attributes tag every trace and metric with the service name, version, and deployment environment, so telemetry can be filtered and correlated in backends.
  • SqlClient is configured with SetDbStatementForText = true so the actual SQL query text appears in traces, and RecordException = true to capture SQL errors as span events.
  • Uses HTTP/Protobuf (port 4318) rather than gRPC (port 4317), which is simpler to configure through proxies and load balancers.

3. Modified Global.asax.cs (2 lines added)

File: LoyaltyPlatform.Api/Global.asax.cs

Two calls were added to the application lifecycle:

 protected void Application_Start()
 {
+    // Initialize OpenTelemetry first
+    OpenTelemetryConfig.Initialize();
+
     AreaRegistration.RegisterAllAreas();
     GlobalConfiguration.Configure(WebApiConfig.Register);
     FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
     RouteConfig.RegisterRoutes(RouteTable.Routes);
     BundleConfig.RegisterBundles(BundleTable.Bundles);
 }

+protected void Application_End()
+{
+    // Gracefully shutdown OpenTelemetry
+    OpenTelemetryConfig.Shutdown();
+}
  • Initialize() is called first — before route registration — so the telemetry pipeline is ready to capture everything from the first request.
  • Shutdown() in Application_End flushes any buffered telemetry and disposes providers cleanly.

4. Modified Web.config (TelemetryHttpModule registration)

File: LoyaltyPlatform.Api/Web.config

The ASP.NET TelemetryHttpModule must be registered in Web.config to capture incoming HTTP request traces. On .NET Framework, the auto-registration via CLR Profiler often fails due to timing issues, so explicit registration is required.

 <system.web>
   <compilation debug="true" targetFramework="4.6.2" />
   <httpRuntime targetFramework="4.6.2" />
+  <httpModules>
+    <add name="TelemetryHttpModule"
+         type="OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule,
+               OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule" />
+  </httpModules>
 </system.web>
 <system.webServer>
+  <validation validateIntegratedModeConfiguration="false" />
+  <modules>
+    <add name="TelemetryHttpModule"
+         type="OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule,
+               OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"
+         preCondition="integratedMode,managedHandler" />
+  </modules>

The module is registered in both <system.web><httpModules> (Classic mode) and <system.webServer><modules> (Integrated mode / IIS Express) to cover all hosting scenarios.

Binding redirects were also added for OpenTelemetry's transitive dependencies (System.Diagnostics.DiagnosticSource, Microsoft.Extensions.*, Google.Protobuf, etc.) to resolve version conflicts.

5. Updated LoyaltyPlatform.Api.csproj (assembly references)

All OpenTelemetry DLL references and their dependency DLLs were added to the project file so the compiler and runtime can find them. The OpenTelemetryConfig.cs file was also added to the <Compile> item group.

What Telemetry Gets Collected

With these changes, the application automatically emits:

Traces

Signal Source Example
Incoming HTTP requests AddAspNetInstrumentation() + TelemetryHttpModule GET /api/clubs with status, duration, route
Outgoing HTTP calls AddHttpClientInstrumentation() Any HttpClient / HttpWebRequest call
SQL queries AddSqlClientInstrumentation() Query text, duration, exceptions

Each trace includes resource attributes: service.name, service.version, and environment.

Metrics

Metric Source Examples
HTTP server request duration AddAspNetInstrumentation() http.server.request.duration
HTTP client request duration AddHttpClientInstrumentation() http.client.request.duration
Runtime stats AddRuntimeInstrumentation() GC collection count, assemblies loaded, exception count
Process stats AddProcessInstrumentation() process.memory.usage, process.cpu.time, thread count

How to Run

Environment Variables

Variable Default Description
OTEL_SERVICE_NAME Base14Scout Service name in telemetry
OTEL_EXPORTER_OTLP_ENDPOINT http://localhost:4318 OTLP collector endpoint (HTTP)
OTEL_ENVIRONMENT development Deployment environment tag

With an OTel Collector

  1. Run an OpenTelemetry Collector listening on port 4318 (HTTP/Protobuf), or point OTEL_EXPORTER_OTLP_ENDPOINT to your collector/backend.
  2. Open the solution in Visual Studio and run LoyaltyPlatform.Api.
  3. Hit GET /api/clubs — traces and metrics will flow to the collector.

With a Backend (e.g., Grafana, Jaeger, Base14 Scout)

Set OTEL_EXPORTER_OTLP_ENDPOINT to your backend's OTLP ingestion URL. For example:

set OTEL_SERVICE_NAME=Base14Scout
set OTEL_EXPORTER_OTLP_ENDPOINT=https://your-backend.example.com:4318
set OTEL_ENVIRONMENT=production

Summary of All Changed Files

File Change Purpose
LoyaltyPlatform.Api/App_Start/OpenTelemetryConfig.cs New TracerProvider + MeterProvider configuration
LoyaltyPlatform.Api/Global.asax.cs Modified Call Initialize() on start, Shutdown() on end
LoyaltyPlatform.Api/Web.config Modified Register TelemetryHttpModule + binding redirects
LoyaltyPlatform.Api/packages.config Modified Add 12 OpenTelemetry NuGet packages + dependencies
LoyaltyPlatform.Api/LoyaltyPlatform.Api.csproj Modified Assembly references for all new DLLs

No application code (controllers, models, business logic) was modified. The instrumentation is entirely additive — the existing API behavior is unchanged.

Project Structure

Base14Scout.sln
├── LoyaltyPlatform.Api/              # ASP.NET WebAPI/MVC (C#) — instrumented
│   ├── App_Start/
│   │   ├── OpenTelemetryConfig.cs    # ← NEW: OTel setup
│   │   ├── WebApiConfig.cs
│   │   ├── RouteConfig.cs
│   │   ├── BundleConfig.cs
│   │   └── FilterConfig.cs
│   ├── Controllers/
│   │   ├── ClubsController.cs        # REST API for clubs
│   │   └── HomeController.cs
│   ├── Models/
│   │   ├── ClubItem.cs
│   │   └── ClubsReturn.cs
│   ├── Configuration/
│   │   ├── ExternalConfig.cs
│   │   └── ExternalConfigLoader.cs
│   ├── SampleData/
│   │   └── ClubsReturn_SampleData.json
│   ├── Views/                        # MVC views
│   ├── Global.asax.cs                # ← MODIFIED: OTel lifecycle hooks
│   ├── Web.config                    # ← MODIFIED: HttpModule + binding redirects
│   └── packages.config               # ← MODIFIED: OTel NuGet packages
│
├── Base14Scout/                      # ASP.NET Web Forms (VB.NET) — not instrumented
│   ├── App_Code/
│   │   ├── Member.vb                 # SOAP service: FetchMemberState
│   │   └── Offer.vb                  # SOAP service stub
│   ├── Member.asmx
│   ├── Offer.asmx
│   └── SampleData/
│
└── docs/
    └── OTEL_AUTO_INSTRUMENTATION.md  # Reference: CLR Profiler auto-instrumentation approach

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors