![]() |
VOOZH | about |
dotnet add package Davasorus.Utility.DotNet.Telemetry --version 2026.2.3.2
NuGet\Install-Package Davasorus.Utility.DotNet.Telemetry -Version 2026.2.3.2
<PackageReference Include="Davasorus.Utility.DotNet.Telemetry" Version="2026.2.3.2" />
<PackageVersion Include="Davasorus.Utility.DotNet.Telemetry" Version="2026.2.3.2" />Directory.Packages.props
<PackageReference Include="Davasorus.Utility.DotNet.Telemetry" />Project file
paket add Davasorus.Utility.DotNet.Telemetry --version 2026.2.3.2
#r "nuget: Davasorus.Utility.DotNet.Telemetry, 2026.2.3.2"
#:package Davasorus.Utility.DotNet.Telemetry@2026.2.3.2
#addin nuget:?package=Davasorus.Utility.DotNet.Telemetry&version=2026.2.3.2Install as a Cake Addin
#tool nuget:?package=Davasorus.Utility.DotNet.Telemetry&version=2026.2.3.2Install as a Cake Tool
OpenTelemetry distributed tracing and telemetry integration for Davasorus Utility packages. Provides optional activity sources, spans, and tracing capabilities with Fluent API configuration.
dotnet add package Davasorus.Utility.DotNet.Telemetry
This section shows how to integrate the Telemetry package into your existing applications and libraries.
Add the telemetry package reference to your .csproj file:
<PackageReference Include="Davasorus.Utility.DotNet.Telemetry" Version="2025.4.3.1" />
In your service or client class, create a static ActivitySource:
using System.Diagnostics;
using Davasorus.Utility.DotNet.Telemetry;
public class CacheService
{
// Create activity source for this component
private static readonly ActivitySource ActivitySource =
ActivitySourceHelper.Create("Cache", "Memory");
private readonly ILogger<CacheService> _logger;
// ... rest of class
}
public async Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default)
{
using (_logger.BeginScope(new Dictionary<string, object>
{
["CacheKey"] = key,
["Operation"] = "Get"
}))
{
try
{
_logger.LogDebug("Retrieving value from cache for key: {Key}", key);
var result = await _client.GetAsync<T>(key, cancellationToken);
if (result != null)
{
_logger.LogDebug("Cache hit for key: {Key}", key);
}
else
{
_logger.LogDebug("Cache miss for key: {Key}", key);
}
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving value from cache for key: {Key}", key);
throw;
}
}
}
public async Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default)
{
// Start activity (returns null if telemetry not configured - safe!)
using var activity = ActivitySource.StartActivitySafe("Cache.Get");
activity.AddTagSafe("cache.key", key);
activity.AddTagSafe("cache.operation", "get");
// Enrich logging scope with trace context
using (_logger.BeginScope(activity.ToLoggingScope(new Dictionary<string, object>
{
["CacheKey"] = key,
["Operation"] = "Get"
})))
{
try
{
_logger.LogDebug("Retrieving value from cache for key: {Key}", key);
var result = await _client.GetAsync<T>(key, cancellationToken);
// Add telemetry tags
activity.AddTagSafe("cache.hit", result != null);
if (result != null)
{
_logger.LogDebug("Cache hit for key: {Key}", key);
}
else
{
_logger.LogDebug("Cache miss for key: {Key}", key);
}
// Mark success
activity.SetStatusSafe(ActivityStatusCode.Ok);
return result;
}
catch (Exception ex)
{
// Record exception in telemetry
activity.RecordExceptionSafe(ex);
_logger.LogError(ex, "Error retrieving value from cache for key: {Key}", key);
throw;
}
}
}
public async Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default)
{
// Combines activity start and logging scope in one call
var (activity, logScope) = ActivitySource.StartActivityWithLogging(
_logger,
"Cache.Get",
new Dictionary<string, object>
{
["CacheKey"] = key,
["Operation"] = "Get",
["cache.operation"] = "get"
}
);
using (activity)
using (logScope)
{
activity.AddTagSafe("cache.key", key);
try
{
_logger.LogDebug("Retrieving value from cache");
var result = await _client.GetAsync<T>(key, cancellationToken);
activity.AddTagSafe("cache.hit", result != null);
activity.SetStatusSafe(ActivityStatusCode.Ok);
return result;
}
catch (Exception ex)
{
activity.RecordExceptionSafe(ex);
_logger.LogError(ex, "Cache retrieval failed");
throw;
}
}
}
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using Davasorus.Utility.DotNet.Telemetry;
namespace MyApp.Services;
public class CacheService : ICacheService
{
private static readonly ActivitySource ActivitySource =
ActivitySourceHelper.Create("Cache", "Memory");
private readonly ICacheClient _client;
private readonly ILogger<CacheService> _logger;
public CacheService(ICacheClient client, ILogger<CacheService> logger)
{
_client = client;
_logger = logger;
}
public async Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default)
{
using var activity = ActivitySource.StartActivitySafe("Cache.Get");
activity.AddTagSafe("cache.key", key);
activity.AddTagSafe("cache.operation", "get");
activity.AddTagSafe("cache.backend", "memory");
using (_logger.BeginScope(activity.ToLoggingScope(new Dictionary<string, object>
{
["CacheKey"] = key,
["Operation"] = "Get"
})))
{
try
{
_logger.LogDebug("Retrieving from cache");
var result = await _client.GetAsync<T>(key, cancellationToken);
activity.AddTagSafe("cache.hit", result != null);
activity.SetStatusSafe(ActivityStatusCode.Ok);
return result;
}
catch (Exception ex)
{
activity.RecordExceptionSafe(ex);
_logger.LogError(ex, "Cache retrieval failed");
throw;
}
}
}
public async Task SetAsync<T>(
string key,
T value,
CacheOptions? options = null,
CancellationToken cancellationToken = default)
{
using var activity = ActivitySource.StartActivitySafe("Cache.Set");
activity.AddTagSafe("cache.key", key);
activity.AddTagSafe("cache.operation", "set");
activity.AddTagSafe("cache.backend", "memory");
using (_logger.BeginScope(activity.ToLoggingScope(new Dictionary<string, object>
{
["CacheKey"] = key,
["Operation"] = "Set"
})))
{
try
{
_logger.LogDebug("Setting value in cache");
await _client.SetAsync(key, value, options, cancellationToken);
activity.SetStatusSafe(ActivityStatusCode.Ok);
_logger.LogDebug("Successfully set value");
}
catch (Exception ex)
{
activity.RecordExceptionSafe(ex);
_logger.LogError(ex, "Cache set failed");
throw;
}
}
}
}
Your existing tests will continue to work without any changes. The telemetry methods are all no-ops when telemetry is not configured:
[Fact]
public async Task GetAsync_ReturnsValue()
{
// Arrange
var logger = new Mock<ILogger<CacheService>>();
var client = new Mock<ICacheClient>();
var service = new CacheService(client.Object, logger.Object);
// Act
var result = await service.GetAsync<string>("key");
// Assert
Assert.NotNull(result);
// Telemetry code runs but doesn't affect the test
}
.csprojusing System.Diagnostics; and using Davasorus.Utility.DotNet.Telemetry;ActivitySource for the componentStartActivitySafeAddTagSafeToLoggingScopeRecordExceptionSafeSetStatusSafeTelemetry is completely optional. Your application works without it.
Note on Protocols: For standard OTLP collectors (Jaeger, Tempo), use gRPC with port 4317 and set
useHttpProtobuf: false. For HTTP/Protobuf collectors (Seq, etc.), use port 4318 or provider-specific endpoint and setuseHttpProtobuf: true.
using Davasorus.Utility.DotNet.Telemetry.Configuration;
// Option A: Fluent API with OTLP exporter
builder.Services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyApplication")
.WithServiceVersion("1.0.0")
.WithOtlpExporter("http://localhost:4317", useHttpProtobuf: false) // gRPC for Jaeger/Tempo
);
// Option B: Console for local development
builder.Services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyApplication")
.WithConsoleExporter()
);
// Option C: Options-based configuration
builder.Services.AddDavasorusTelemetry(options =>
{
options.ServiceName = "MyApplication";
options.UseOtlpExporter = true;
options.OtlpEndpoint = "http://localhost:4317";
options.OtlpUseHttpProtobuf = false;
});
// Option D: Don't configure at all - everything still works!
// No telemetry will be collected, but no errors either
For production deployments, configure telemetry via appsettings.json instead of hardcoding values:
appsettings.json:
{
"Telemetry": {
"Enabled": true,
"ServiceName": "MyApplication",
"ServiceVersion": "1.0.0",
"ServiceNamespace": "Production",
"UseOtlpExporter": true,
"OtlpUseHttpProtobuf": true,
"OtlpHttpEndpoint": "http://your-seq-server/ingest/otlp/v1/traces",
"OtlpHeaders": {
"X-Seq-ApiKey": "your-api-key-here"
},
"EnableHttpInstrumentation": true,
"EnableSqlInstrumentation": true,
"EnrichWithSqlCommandText": false,
"MaxAttributesPerSpan": 128,
"MaxEventsPerSpan": 128,
"MaxLinksPerSpan": 128
}
}
Program.cs:
using Davasorus.Utility.DotNet.Telemetry.Configuration;
var builder = WebApplication.CreateBuilder(args);
// Bind configuration to TelemetryOptions
builder.Services.AddDavasorusTelemetry(options =>
{
builder.Configuration.GetSection("Telemetry").Bind(options);
});
Example for Jaeger (gRPC):
{
"Telemetry": {
"ServiceName": "my-service",
"UseOtlpExporter": true,
"OtlpUseHttpProtobuf": false,
"OtlpEndpoint": "http://jaeger:4317"
}
}
Example for standard HTTP collector:
{
"Telemetry": {
"ServiceName": "my-service",
"UseOtlpExporter": true,
"OtlpUseHttpProtobuf": true,
"OtlpHttpEndpoint": "http://collector:4318/v1/traces"
}
}
using System.Diagnostics;
using Davasorus.Utility.DotNet.Telemetry;
public class CacheService
{
// Create ActivitySource for your component
private static readonly ActivitySource ActivitySource =
ActivitySourceHelper.Create("Cache", "Memory");
private readonly ILogger<CacheService> _logger;
public async Task<T?> GetAsync<T>(string key)
{
// Start activity - returns null if telemetry not configured (safe!)
using var activity = ActivitySource.StartActivitySafe("Cache.Get");
// Add tags (safe even if activity is null)
activity.AddTagSafe("cache.key", key);
activity.AddTagSafe("cache.operation", "get");
// Create logging scope with trace context
using var logScope = _logger.BeginScope(
activity.ToLoggingScope(new Dictionary<string, object>
{
["CacheKey"] = key,
["Operation"] = "Get"
})
);
try
{
_logger.LogDebug("Retrieving from cache");
var result = await RetrieveFromCache<T>(key);
// Record success (safe no-op if activity is null)
activity.AddTagSafe("cache.hit", result != null);
activity.SetStatusSafe(ActivityStatusCode.Ok);
return result;
}
catch (Exception ex)
{
// Record exception (safe no-op if activity is null)
activity.RecordExceptionSafe(ex);
_logger.LogError(ex, "Cache retrieval failed");
throw;
}
}
}
public async Task<T?> GetAsync<T>(string key)
{
// Combines activity start and logging scope in one call
var (activity, logScope) = ActivitySource.StartActivityWithLogging(
_logger,
"Cache.Get",
new Dictionary<string, object>
{
["cache.key"] = key,
["cache.operation"] = "get"
}
);
using (activity)
using (logScope)
{
try
{
var result = await RetrieveFromCache<T>(key);
activity.SetStatusSafe(ActivityStatusCode.Ok);
return result;
}
catch (Exception ex)
{
activity.RecordExceptionSafe(ex);
throw;
}
}
}
For production deployments, consider these recommended settings to balance observability with performance and cost:
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("ProductionService")
.WithServiceVersion("2.1.0")
.WithServiceNamespace("Production") // Or auto-detect from env vars
// Sampling: Use ParentBased with 10% ratio for production
// This reduces volume by 90% while maintaining trace continuity
.WithSampling("ParentBased", ratio: 0.1)
// Batch Export: Optimize for production load
.WithBatchExport(
maxQueueSize: 4096, // Increase for high throughput
maxBatchSize: 1024, // Larger batches = fewer exports
scheduledDelayMs: 10000, // Export every 10 seconds
exportTimeoutMs: 30000 // 30 second timeout
)
// Compression: Enable to save 70-90% bandwidth
.WithCompression(enabled: true)
// OTLP Exporter: Configure for your collector
.WithOtlpExporter("https://collector.production.com:4317", useHttpProtobuf: false)
// Instrumentation: Enable what you need
.WithAspNetCoreInstrumentation(enabled: true)
.WithHttpInstrumentation(enabled: true)
.WithSqlInstrumentation(enabled: true, recordCommandText: false) // Never log SQL in production
.WithEntityFrameworkInstrumentation(enabled: true)
.WithAwsInstrumentation(enabled: true)
// Resource Detection: Auto-discover infrastructure details
.WithEnvironmentDetection(enabled: true) // Reads ASPNETCORE_ENVIRONMENT
.WithContainerResourceDetection(enabled: true) // Docker/K8s metadata
.WithHostResourceDetection(enabled: true) // Hostname, OS, CPU
// Metrics: Enable with 1-minute export interval
.WithMetrics(enabled: true, exportIntervalMs: 60000)
.AddMeter("MyApp.*")
// Propagation: Use W3C for interoperability
.WithPropagation("W3C")
);
appsettings.Production.json:
{
"Telemetry": {
"Enabled": true,
"ServiceName": "my-service",
"ServiceVersion": "2025.4.3.1",
"SamplingStrategy": "ParentBased",
"SamplingRatio": 0.1,
"MaxQueueSize": 4096,
"MaxExportBatchSize": 1024,
"ScheduledDelayMilliseconds": 10000,
"ExporterTimeoutMilliseconds": 30000,
"EnableCompression": true,
"UseOtlpExporter": true,
"OtlpUseHttpProtobuf": false,
"OtlpEndpoint": "https://collector.production.com:4317",
"OtlpHeaders": {
"Authorization": "Bearer ${TELEMETRY_API_KEY}"
},
"EnableAspNetCoreInstrumentation": true,
"EnableHttpInstrumentation": true,
"EnableSqlInstrumentation": true,
"EnrichWithSqlCommandText": false,
"EnableEntityFrameworkInstrumentation": true,
"EnableAwsInstrumentation": true,
"AutoDetectEnvironment": true,
"EnableContainerResourceDetection": true,
"EnableHostResourceDetection": true,
"EnableMetrics": true,
"MetricExportIntervalMilliseconds": 60000,
"MeterPatterns": ["MyApp.*", "Davasorus.Utility.*"],
"PropagationFormat": "W3C",
"ValidateOnStartup": true
}
}
AlwaysOn - Records every trace (100%)
.WithSampling("AlwaysOn")
AlwaysOff - Records no traces (0%)
.WithSampling("AlwaysOff")
TraceIdRatio - Random sampling based on trace ID
.WithSampling("TraceIdRatio", ratio: 0.1) // 10% of traces
ParentBased (Recommended) - Respects parent span decisions
.WithSampling("ParentBased", ratio: 0.1) // Root traces sampled at 10%
Production Recommendation:
// Start with 10% sampling, adjust based on traffic volume
.WithSampling("ParentBased", ratio: 0.1)
// High-volume services (>1000 req/sec): Use 1-5%
.WithSampling("ParentBased", ratio: 0.01)
// Critical paths or debugging: Temporarily increase to 100%
.WithSampling("ParentBased", ratio: 1.0)
Fine-tune export behavior for your workload:
// High-throughput service (>1000 req/sec)
.WithBatchExport(
maxQueueSize: 8192, // Large queue to handle bursts
maxBatchSize: 2048, // Large batches for efficiency
scheduledDelayMs: 5000, // Export every 5 seconds
exportTimeoutMs: 60000 // 60 second timeout
)
// Low-latency service (near real-time)
.WithBatchExport(
maxQueueSize: 1024, // Smaller queue
maxBatchSize: 256, // Smaller batches = faster exports
scheduledDelayMs: 2000, // Export every 2 seconds
exportTimeoutMs: 15000 // 15 second timeout
)
// Default balanced configuration
.WithBatchExport() // Uses defaults: 2048/512/5000/30000
Enable gzip compression to reduce bandwidth by 70-90%:
// Always enable compression for production
.WithCompression(enabled: true)
Performance Impact:
When to disable:
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithConsoleExporter()
);
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("ProductionService")
.WithServiceVersion("2.1.0")
.WithServiceNamespace("Production")
.WithOtlpExporter("http://jaeger:4317", useHttpProtobuf: false)
.WithHttpInstrumentation()
.WithSqlInstrumentation()
);
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithConsoleExporter() // For local debugging
.WithOtlpExporter("http://collector:4317", useHttpProtobuf: false) // For production tracing
.AddActivitySource("MyApp.*") // Custom sources
);
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithOtlpExporter("http://collector:4318", useHttpProtobuf: true)
);
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithOtlpExporter("https://api.honeycomb.io:443")
.AddOtlpHeader("x-honeycomb-team", "your-api-key")
);
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.AddResourceAttribute("deployment.environment", "production")
.AddResourceAttribute("host.name", Environment.MachineName)
.AddResourceAttribute("k8s.pod.name", podName)
);
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithSqlInstrumentation(enabled: true, recordCommandText: false) // Safe default
);
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithMetrics(enabled: true, exportIntervalMs: 60000) // Export every 60 seconds
.AddMeter("MyApp.*") // Listen to MyApp meters
.AddMeter("Davasorus.Utility.*") // Listen to Davasorus Utility meters
.WithOtlpExporter() // Metrics use same exporter as traces
);
// In your code, create meters and instruments:
using System.Diagnostics.Metrics;
var meter = new Meter("MyApp.Cache");
var cacheHitCounter = meter.CreateCounter<long>("cache.hits", "hits", "Number of cache hits");
var cacheMissCounter = meter.CreateCounter<long>("cache.misses", "misses", "Number of cache misses");
var cacheLatencyHistogram = meter.CreateHistogram<double>("cache.latency", "ms", "Cache operation latency");
// Record metrics:
cacheHitCounter.Add(1);
cacheMissCounter.Add(1);
cacheLatencyHistogram.Record(15.5);
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyWebApi")
.WithAspNetCoreInstrumentation(enabled: true) // Enabled by default
.WithOtlpExporter()
);
// Automatically captures:
// - HTTP request/response (method, path, status code)
// - Routing information
// - Middleware execution
// - Exception details
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithEntityFrameworkInstrumentation(enabled: true)
.WithOtlpExporter()
);
// Automatically captures:
// - Database queries
// - Query execution time
// - Connection information
// - Query parameters (if configured)
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyGrpcClient")
.WithGrpcClientInstrumentation(enabled: true)
.WithOtlpExporter()
);
// Automatically captures:
// - gRPC method calls
// - Request/response metadata
// - Status codes
// - Streaming operations
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyAwsService")
.WithAwsInstrumentation(enabled: true)
.WithPropagation("AWS") // Use AWS X-Ray propagation
.WithOtlpExporter()
);
// Automatically captures:
// - S3 operations (GetObject, PutObject, etc.)
// - DynamoDB operations (Query, Scan, PutItem, etc.)
// - SQS operations (SendMessage, ReceiveMessage, etc.)
// - SNS operations (Publish, Subscribe, etc.)
// - Lambda invocations
// - And all other AWS SDK calls
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithRedisInstrumentation(enabled: true)
.WithOtlpExporter()
);
// Automatically captures:
// - Redis commands (GET, SET, HGET, etc.)
// - Command execution time
// - Connection information
// - Pipeline operations
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("FullyInstrumentedService")
.WithServiceVersion("1.0.0")
// Enable all instrumentation types
.WithAspNetCoreInstrumentation(enabled: true)
.WithHttpInstrumentation(enabled: true)
.WithSqlInstrumentation(enabled: true, recordCommandText: false)
.WithEntityFrameworkInstrumentation(enabled: true)
.WithGrpcClientInstrumentation(enabled: true)
.WithAwsInstrumentation(enabled: true)
.WithRedisInstrumentation(enabled: true)
.WithOtlpExporter()
);
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithEnvironmentDetection(enabled: true) // Enabled by default
.WithOtlpExporter()
);
// Automatically reads ServiceNamespace from:
// 1. ASPNETCORE_ENVIRONMENT environment variable (ASP.NET Core apps)
// 2. DOTNET_ENVIRONMENT environment variable (.NET apps)
// 3. Falls back to ServiceNamespace property if neither is set
// Example environment variables:
// ASPNETCORE_ENVIRONMENT=Production -> service.namespace: "Production"
// DOTNET_ENVIRONMENT=Staging -> service.namespace: "Staging"
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithContainerResourceDetection(enabled: true) // Auto-detect Docker/K8s
.WithHostResourceDetection(enabled: true) // Auto-detect host info
.WithOtlpExporter()
);
// Container Resource Detector automatically adds:
// - container.id: Docker container ID
// - container.name: Container name
// - container.image.name: Image name
// - container.image.tag: Image tag
// Host Resource Detector automatically adds:
// - host.name: Machine hostname
// - host.id: Host identifier
// - os.type: Operating system (e.g., "linux", "windows")
// - os.description: OS version details
// - process.pid: Process ID
// - process.executable.name: Executable name
// - process.executable.path: Executable path
// - process.runtime.name: Runtime name (e.g., ".NET")
// - process.runtime.version: Runtime version
// W3C Trace Context (Default - Recommended)
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithPropagation("W3C") // Default, most interoperable
.WithOtlpExporter()
);
// Uses W3C Trace Context headers: traceparent, tracestate
// B3 Propagation (Zipkin)
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithPropagation("B3")
.WithOtlpExporter()
);
// Uses B3 headers: X-B3-TraceId, X-B3-SpanId, X-B3-Sampled
// Jaeger Propagation
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithPropagation("Jaeger")
.WithOtlpExporter()
);
// Uses Jaeger headers: uber-trace-id
// AWS X-Ray Propagation
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithPropagation("AWS")
.WithAwsInstrumentation(enabled: true) // Typically used with AWS services
.WithOtlpExporter()
);
// Uses AWS X-Ray headers: X-Amzn-Trace-Id
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithDiagnosticLogging(enabled: true) // Enable diagnostic output
.WithValidation(enabled: true) // Fail-fast on invalid config
.WithOtlpExporter("http://localhost:4317")
);
// Diagnostic logging writes to console:
// - Configuration validation results
// - Exporter initialization status
// - Activity source registration
// - Instrumentation setup details
// - Resource attributes detected
// - Any configuration warnings or errors
// Example output:
// [Telemetry] Service Name: MyService
// [Telemetry] Service Version: 1.0.0
// [Telemetry] OTLP Endpoint: http://localhost:4317
// [Telemetry] Sampling Strategy: ParentBased (Ratio: 0.1)
// [Telemetry] Compression: Enabled
// [Telemetry] Instrumentation: HTTP, SQL, ASP.NET Core
// [Telemetry] Resource Attributes: 5 custom attributes
// [Telemetry] Validation: Passed
This section provides provider-specific configuration examples for popular OTLP-compatible telemetry backends.
Seq is a centralized log and trace aggregation server that supports OpenTelemetry.
services.AddDavasorusTelemetry(options =>
{
options.ServiceName = "MyApplication";
options.OtlpEndpoint = "http://localhost:5341/ingest/otlp/v1/traces";
options.OtlpUseHttpProtobuf = true; // Seq requires HTTP/Protobuf
options.UseOtlpExporter = true;
// Optional: Add API key
options.OtlpHeaders = new Dictionary<string, string>
{
["X-Seq-ApiKey"] = "your-api-key"
};
});
Seq Notes:
/ingest/otlp/v1/traces (required)Jaeger is an open-source distributed tracing platform.
services.AddDavasorusTelemetry(options =>
{
options.ServiceName = "MyApplication";
options.OtlpEndpoint = "http://localhost:4317"; // gRPC
options.OtlpUseHttpProtobuf = false; // Use gRPC for Jaeger
options.UseOtlpExporter = true;
});
// Alternative: HTTP/Protobuf endpoint
services.AddDavasorusTelemetry(options =>
{
options.ServiceName = "MyApplication";
options.OtlpHttpEndpoint = "http://localhost:4318/v1/traces"; // HTTP
options.OtlpUseHttpProtobuf = true;
options.UseOtlpExporter = true;
});
Jaeger Notes:
Datadog provides full-stack observability with OTLP support via the Datadog Agent.
services.AddDavasorusTelemetry(options =>
{
options.ServiceName = "MyApplication";
options.OtlpEndpoint = "http://localhost:4317"; // Datadog Agent
options.UseOtlpExporter = true;
// API key configured in Datadog Agent or environment
});
Datadog Notes:
DD_API_KEY environment variable or configure in agentEnable OTLP in Datadog Agent (datadog.yaml):
otlp_config:
receiver:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
Grafana Tempo is a high-volume distributed tracing backend.
services.AddDavasorusTelemetry(options =>
{
options.ServiceName = "MyApplication";
options.OtlpEndpoint = "http://tempo:4317"; // gRPC
options.OtlpUseHttpProtobuf = false;
options.UseOtlpExporter = true;
});
// Alternative: HTTP endpoint
services.AddDavasorusTelemetry(options =>
{
options.ServiceName = "MyApplication";
options.OtlpHttpEndpoint = "http://tempo:4318/v1/traces"; // HTTP
options.OtlpUseHttpProtobuf = true;
options.UseOtlpExporter = true;
});
Tempo Notes:
Honeycomb is a full-featured observability platform.
services.AddDavasorusTelemetry(options =>
{
options.ServiceName = "MyApplication";
options.OtlpEndpoint = "https://api.honeycomb.io:443";
options.UseOtlpExporter = true;
options.OtlpHeaders = new Dictionary<string, string>
{
["x-honeycomb-team"] = "your-api-key",
["x-honeycomb-dataset"] = "your-dataset-name"
};
});
Honeycomb Notes:
https://api.honeycomb.io:443x-honeycomb-team headerx-honeycomb-dataset headerConfiguration for a standard OpenTelemetry Collector deployment.
services.AddDavasorusTelemetry(options =>
{
options.ServiceName = "MyApplication";
options.OtlpEndpoint = "http://otel-collector:4317"; // gRPC
options.UseOtlpExporter = true;
// Optional: Add custom headers for authentication
options.OtlpHeaders = new Dictionary<string, string>
{
["Authorization"] = "Bearer your-token"
};
});
Generic Collector Notes:
Azure Monitor supports OTLP ingestion (preview feature).
services.AddDavasorusTelemetry(options =>
{
options.ServiceName = "MyApplication";
options.OtlpHttpEndpoint = "https://<region>.in.applicationinsights.azure.com/v1/traces";
options.OtlpUseHttpProtobuf = true;
options.UseOtlpExporter = true;
options.OtlpHeaders = new Dictionary<string, string>
{
["Authorization"] = $"Bearer {instrumentationKey}"
};
});
Azure Monitor Notes:
eastus, westus2)AWS X-Ray requires the OpenTelemetry Collector with X-Ray exporter.
services.AddDavasorusTelemetry(options =>
{
options.ServiceName = "MyApplication";
options.OtlpEndpoint = "http://localhost:4317"; // OTEL Collector
options.UseOtlpExporter = true;
// Enable AWS propagation for X-Ray compatibility
options.PropagationFormat = "AWS";
});
AWS X-Ray Notes:
Example OTEL Collector Config for X-Ray:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
exporters:
awsxray:
region: us-east-1
service:
pipelines:
traces:
receivers: [otlp]
exporters: [awsxray]
// Create standardized activity source
var source = ActivitySourceHelper.Create("Cache", "Memory");
// Creates: "Davasorus.Utility.Cache.Memory"
// Check if enabled
if (ActivitySourceHelper.IsEnabled(source))
{
// Telemetry is configured
}
All extension methods are null-safe and work even when telemetry is not configured:
// Start activity (returns null if not configured)
var activity = source.StartActivitySafe("Operation");
// Add tags (no-op if activity is null)
activity.AddTagSafe("key", "value");
activity.AddTagsSafe(new Dictionary<string, object> { ["key"] = "value" });
// Record exception (no-op if activity is null)
activity.RecordExceptionSafe(exception);
// Set status (no-op if activity is null)
activity.SetStatusSafe(ActivityStatusCode.Ok);
// Add event (no-op if activity is null)
activity.AddEventSafe("CacheHit", new Dictionary<string, object> { ["key"] = key });
// Create logging scope with trace context
var scope = activity.ToLoggingScope(additionalProperties);
// Enrich existing scope
activity.EnrichLoggingScope(existingScope);
// Combined activity + logging
var (activity, logScope) = source.StartActivityWithLogging(logger, "Operation", properties);
builder
// Core Configuration
.WithEnabled(bool) // Enable/disable telemetry
.WithServiceName(string) // Set service name
.WithServiceVersion(string) // Set version
.WithServiceNamespace(string) // Set namespace (env)
.WithServiceInstanceId(string) // Set instance ID
// Exporters
.WithConsoleExporter() // Add console output
.WithOtlpExporter(endpoint, useHttp) // Add OTLP exporter
.WithOtlpHeaders(headers) // Set OTLP headers
.AddOtlpHeader(key, value) // Add single header
// Sampling
.WithSampling(strategy, ratio) // Configure sampling (AlwaysOn/AlwaysOff/TraceIdRatio/ParentBased)
// Batch Export
.WithBatchExport(queueSize, batchSize, // Configure batch processor
scheduledDelayMs, timeoutMs)
// Compression
.WithCompression(enabled) // Enable gzip compression
// Metrics
.WithMetrics(enabled, exportIntervalMs) // Enable metrics collection
.AddMeter(pattern) // Add meter pattern
// Trace Instrumentation
.WithHttpInstrumentation(enabled) // HTTP client tracing
.WithSqlInstrumentation(enabled, recordCmd) // SQL client tracing
.WithAspNetCoreInstrumentation(enabled) // ASP.NET Core tracing
.WithEntityFrameworkInstrumentation(enabled) // Entity Framework tracing
.WithGrpcClientInstrumentation(enabled) // gRPC client tracing
.WithAwsInstrumentation(enabled) // AWS SDK tracing
.WithRedisInstrumentation(enabled) // Redis tracing
// Activity Sources
.AddActivitySource(pattern) // Add activity source pattern
.AddActivitySources(patterns) // Add multiple patterns
// Resource Attributes
.AddResourceAttribute(key, value) // Add resource attribute
// Resource Detection
.WithEnvironmentDetection(enabled) // Auto-detect environment
.WithContainerResourceDetection(enabled) // Auto-detect container
.WithHostResourceDetection(enabled) // Auto-detect host
// Propagation
.WithPropagation(format) // Configure propagation (W3C/B3/Jaeger/AWS)
// Span Configuration
.WithSpanLimits(attrs, events, links) // Set span limits
.WithExceptionRecording(enabled) // Record exceptions
// Diagnostics
.WithDiagnosticLogging(enabled) // Enable diagnostic logging
.WithValidation(enabled) // Validate configuration at startup
.Build() // Build options
The package now includes a SemanticConventions class with standard OpenTelemetry attribute names for consistent, interoperable telemetry:
using Davasorus.Utility.DotNet.Telemetry;
// Service attributes
activity.AddTagSafe(SemanticConventions.Service.Name, "my-service");
activity.AddTagSafe(SemanticConventions.Service.Version, "1.0.0");
// HTTP attributes (OTel v1.28+)
activity.AddTagSafe(SemanticConventions.Http.RequestMethod, "GET");
activity.AddTagSafe(SemanticConventions.Http.ResponseStatusCode, 200);
activity.AddTagSafe(SemanticConventions.Http.UrlFull, url);
// Database attributes (OTel v1.28+)
activity.AddTagSafe(SemanticConventions.Database.SystemName, "sqlserver");
activity.AddTagSafe(SemanticConventions.Database.Namespace, "MyDB");
activity.AddTagSafe(SemanticConventions.Database.OperationName, "SELECT");
// Messaging attributes (SQS, SNS, etc.)
activity.AddTagSafe(SemanticConventions.Messaging.System, "sqs");
activity.AddTagSafe(SemanticConventions.Messaging.DestinationName, "my-queue");
activity.AddTagSafe(SemanticConventions.Messaging.MessageId, messageId);
// Exception attributes
activity.AddTagSafe(SemanticConventions.Exception.Type, ex.GetType().FullName);
activity.AddTagSafe(SemanticConventions.Exception.Message, ex.Message);
The package provides helper methods for common scenarios:
// Service attributes (all at once)
activity.SetServiceAttributes(
serviceName: "my-application",
serviceVersion: "2025.4.3.1",
serviceNamespace: "production",
serviceInstanceId: Environment.MachineName
);
// HTTP request attributes
activity.SetHttpAttributes(
method: "POST",
url: "https://api.example.com/users",
statusCode: 201
);
// Database operation attributes
activity.SetDatabaseAttributes(
system: "sqlserver",
name: "EPSPartitionToolDB",
operation: "INSERT"
);
// Cache Operations
activity.AddTagSafe("cache.operation", "get"); // get, set, remove, clear
activity.AddTagSafe("cache.key", key);
activity.AddTagSafe("cache.hit", true);
activity.AddTagSafe("cache.backend", "memory"); // memory, sqlserver, sqlite
// SQL Operations (in addition to semantic conventions)
activity.AddTagSafe("db.system", "mssql"); // mssql, sqlite
activity.AddTagSafe("db.operation", "query"); // query, execute, stored_procedure
activity.AddTagSafe("db.name", "MyDatabase");
activity.AddTagSafe("db.statement", sanitizedQuery); // Be careful with PII!
// Service Operations
activity.AddTagSafe("service.name", serviceName);
activity.AddTagSafe("service.operation", "start"); // start, stop, restart
activity.AddTagSafe("host.name", hostName);
New in 2025.4.2.3: Baggage allows you to propagate key-value pairs across service boundaries without explicitly passing them through method parameters or HTTP headers.
Baggage is part of the W3C Trace Context specification and automatically propagates across:
baggage header)✅ Good use cases:
❌ Bad use cases:
// Service A: Add baggage to activity
using var activity = ActivitySource.StartActivitySafe("ProcessRequest");
activity.AddBaggageSafe("tenant.id", "acme-corp");
activity.AddBaggageSafe("user.id", "user-12345");
activity.AddBaggageSafe("correlation.id", Guid.NewGuid().ToString());
// Call downstream service...
await CallServiceB();
// Service B: Read baggage (no direct reference to Service A needed!)
var tenantId = Activity.Current?.GetBaggageSafe("tenant.id");
var userId = Activity.Current?.GetBaggageSafe("user.id");
_logger.LogInformation("Processing for tenant {TenantId}, user {UserId}", tenantId, userId);
// Add multiple items at once
var baggageItems = new[]
{
new KeyValuePair<string, string?>("tenant.id", "12345"),
new KeyValuePair<string, string?>("user.role", "admin"),
new KeyValuePair<string, string?>("feature.flags", "new-ui,dark-mode")
};
activity.AddBaggageSafe(baggageItems);
// Retrieve all baggage
var allBaggage = activity.GetAllBaggageSafe();
foreach (var item in allBaggage)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
public class ProcessOrderJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
// Background jobs should use StartRootActivity to detach from ambient context
using var scope = ActivitySource.StartRootActivity("Job.ProcessOrder");
var activity = scope.Activity;
// Add baggage specific to this job execution
activity.AddBaggageSafe("job.type", "order-processing");
activity.AddBaggageSafe("job.priority", context.JobDetail.JobDataMap.GetString("priority"));
// Baggage will propagate to any downstream calls
await _orderService.ProcessAsync(orderId);
}
}
| Feature | Baggage | Tags |
|---|---|---|
| Propagates across services | ✅ Yes | ❌ No |
| Network overhead | ⚠️ Yes | ✅ No |
| Use for filtering | ⚠️ Limited | ✅ Yes |
| Size limit | 1KB total | 128 per span |
| Best for | Cross-cutting IDs | Span-specific data |
Use StartRootActivity to create independent traces per background-job execution.
It returns a DetachedActivityScope that disposes the underlying activity and
restores the prior ambient Activity.Current on Dispose.
By default, background jobs (Quartz.NET, Hangfire, etc.) inherit Activity.Current from the application startup, causing all job executions to share the same TraceId:
// ❌ PROBLEM: All jobs share one trace
public class MyJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
using var activity = ActivitySource.StartActivitySafe("Job.Execute");
// TraceId: abc123... (same for every execution!)
}
}
This makes per-execution debugging impossible in Seq, Jaeger, or other tracing tools.
Use StartRootActivity to force a new root trace per execution:
// ✅ SOLUTION: Each job gets unique trace
public class MyJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
using var scope = ActivitySource.StartRootActivity("Job.Execute");
var activity = scope.Activity;
// TraceId: abc123... (unique per execution!)
}
}
using Davasorus.Utility.DotNet.Telemetry;
public class RealmMovementProcessJob : IJob
{
private static readonly ActivitySource ActivitySource =
ActivitySourceHelper.Create("Job", "RealmMovement");
private readonly ILogger<RealmMovementProcessJob> _logger;
public async Task Execute(IJobExecutionContext context)
{
// Create independent trace for this job execution
using var scope = ActivitySource.StartRootActivity("Job.RealmMovement.Execute");
var activity = scope.Activity;
// Add job-specific context
activity
.AddTagSafe("job.key", context.JobDetail.Key.ToString())
.AddTagSafe("job.type", nameof(RealmMovementProcessJob))
.AddTagSafe("job.fire_time", context.FireTimeUtc.ToString("o"));
// Create logging scope with trace context
using var logScope = _logger.BeginScope(
activity.ToLoggingScope(new Dictionary<string, object>
{
["JobKey"] = context.JobDetail.Key.ToString(),
["JobType"] = nameof(RealmMovementProcessJob)
})
);
try
{
_logger.LogInformation("Job execution started");
// Your job logic here
await ProcessRealmMovementAsync();
activity?.SetStatusSafe(ActivityStatusCode.Ok);
_logger.LogInformation("Job execution completed successfully");
}
catch (Exception ex)
{
activity?.RecordExceptionSafe(ex, escaped: true);
_logger.LogError(ex, "Job execution failed");
throw;
}
}
}
The telemetry package works seamlessly with your existing logging scopes:
Before (with logging only):
using var scope = logger.BeginScope(new Dictionary<string, object>
{
["Operation"] = "GetCache",
["CacheKey"] = key
});
logger.LogInformation("Retrieving from cache");
After (with telemetry + logging):
using var activity = source.StartActivitySafe("Cache.Get");
activity.AddTagSafe("cache.key", key);
using var scope = logger.BeginScope(activity.ToLoggingScope(new Dictionary<string, object>
{
["Operation"] = "GetCache",
["CacheKey"] = key
}));
logger.LogInformation("Retrieving from cache");
// Log now includes TraceId and SpanId for correlation!
Log Output:
[2025-11-29 10:15:23] [INFO] Retrieving from cache
Operation: GetCache
CacheKey: user:123
TraceId: 4bf92f3577b34da6a3ce929d0e0e4736
SpanId: 00f067aa0ba902b7
docker run -d --name jaeger \
-p 4317:4317 \
-p 16686:16686 \
jaegertracing/all-in-one:latest
services.AddDavasorusTelemetry(t => t
.WithServiceName("MyApp")
.WithOtlpExporter("http://localhost:4317", useHttpProtobuf: false)
);
Development/Staging:
.WithSampling("AlwaysOn") // 100% sampling for complete visibility
Production - Low Traffic (<100 req/sec):
.WithSampling("ParentBased", ratio: 0.1) // 10% sampling
Production - Medium Traffic (100-1000 req/sec):
.WithSampling("ParentBased", ratio: 0.05) // 5% sampling
Production - High Traffic (>1000 req/sec):
.WithSampling("ParentBased", ratio: 0.01) // 1% sampling
Key Principles:
ParentBased in production to maintain trace continuityEnable selectively based on your application needs:
// Web API Service
.WithAspNetCoreInstrumentation(enabled: true) // ✅ Required
.WithHttpInstrumentation(enabled: true) // ✅ For outbound HTTP calls
.WithSqlInstrumentation(enabled: true) // ✅ If using SQL
.WithEntityFrameworkInstrumentation(false) // ⚠️ Redundant with SQL instrumentation
.WithGrpcClientInstrumentation(false) // ❌ Only if using gRPC
.WithAwsInstrumentation(false) // ❌ Only if using AWS SDK
.WithRedisInstrumentation(false) // ❌ Only if using Redis
// Background Job Service
.WithAspNetCoreInstrumentation(enabled: false) // ❌ Not a web service
.WithHttpInstrumentation(enabled: true) // ✅ For API calls
.WithSqlInstrumentation(enabled: true) // ✅ For database access
.WithAwsInstrumentation(enabled: true) // ✅ If using SQS/SNS/S3
Performance Consideration:
Never log sensitive data in production:
// ❌ DANGEROUS - Logs SQL queries with parameters
.WithSqlInstrumentation(enabled: true, recordCommandText: true)
// ✅ SAFE - Omits SQL query text
.WithSqlInstrumentation(enabled: true, recordCommandText: false)
// ❌ DANGEROUS - Logs HTTP request/response bodies
.AddResourceAttribute("http.request.body", requestBody)
// ✅ SAFE - Only logs non-sensitive metadata
activity.AddTagSafe("http.request.method", "POST");
activity.AddTagSafe("http.response.status_code", 200);
Review your resource attributes:
// ✅ Safe metadata
.AddResourceAttribute("deployment.environment", "production")
.AddResourceAttribute("service.namespace", "backend")
.AddResourceAttribute("host.name", Environment.MachineName)
// ❌ Never include secrets
// .AddResourceAttribute("database.password", password)
// .AddResourceAttribute("api.key", apiKey)
Sanitize URLs and headers:
// Remove query parameters with sensitive data
var sanitizedUrl = new Uri(url).GetLeftPart(UriPartial.Path);
activity.AddTagSafe("http.url", sanitizedUrl);
// Never log Authorization headers
// activity.AddTagSafe("http.request.header.authorization", authHeader); // ❌ NEVER
Enable in containerized environments:
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithContainerResourceDetection(enabled: true) // ✅ Docker/Kubernetes
.WithHostResourceDetection(enabled: true) // ✅ Adds host metadata
.WithEnvironmentDetection(enabled: true) // ✅ Reads env vars
);
Performance considerations:
W3C (Recommended for most scenarios):
.WithPropagation("W3C") // Default, most interoperable
B3 (For Zipkin compatibility):
.WithPropagation("B3")
Jaeger (For Jaeger-only environments):
.WithPropagation("Jaeger")
AWS (For AWS X-Ray integration):
.WithPropagation("AWS")
.WithAwsInstrumentation(enabled: true)
Choose appropriate metric types:
var meter = new Meter("MyApp.Service");
// ✅ Counter: For incrementing values (requests, errors, items processed)
var requestCounter = meter.CreateCounter<long>("requests.total");
requestCounter.Add(1);
// ✅ Histogram: For distributions (latency, request size, response time)
var latencyHistogram = meter.CreateHistogram<double>("request.duration", "ms");
latencyHistogram.Record(125.5);
// ✅ UpDownCounter: For values that go up and down (queue depth, active connections)
var activeConnections = meter.CreateUpDownCounter<long>("connections.active");
activeConnections.Add(1); // Connection opened
activeConnections.Add(-1); // Connection closed
// ✅ ObservableGauge: For current state snapshots (memory usage, CPU %)
var memoryGauge = meter.CreateObservableGauge("memory.usage", () => GC.GetTotalMemory(false));
Metric naming conventions:
// ✅ Good metric names (use dots, lowercase, descriptive)
"http.server.requests.total"
"cache.hits.total"
"db.query.duration"
"queue.messages.pending"
// ❌ Bad metric names (avoid spaces, uppercase, vague names)
"RequestCount"
"Cache Hits"
"time"
"value"
Export interval recommendations:
// Development: Fast feedback
.WithMetrics(enabled: true, exportIntervalMs: 10000) // 10 seconds
// Production: Balance freshness vs. overhead
.WithMetrics(enabled: true, exportIntervalMs: 60000) // 60 seconds (recommended)
// High-frequency metrics: For critical services
.WithMetrics(enabled: true, exportIntervalMs: 30000) // 30 seconds
Enable during initial setup and troubleshooting:
// Development: Always enable
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithDiagnosticLogging(enabled: true)
.WithValidation(enabled: true)
);
// Production: Disable after telemetry is working
services.AddDavasorusTelemetry(telemetry => telemetry
.WithServiceName("MyService")
.WithDiagnosticLogging(enabled: false) // Reduce console noise
.WithValidation(enabled: true) // Keep validation enabled
);
When to enable diagnostic logging:
Use descriptive, hierarchical names:
// ✅ Good activity names (action.object or component.operation)
ActivitySource.StartActivitySafe("Cache.Get");
ActivitySource.StartActivitySafe("Database.Query");
ActivitySource.StartActivitySafe("Queue.Send");
ActivitySource.StartActivitySafe("Service.ProcessOrder");
// ❌ Bad activity names (too vague or too specific)
ActivitySource.StartActivitySafe("Operation");
ActivitySource.StartActivitySafe("DoWork");
ActivitySource.StartActivitySafe("GetUserFromDatabaseById123"); // Too specific
Follow semantic conventions:
// HTTP operations
activity.SetHttpAttributes(method: "POST", url: "/api/users", statusCode: 201);
// Database operations
activity.SetDatabaseAttributes(system: "sqlserver", name: "MyDB", operation: "SELECT");
// Cache operations (custom convention)
activity.AddTagSafe("cache.operation", "get");
activity.AddTagSafe("cache.key", cacheKey);
activity.AddTagSafe("cache.hit", wasHit);
*Safe extension methods - They're null-safe and won't failCheck telemetry is enabled:
.WithEnabled(true)
Verify activity source patterns match:
.AddActivitySource("Davasorus.Utility.*")
.AddActivitySource("MyApp.*") // Add your patterns
Confirm exporter is configured:
.WithConsoleExporter() // For debugging
// OR
.WithOtlpExporter("http://localhost:4317")
Check sampling isn't excluding traces:
.WithSampling("AlwaysOn") // Temporarily use 100% sampling
Enable diagnostic logging:
.WithDiagnosticLogging(enabled: true)
Review console output for configuration issues.
Validate OTLP endpoint is reachable:
curl http://localhost:4317 # gRPC endpoint
curl http://localhost:4318 # HTTP endpoint
Possible causes:
Solutions:
// 1. Ensure exporter is configured
.WithConsoleExporter()
// 2. Verify pattern matches
var source = ActivitySourceHelper.Create("Cache", "Memory");
// Creates: "Davasorus.Utility.Cache.Memory"
// Pattern must match: .AddActivitySource("Davasorus.Utility.*")
// 3. Check if enabled
if (ActivitySourceHelper.IsEnabled(source))
{
// Telemetry is working
}
Possible causes:
using statements to properly dispose activitiesSolutions:
// 1. Always use 'using' for activity disposal
using var activity = source.StartActivitySafe("Operation");
// 2. Ensure propagation format matches across services
.WithPropagation("W3C") // Use same format everywhere
// 3. For HTTP calls, verify traceparent header exists
var request = new HttpRequestMessage();
// OpenTelemetry automatically adds traceparent header
// 4. For background jobs, use StartRootActivity
using var scope = source.StartRootActivity("Job.Execute");
var activity = scope.Activity;
Check metrics are enabled:
.WithMetrics(enabled: true)
Verify meter patterns match:
.AddMeter("MyApp.*")
// In your code:
var meter = new Meter("MyApp.Service"); // Must match pattern
Confirm metric export interval:
.WithMetrics(enabled: true, exportIntervalMs: 60000) // 60 seconds
Metrics are exported on interval, not immediately.
Check OTLP endpoint supports metrics: Some collectors require separate endpoints for traces vs. metrics.
Possible causes:
Solutions:
// 1. Reduce queue size
.WithBatchExport(
maxQueueSize: 1024, // Reduce from default 2048
maxBatchSize: 256 // Reduce from default 512
)
// 2. Enable sampling
.WithSampling("ParentBased", ratio: 0.1) // Only keep 10%
// 3. Reduce export delay
.WithBatchExport(scheduledDelayMs: 2000) // Export more frequently
Check exporter timeout:
.WithBatchExport(exportTimeoutMs: 30000) // 30 seconds
Verify endpoint URL format:
// ✅ Correct formats
"http://localhost:4317" // gRPC
"https://collector:4317" // gRPC with TLS
"http://localhost:4318/v1/traces" // HTTP/Protobuf
// ❌ Incorrect formats
"localhost:4317" // Missing http://
"http://localhost:4317/v1/traces" // Path not needed for gRPC
Enable diagnostic logging:
.WithDiagnosticLogging(enabled: true)
Check compression compatibility:
.WithCompression(enabled: false) // Disable if collector doesn't support it
Check environment variables:
# Linux/Mac
export ASPNETCORE_ENVIRONMENT=Production
export DOTNET_ENVIRONMENT=Production
# Windows
set ASPNETCORE_ENVIRONMENT=Production
set DOTNET_ENVIRONMENT=Production
# Verify
echo $ASPNETCORE_ENVIRONMENT # Linux/Mac
echo %ASPNETCORE_ENVIRONMENT% # Windows
Or explicitly set namespace:
.WithServiceNamespace("Production")
.WithEnvironmentDetection(enabled: false) // Disable auto-detection
Verify running in container:
# Check if container ID file exists
cat /proc/self/cgroup # Linux containers
Enable container detection:
.WithContainerResourceDetection(enabled: true)
Check resource attributes in exported spans:
Look for container.id, container.name, container.image.name
Common validation failures:
// ❌ Invalid sampling ratio
.WithSampling("TraceIdRatio", ratio: 1.5) // Must be 0.0-1.0
// ❌ Invalid URL
.WithOtlpExporter("localhost:4317") // Missing http://
// ❌ Batch size exceeds queue size
.WithBatchExport(maxQueueSize: 512, maxBatchSize: 1024) // Batch > Queue
// ❌ Invalid export interval
.WithMetrics(enabled: true, exportIntervalMs: -1000) // Must be positive
Solution: Enable validation to catch issues early:
.WithValidation(enabled: true) // Enabled by default
Note: Some packages are in beta stage. They are production-ready but may have API changes in future releases. The core tracing functionality uses stable OpenTelemetry 1.15.2 packages.
Copyright © Tyler Technologies. All rights reserved.
For issues, questions, or contributions, please open an issue in the repository or contact your development team.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 5 NuGet packages that depend on Davasorus.Utility.DotNet.Telemetry:
| Package | Downloads |
|---|---|
|
Davasorus.Utility.DotNet.Encryption
Data Encryption and decryption for TEPS Utilities |
|
|
Davasorus.Utility.DotNet.SQS
Amazon SQS interaction for TEPS Utilities |
|
|
Davasorus.Utility.DotNet.Auth
Handles Authentication for TEPS Utilities |
|
|
Davasorus.Utility.DotNet.Api
API Interaction for TEPS Utilities with generic deserialization, configurable error reporting, and improved DI configuration. Supports REST, GraphQL, gRPC, WebSocket, SignalR, and SSE protocols. |
|
|
Davasorus.Utility.DotNet.Cache
Unified caching abstraction for .NET applications with support for in-memory, SQL Server, and SQLite cache backends. |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2026.2.3.2 | 353 | 6/13/2026 |
| 2026.2.3.1 | 177 | 6/13/2026 |
| 2026.2.2.12 | 2,819 | 5/31/2026 |
| 2026.2.2.11 | 101 | 5/31/2026 |
| 2026.2.2.10 | 2,513 | 5/30/2026 |
| 2026.2.2.9 | 2,102 | 5/25/2026 |
| 2026.2.2.8 | 573 | 5/23/2026 |
| 2026.2.2.7 | 1,321 | 5/23/2026 |
| 2026.2.2.6 | 1,296 | 5/16/2026 |
| 2026.2.2.5 | 911 | 5/14/2026 |
| 2026.2.2.4 | 868 | 5/13/2026 |
| 2026.2.2.3 | 432 | 5/12/2026 |
| 2026.2.2.2 | 742 | 5/9/2026 |
| 2026.2.2.1 | 2,063 | 5/1/2026 |
| 2026.2.1.3 | 815 | 4/22/2026 |
| 2026.2.1.2 | 197 | 4/16/2026 |
| 2026.2.1.1 | 5,858 | 4/9/2026 |
| 2026.1.3.3 | 2,038 | 3/29/2026 |
| 2026.1.3.2 | 2,764 | 3/12/2026 |
| 2026.1.3.1 | 727 | 3/9/2026 |