SigNoz Cloud - This page is relevant for SigNoz Cloud editions.
Self-Host - This page is relevant for self-hosted SigNoz editions.

Send Metrics from .NET Application using OpenTelemetry

This guide shows you how to send custom and runtime metrics from your .NET application to SigNoz using OpenTelemetry automatic instrumentation.

Prerequisites

  • .NET 6.0 or later
  • A SigNoz Cloud account or self-hosted SigNoz instance

Send metrics to SigNoz

Step 1. Download the OpenTelemetry .NET automatic instrumentation

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

Step 2. Set environment variables

export OTEL_SERVICE_NAME="<service-name>"
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
export OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
export OTEL_METRICS_EXPORTER="otlp"
export OTEL_METRIC_EXPORT_INTERVAL="60000"
export OTEL_TRACES_EXPORTER="none"
export OTEL_LOGS_EXPORTER="none"
export OTEL_DOTNET_AUTO_METRICS_ADDITIONAL_SOURCES="MyApp.Metrics"

Replace the following:

  • <region>: Your SigNoz Cloud region (us, eu, or in). See endpoints.
  • <your-ingestion-key>: Your SigNoz ingestion key.
  • <service-name>: A descriptive name for your service (e.g., payment-service).

Step 3. Add custom metrics to your application

Use new Meter() to create metrics. The automatic instrumentation picks up meters registered via OTEL_DOTNET_AUTO_METRICS_ADDITIONAL_SOURCES.

OrderService.cs
using System.Diagnostics.Metrics;

public class OrderService
{
    private static readonly Meter meter = new Meter("MyApp.Metrics", "1.0.0");
    private static readonly Counter<long> ordersCounter = meter.CreateCounter<long>(
        "orders.processed",
        description: "Total number of orders processed");

    public void ProcessOrder(string orderId)
    {
        // Your business logic here
        ordersCounter.Add(1);
    }
}
Info

This example shows a Counter, which only increases. OpenTelemetry supports other metric types like UpDownCounter, Histogram, and ObservableGauge. See Custom Metrics Examples for complete examples of each type.

Step 4. Run your application

. $HOME/.otel-dotnet-auto/instrument.sh
dotnet run
Info

The automatic instrumentation generates HTTP metrics automatically:

  • Server metrics (http.server.request.duration) for ASP.NET Core apps
  • Client metrics (http.client.request.duration) for apps using HttpClient

See the .NET HTTP metrics semantic conventions for details.

Validate

Once your application is running with automatic instrumentation:

  1. Trigger some application activity to generate metrics.
  2. In SigNoz, go to Metrics > Metrics Explorer.
  3. Search for your custom metric (e.g., orders.processed).
  4. Runtime metrics like process.runtime.dotnet.gc.collections.count are collected automatically.
Info

Want more metrics? .NET runtime metrics (GC, threads, JIT) are collected automatically by the automatic instrumentation. See .NET Runtime Metrics for the full list.

.NET Runtime Metrics

The automatic instrumentation collects .NET runtime and process metrics automatically. No additional configuration is needed.

Exported Metrics

Garbage Collection Metrics:

  • process.runtime.dotnet.gc.collections.count - Number of garbage collections by generation
  • process.runtime.dotnet.gc.objects.size - Bytes in use by GC heap objects
  • process.runtime.dotnet.gc.allocations.size - Total bytes allocated on managed heap
  • process.runtime.dotnet.gc.duration - Total time paused in GC

JIT Compilation Metrics:

  • process.runtime.dotnet.jit.il_compiled.size - IL bytes compiled by JIT
  • process.runtime.dotnet.jit.methods_compiled.count - Number of methods JIT compiled
  • process.runtime.dotnet.jit.compilation_time - Time spent in JIT compilation

Threading Metrics:

  • process.runtime.dotnet.thread_pool.threads.count - Number of thread pool threads
  • process.runtime.dotnet.thread_pool.completed_items.count - Work items completed
  • process.runtime.dotnet.thread_pool.queue.length - Thread pool queue length
  • process.runtime.dotnet.monitor.lock_contention.count - Monitor lock contentions
  • process.runtime.dotnet.timer.count - Active timer count

Assembly Metrics:

  • process.runtime.dotnet.assemblies.count - Number of loaded assemblies

Process Metrics:

  • process.cpu.time - CPU time by state (user/system)
  • process.cpu.count - Number of available processors
  • process.memory.usage - Physical memory in use
  • process.memory.virtual - Committed virtual memory
  • process.thread.count - Process thread count

Custom Metrics Examples

OpenTelemetry provides four metric instrument types. Each example shows how to create and use the instrument.

Counter

A value that only increases (e.g., total requests, orders processed).

using System.Diagnostics.Metrics;

var meter = new Meter("MyApp.Metrics", "1.0.0");

var requestCounter = meter.CreateCounter<long>(
    "http.requests.total",
    description: "Total number of HTTP requests");

// Increment the counter
requestCounter.Add(1);

UpDownCounter

A value that can increase or decrease (e.g., queue size, active connections).

using System.Diagnostics.Metrics;

var meter = new Meter("MyApp.Metrics", "1.0.0");

var activeRequests = meter.CreateUpDownCounter<int>(
    "http.requests.active",
    description: "Number of active requests");

// Increment when request starts
activeRequests.Add(1);

// Decrement when request ends
activeRequests.Add(-1);

Histogram

A distribution of values (e.g., request duration, response size).

using System.Diagnostics.Metrics;

var meter = new Meter("MyApp.Metrics", "1.0.0");

var requestDuration = meter.CreateHistogram<double>(
    "http.request.duration",
    unit: "ms",
    description: "HTTP request duration");

// Record a duration value
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
// ... process request ...
requestDuration.Record(stopwatch.ElapsedMilliseconds);

ObservableGauge

A point-in-time value via callback (e.g., temperature, memory usage).

using System.Diagnostics.Metrics;

var meter = new Meter("MyApp.Metrics", "1.0.0");

meter.CreateObservableGauge(
    "app.memory.usage",
    () => GC.GetTotalMemory(false),
    unit: "By",
    description: "Current memory usage");

Troubleshooting

Metrics not appearing?

Check environment variables:

echo $OTEL_EXPORTER_OTLP_ENDPOINT
echo $OTEL_METRICS_EXPORTER
echo $OTEL_DOTNET_AUTO_METRICS_ADDITIONAL_SOURCES

Verify instrumentation is active: Look for OpenTelemetry startup messages in the application logs.

Check meter name registration: Ensure OTEL_DOTNET_AUTO_METRICS_ADDITIONAL_SOURCES matches your meter name (e.g., MyApp.Metrics). You can use wildcards like MyApp.*.

Metrics delayed or not updating?

OTEL_METRIC_EXPORT_INTERVAL controls how often metrics are exported in milliseconds. The default is 60000 (60 seconds), so metrics may take up to a minute to appear.

To reduce the interval for testing:

export OTEL_METRIC_EXPORT_INTERVAL=10000  # 10 seconds

Authentication errors

If you see "Unauthorized" or "403 Forbidden":

  • Verify your ingestion key is correct in OTEL_EXPORTER_OTLP_HEADERS
  • Ensure the header format is exactly: signoz-ingestion-key=<your-key> (no extra spaces)

Setup OpenTelemetry Collector (Optional)

What is the OpenTelemetry Collector?

Think of the OTel Collector as a middleman between your app and SigNoz. Instead of your application sending data directly to SigNoz, it sends everything to the Collector first, which then forwards it along.

Why use it?

  • Cleaning up data - Filter out noisy metrics you don't care about, or remove sensitive info before it leaves your servers.
  • Keeping your app lightweight - Let the Collector handle batching, retries, and compression instead of your application code.
  • Adding context automatically - The Collector can tag your data with useful info like which Kubernetes pod or cloud region it came from.
  • Future flexibility - Want to send data to multiple backends later? The Collector makes that easy without changing your app.

See Switch from direct export to Collector for step-by-step instructions to convert your setup.

For more details, see Why use the OpenTelemetry Collector? and the Collector configuration guide.

Next Steps

Last updated: January 26, 2026

Edit on GitHub

Was this page helpful?