Zero-Code Instrumentation of .NET using OpenTelemetry
8 minute read
Overview
Zero-code instrumentation for .NET with OpenTelemetry provides automatic telemetry collection from .NET Framework and .NET applications without requiring any code modifications. The OpenTelemetry .NET Automatic Instrumentation works across all officially supported operating systems and versions of .NET, including .NET Framework 4.6.2+, .NET Core, and modern .NET. It automatically instruments popular libraries and frameworks, capturing traces, metrics, and logs from HTTP servers, database operations, message queues, and more.
How It Works
The .NET automatic instrumentation works by:
- Profiler API: Uses the .NET CLR Profiling API to inject instrumentation code at runtime
- Assembly Rewriting: Modifies assemblies as they’re loaded to add telemetry collection
- Automatic Discovery: Detects supported libraries and frameworks automatically
- Export via OTLP: Sends telemetry to configured backends like Edge Delta
This approach requires no source code changes and works with both .NET Framework and modern .NET applications.
Prerequisites
Ensure that you have:
- .NET Runtime:
- .NET Framework 4.6.2 or later
- .NET 6.0 or later (all supported versions)
- Operating System: Windows, Linux, or macOS
- Architecture: x86, AMD64 (x86-64), or ARM64
- Permissions:
- Administrator rights (Windows)
- Sufficient permissions to set environment variables
- Edge Delta: An account and pipeline with an OTLP input node
Installation
The installation process differs by operating system.
Linux and macOS Installation
Step 1: Download the Installation Script
curl -sSfL https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/latest/download/otel-dotnet-auto-install.sh -O
Step 2: Run the Installer
sh ./otel-dotnet-auto-install.sh
The script installs the instrumentation to $HOME/.otel-dotnet-auto/.
Step 3: Make the Instrument Script Executable
chmod +x $HOME/.otel-dotnet-auto/instrument.sh
Step 4: Source the Environment
For each shell session where you want instrumentation:
. $HOME/.otel-dotnet-auto/instrument.sh
macOS Additional Requirement:
macOS requires coreutils:
brew install coreutils
Windows Installation (PowerShell)
Prerequisites: PowerShell 5.1 or later
Step 1: Download and Import the Module
$module_url = "https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/latest/download/OpenTelemetry.DotNet.Auto.psm1"
$download_path = Join-Path $env:temp "OpenTelemetry.DotNet.Auto.psm1"
Invoke-WebRequest -Uri $module_url -OutFile $download_path -UseBasicParsing
Import-Module $download_path
Step 2: Install the Core Components
Install-OpenTelemetryCore
This installs the instrumentation to $env:ProgramFiles\OpenTelemetry .NET AutoInstrumentation\.
Step 3: Register for Current Session
Register-OpenTelemetryForCurrentSession -OTelServiceName "MyServiceDisplayName"
This sets the required environment variables for the current PowerShell session.
Configuration
Configure the instrumentation entirely through environment variables.
Required Environment Variables
| Variable | Description | Example |
|---|---|---|
OTEL_SERVICE_NAME | Service name for identification | my-dotnet-service |
OTEL_EXPORTER_OTLP_ENDPOINT | OTLP endpoint for all signals | http://localhost:4318 |
Common Configuration Variables
| Variable | Description | Default |
|---|---|---|
OTEL_TRACES_EXPORTER | Trace exporter type | otlp |
OTEL_METRICS_EXPORTER | Metrics exporter type | otlp |
OTEL_LOGS_EXPORTER | Logs exporter type | otlp |
OTEL_EXPORTER_OTLP_PROTOCOL | OTLP protocol | http/protobuf |
OTEL_LOG_LEVEL | Logging verbosity | info |
OTEL_RESOURCE_ATTRIBUTES | Additional resource attributes | See below |
Endpoint Configuration for Edge Delta
Kubernetes Environment:
export OTEL_EXPORTER_OTLP_ENDPOINT="http://ed-data-supply-svc:4318"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
Non-Kubernetes (Windows):
$env:OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
$env:OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
Non-Kubernetes (Linux/macOS):
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
Using gRPC (port 4317):
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
Signal-Specific Endpoints:
If you need different endpoints for each signal:
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="http://localhost:4318/v1/traces"
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT="http://localhost:4318/v1/metrics"
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT="http://localhost:4318/v1/logs"
See Ingest Data from an OTLP Source for detailed Edge Delta configuration.
Resource Attributes
Add metadata to all telemetry:
export OTEL_RESOURCE_ATTRIBUTES="service.version=1.0.0,deployment.environment=production,service.namespace=my-team"
Resource Detectors
Control which resource detectors are enabled:
export OTEL_DOTNET_AUTO_RESOURCE_DETECTOR_ENABLED="true"
Available detectors (automatically enabled):
- Azure App Service
- Container (Docker/Kubernetes)
- Host
- Operating System
- Process
- Process Runtime
Controlling Instrumentation
Disable All Instrumentation:
export OTEL_DOTNET_AUTO_INSTRUMENTATION_ENABLED="false"
Disable Specific Signals:
export OTEL_DOTNET_AUTO_TRACES_INSTRUMENTATION_ENABLED="false"
export OTEL_DOTNET_AUTO_METRICS_INSTRUMENTATION_ENABLED="false"
export OTEL_DOTNET_AUTO_LOGS_INSTRUMENTATION_ENABLED="false"
Disable Specific Instrumentations:
# Disable HTTP client instrumentation
export OTEL_DOTNET_AUTO_TRACES_HTTP_INSTRUMENTATION_ENABLED="false"
# Disable SQL Client instrumentation
export OTEL_DOTNET_AUTO_TRACES_SQLCLIENT_INSTRUMENTATION_ENABLED="false"
Running Your Application
Linux and macOS
Standard Application:
# Source the environment (once per session)
. $HOME/.otel-dotnet-auto/instrument.sh
# Set configuration
export OTEL_SERVICE_NAME="my-dotnet-service"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
# Run your application
./MyApp
Using dotnet run:
. $HOME/.otel-dotnet-auto/instrument.sh
export OTEL_SERVICE_NAME="my-web-api"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
dotnet run
Windows - Standard Applications
PowerShell:
# Import module
Import-Module "C:\Program Files\OpenTelemetry .NET AutoInstrumentation\OpenTelemetry.DotNet.Auto.psm1"
# Register for current session
Register-OpenTelemetryForCurrentSession -OTelServiceName "MyService"
# Set additional configuration
$env:OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
# Run your application
.\MyApp.exe
Windows - IIS Applications
For ASP.NET and ASP.NET Core applications hosted in IIS:
Step 1: Install Core Components
Import-Module "OpenTelemetry.DotNet.Auto.psm1"
Install-OpenTelemetryCore
Step 2: Register for IIS
Register-OpenTelemetryForIIS
This configures all IIS application pools to use the instrumentation.
Step 3: Configure via Web.config
For .NET Framework applications, add to Web.config:
<configuration>
<appSettings>
<add key="OTEL_SERVICE_NAME" value="my-web-app" />
<add key="OTEL_EXPORTER_OTLP_ENDPOINT" value="http://localhost:4318" />
<add key="OTEL_EXPORTER_OTLP_PROTOCOL" value="http/protobuf" />
<add key="OTEL_TRACES_EXPORTER" value="otlp" />
<add key="OTEL_METRICS_EXPORTER" value="otlp" />
</appSettings>
</configuration>
For ASP.NET Core applications in IIS, use environment variables in web.config:
<configuration>
<system.webServer>
<aspNetCore processPath="dotnet" arguments=".\MyApp.dll">
<environmentVariables>
<environmentVariable name="OTEL_SERVICE_NAME" value="my-web-app" />
<environmentVariable name="OTEL_EXPORTER_OTLP_ENDPOINT" value="http://localhost:4318" />
<environmentVariable name="OTEL_EXPORTER_OTLP_PROTOCOL" value="http/protobuf" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</configuration>
Step 4: Restart IIS
iisreset
Auto-Generated Service Names:
IIS applications get automatic service names in the format: SiteName\VirtualDirectoryPath
For example:
Default Web Site→Default Web SiteDefault Web Site\api→Default Web Site\api
Override with OTEL_SERVICE_NAME in configuration.
Windows Services
For Windows Services:
Step 1: Install and Register
Import-Module "OpenTelemetry.DotNet.Auto.psm1"
Install-OpenTelemetryCore
Register-OpenTelemetryForWindowsService -WindowsServiceName "MyServiceName" -OTelServiceName "MyDisplayName"
Step 2: Configure via Registry
Environment variables are stored in the Windows Registry at:
HKLM\SYSTEM\CurrentControlSet\Services\<ServiceName>
Add a REG_MULTI_SZ value named Environment with entries like:
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
Each line is a separate entry in the multi-string value.
Step 3: Restart the Service
Restart-Service -Name "MyServiceName"
Docker Deployment
Dockerfile for .NET Application:
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
# Install OpenTelemetry automatic instrumentation
RUN apt-get update && \
apt-get install -y curl && \
curl -sSfL https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/latest/download/otel-dotnet-auto-install.sh -O && \
sh ./otel-dotnet-auto-install.sh && \
rm ./otel-dotnet-auto-install.sh
# Copy application
COPY publish/ .
# Set environment variables for instrumentation
ENV OTEL_SERVICE_NAME="my-dotnet-service"
ENV OTEL_EXPORTER_OTLP_ENDPOINT="http://ed-data-supply-svc:4318"
ENV OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
ENV OTEL_TRACES_EXPORTER="otlp"
ENV OTEL_METRICS_EXPORTER="otlp"
ENV OTEL_LOG_LEVEL="info"
# Source instrumentation environment and run app
CMD . /root/.otel-dotnet-auto/instrument.sh && dotnet MyApp.dll
docker-compose.yml:
version: '3.8'
services:
dotnet-app:
build: .
environment:
- OTEL_SERVICE_NAME=my-dotnet-service
- OTEL_EXPORTER_OTLP_ENDPOINT=http://ed-data-supply-svc:4318
- OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
- OTEL_TRACES_EXPORTER=otlp
- OTEL_METRICS_EXPORTER=otlp
- OTEL_LOG_LEVEL=info
- OTEL_RESOURCE_ATTRIBUTES=service.version=1.0.0,deployment.environment=production
ports:
- "8080:8080"
Kubernetes Deployment
Deployment Manifest:
apiVersion: apps/v1
kind: Deployment
metadata:
name: dotnet-app
spec:
replicas: 3
selector:
matchLabels:
app: dotnet-app
template:
metadata:
labels:
app: dotnet-app
spec:
initContainers:
# Install instrumentation in init container
- name: otel-instrumentation-installer
image: busybox:latest
command:
- sh
- -c
- |
wget -O /otel-install/otel-dotnet-auto-install.sh https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/latest/download/otel-dotnet-auto-install.sh
chmod +x /otel-install/otel-dotnet-auto-install.sh
sh /otel-install/otel-dotnet-auto-install.sh
cp -r /root/.otel-dotnet-auto /otel-install/
volumeMounts:
- name: otel-install
mountPath: /otel-install
containers:
- name: app
image: myapp:latest
env:
# OpenTelemetry Configuration
- name: OTEL_SERVICE_NAME
value: "my-dotnet-service"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://ed-data-supply-svc.default.svc.cluster.local:4318"
- name: OTEL_EXPORTER_OTLP_PROTOCOL
value: "http/protobuf"
- name: OTEL_TRACES_EXPORTER
value: "otlp"
- name: OTEL_METRICS_EXPORTER
value: "otlp"
- name: OTEL_LOG_LEVEL
value: "info"
- name: OTEL_RESOURCE_ATTRIBUTES
value: "service.version=1.0.0,deployment.environment=production,k8s.namespace.name=default"
# Instrumentation paths
- name: CORECLR_ENABLE_PROFILING
value: "1"
- name: CORECLR_PROFILER
value: "{918728DD-259F-4A6A-AC2B-B85E1B658318}"
- name: CORECLR_PROFILER_PATH
value: "/otel-dotnet-auto/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so"
- name: DOTNET_ADDITIONAL_DEPS
value: "/otel-dotnet-auto/AdditionalDeps"
- name: DOTNET_SHARED_STORE
value: "/otel-dotnet-auto/store"
- name: DOTNET_STARTUP_HOOKS
value: "/otel-dotnet-auto/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll"
- name: OTEL_DOTNET_AUTO_HOME
value: "/otel-dotnet-auto"
volumeMounts:
- name: otel-install
mountPath: /otel-dotnet-auto
subPath: .otel-dotnet-auto
volumes:
- name: otel-install
emptyDir: {}
Alternative: Use OpenTelemetry Operator
For easier deployment, use the OpenTelemetry Operator which automatically injects instrumentation into pods via annotations.
Supported Instrumentation
The automatic instrumentation supports a wide variety of libraries:
Traces (24 Libraries)
Web Frameworks:
- ASP.NET (.NET Framework 4.6.2+)
- ASP.NET Core (.NET 6+)
HTTP Clients:
- HttpClient
- WCF (Windows Communication Foundation)
Databases:
- Microsoft.Data.SqlClient
- System.Data.SqlClient
- Npgsql (PostgreSQL)
- MySql.Data
- MySqlConnector
- MongoDB.Driver
- Oracle.ManagedDataAccess
- Elasticsearch.Net
- Elastic.Transport
Message Queues:
- RabbitMQ.Client
- MassTransit
- NServiceBus
- Kafka (Confluent.Kafka)
Cloud SDKs:
- Azure SDK
Other:
- GraphQL
- Quartz.NET (job scheduling)
- StackExchange.Redis
- gRPC.Net.Client
Metrics (8 Libraries)
- ASP.NET (.NET Framework)
- ASP.NET Core
- HttpClient
- .NET Runtime metrics
- Npgsql
- NServiceBus
- System.Data.SqlClient
- Process metrics
Logs (2 Libraries)
- Microsoft.Extensions.Logging (.NET 9.0+)
- log4net (2.0.13 - 4.0.0)
All instrumentations are enabled by default and can be selectively disabled via environment variables.
Sampling Configuration
Control trace data volume using sampling:
Ratio-Based Sampling
export OTEL_TRACES_SAMPLER="traceidratio"
export OTEL_TRACES_SAMPLER_ARG="0.1" # Sample 10%
Parent-Based Sampling (Recommended)
export OTEL_TRACES_SAMPLER="parentbased_traceidratio"
export OTEL_TRACES_SAMPLER_ARG="0.1" # Sample 10% of root traces
Always Sample (Development)
export OTEL_TRACES_SAMPLER="always_on"
Verifying Instrumentation
Enable Console Exporter for Testing
Test instrumentation locally:
export OTEL_TRACES_EXPORTER="console"
export OTEL_METRICS_EXPORTER="console"
export OTEL_LOGS_EXPORTER="console"
Check Logs
Set debug logging:
export OTEL_LOG_LEVEL="debug"
Look for messages like:
[OpenTelemetry.AutoInstrumentation] Initializing...
[OpenTelemetry.AutoInstrumentation] Loaded instrumentation: AspNetCore
[OpenTelemetry.AutoInstrumentation] Loaded instrumentation: HttpClient
[OpenTelemetry.AutoInstrumentation] Loaded instrumentation: SqlClient
Verify in Edge Delta
- Log into Edge Delta
- Navigate to your OTLP input node
- Check for traces with your service name
- Verify spans from instrumented libraries
Troubleshooting
Instrumentation Not Loading
Windows - Check Installation:
Test-Path "$env:ProgramFiles\OpenTelemetry .NET AutoInstrumentation"
Linux/macOS - Check Installation:
ls -la $HOME/.otel-dotnet-auto/
Verify Environment Variables:
# Linux/macOS
env | grep OTEL
# Windows PowerShell
gci env: | Where-Object { $_.Name -like "OTEL*" }
IIS Applications Not Instrumented
Check IIS Registration:
# Should show OpenTelemetry environment variables
reg query "HKLM\SYSTEM\CurrentControlSet\Services\WAS\Environment"
Restart IIS:
iisreset
Check Application Pool Identity: Ensure the application pool has permissions to access the instrumentation files.
Review Event Viewer: Check Windows Event Viewer → Application logs for .NET runtime errors.
No Telemetry Data
Test OTLP Endpoint:
# Windows
Test-NetConnection -ComputerName localhost -Port 4318
# Linux
curl -X POST http://localhost:4318/v1/traces
Enable Debug Logging:
export OTEL_LOG_LEVEL="debug"
Check Firewall: Ensure port 4318/4317 is open for outbound connections.
Performance Issues
High CPU Usage:
- Reduce sampling rate
- Disable unnecessary instrumentations
- Set appropriate log level (warn or error)
Memory Leaks:
- Check for the latest version of the instrumentation
- Report issues to the OpenTelemetry .NET team
Slow Application Startup:
- This is expected as instrumentation initializes
- Consider warm-up requests in load balancers
Uninstallation
Linux and macOS
No action needed - instrumentation only affects the current shell session.
To remove files:
rm -rf $HOME/.otel-dotnet-auto/
Windows
Import-Module "OpenTelemetry.DotNet.Auto.psm1"
# Unregister from IIS
Unregister-OpenTelemetryForIIS
# Unregister from Windows Services
Unregister-OpenTelemetryForWindowsService -WindowsServiceName "ServiceName"
# Uninstall core components
Uninstall-OpenTelemetryCore
Best Practices
Set Service Names: Always explicitly set
OTEL_SERVICE_NAMEfor clarity.Use Resource Attributes: Include version and environment information:
OTEL_RESOURCE_ATTRIBUTES="service.version=1.2.3,deployment.environment=production"Configure Sampling: Use sampling in production to control costs:
OTEL_TRACES_SAMPLER="parentbased_traceidratio" OTEL_TRACES_SAMPLER_ARG="0.1"Test Locally First: Use console exporters for local testing before deploying.
Monitor Performance: Watch application performance after enabling instrumentation.
Update Regularly: Keep the instrumentation package updated for bug fixes and new features.
Use Configuration Files: For IIS applications, use
Web.configfor maintainable configuration.Secure Credentials: Never hardcode credentials in configuration files; use environment variables or secure stores.
Next Steps
- Review supported libraries for your dependencies
- Learn about code-based instrumentation for custom business logic
- Set up Edge Delta processing for your telemetry data
- Explore sampling strategies for production deployments
- Configure resource attributes for richer metadata and filtering
For more information on OpenTelemetry .NET automatic instrumentation, visit the official documentation and GitHub repository.