Acontplus.Logging
2.0.0
dotnet add package Acontplus.Logging --version 2.0.0
NuGet\Install-Package Acontplus.Logging -Version 2.0.0
<PackageReference Include="Acontplus.Logging" Version="2.0.0" />
<PackageVersion Include="Acontplus.Logging" Version="2.0.0" />
<PackageReference Include="Acontplus.Logging" />
paket add Acontplus.Logging --version 2.0.0
#r "nuget: Acontplus.Logging, 2.0.0"
#:package Acontplus.Logging@2.0.0
#addin nuget:?package=Acontplus.Logging&version=2.0.0
#tool nuget:?package=Acontplus.Logging&version=2.0.0
Acontplus.Logging
Description
Acontplus.Logging is a comprehensive observability library for .NET applications, combining Serilog for structured logging with OpenTelemetry for distributed tracing and metrics. It provides enterprise-grade observability capabilities with support for multiple backends including Jaeger, Dynatrace, Prometheus, Elasticsearch, and more. Perfect for cloud-native, microservices, and distributed architectures.
Features
📊 OpenTelemetry Observability
- Distributed Tracing: Track request flows across services with context propagation
- Custom Metrics: Counters, histograms, gauges for business and technical metrics
- Activity Sources: Create custom spans for detailed operation tracking
- Automatic Instrumentation: ASP.NET Core, HTTP clients, SQL database calls
- Multiple Exporters: OTLP (Jaeger, Grafana Cloud, etc.), Console, Dynatrace
- Single OTLP config:
EnableOtlpExporterat root covers traces, metrics, and logs — no duplication per signal - Auto-Detection: ServiceName and ServiceVersion from assembly metadata
🔧 Structured Logging (Serilog)
- Multi-Sink Architecture: Console, File, Database, Elasticsearch
- JSON Formatting: Production-ready structured logs
- Custom Enrichers: Timezone, environment, machine name
- Async Logging: High-performance async sinks
- Rolling Files: Automatic log rotation and retention
🌐 Backend Integration
- Jaeger: Distributed tracing UI and analysis via OTLP protocol
- Grafana Cloud / Tempo: Traces and metrics with a free tier
- Dynatrace: Enterprise APM with AI-powered insights (per-signal OTLP endpoints)
- Prometheus: Metrics via OpenTelemetry Collector → Prometheus scrape
- Grafana: Unified dashboards for metrics and traces
- Elasticsearch: ELK stack integration for log analytics
- OTLP: Vendor-neutral telemetry protocol
⚡ Enterprise Features
- ELK Stack Support: Official Elastic.Serilog.Sinks package
- ECS Compliance: Elastic Common Schema adherence
- W3C Trace Context: Standard context propagation
- Sampling Strategies: Configurable trace sampling
- Resource Attributes: Service name, version, namespace
- Exception Tracking: Automatic exception recording in traces
Installation
Install the library via NuGet:
dotnet add package Acontplus.Logging
For ASP.NET Core applications, also install:
dotnet add package Serilog.AspNetCore
For Worker Services or Generic Hosts:
dotnet add package Serilog.Extensions.Hosting
Quick Start
1. Basic Setup with OpenTelemetry
using Acontplus.Logging;
using Serilog;
var builder = WebApplication.CreateBuilder(args);
// Configure Serilog
builder.Host.UseSerilog((context, services, configuration) =>
{
configuration.ConfigureAdvancedLogger(context.Configuration, builder.Environment.EnvironmentName);
});
// Configure OpenTelemetry (tracing + metrics)
builder.Services.AddAdvancedOpenTelemetry(builder.Configuration);
// ServiceName is auto-detected from assembly name if not configured
// ServiceVersion is auto-detected from assembly version if not configured
// Register ActivitySource and Meter for custom instrumentation
builder.Services.AddActivitySource("YourServiceName"); // Optional: overrides auto-detected name
builder.Services.AddMeter("YourServiceName"); // Optional: overrides auto-detected name
// Optional: Register helpers for easier usage
builder.Services.AddSingleton<TracingHelper>();
builder.Services.AddSingleton<MetricsHelper>();
var app = builder.Build();
app.UseSerilogRequestLogging();
app.MapControllers();
app.Run();
2. Configuration (appsettings.json)
{
"AdvancedLogging": {
"EnableLocalFile": true,
"LocalFilePath": "logs/log-.log",
"RollingInterval": "Day",
"RetainedFileCountLimit": 7,
"Formatter": "Json",
"EnableElasticsearchLogging": true,
"ElasticsearchUrl": "http://localhost:9200",
"TimeZoneId": "UTC"
},
"OpenTelemetry": {
"Enabled": true,
// OTLP configured once at root — UseOtlpExporter covers traces, metrics, AND logs in one call.
// ServiceName and ServiceVersion are auto-detected from assembly if not specified.
"EnableOtlpExporter": true,
"OtlpEndpoint": "http://localhost:4317", // gRPC; use http://localhost:4318 for HTTP/protobuf
"OtlpProtocol": "grpc", // grpc (default) or http
"ServiceNamespace": "YourNamespace",
"Tracing": {
"EnableAspNetCoreInstrumentation": true,
"EnableHttpClientInstrumentation": true,
"EnableSqlClientInstrumentation": true
},
"Metrics": {
"EnableAspNetCoreInstrumentation": true,
"EnableHttpClientInstrumentation": true,
"EnableRuntimeInstrumentation": true
}
}
}
Configuration
Configure the logging system by adding the AdvancedLogging section to your appsettings.json:
{
"AdvancedLogging": {
"EnableLocalFile": true,
"Shared": false,
"Buffered": true,
"LocalFilePath": "logs/log-.log",
"RollingInterval": "Day",
"RetainedFileCountLimit": 7,
"FileSizeLimitBytes": 10485760,
"EnableDatabaseLogging": false,
"DatabaseConnectionString": "Server=...",
"EnableElasticsearchLogging": false,
"ElasticsearchUrl": "http://localhost:9200",
"ElasticsearchIndexFormat": "logs-{0:yyyy.MM.dd}",
"ElasticsearchUsername": "elastic",
"ElasticsearchPassword": "your-password",
"TimeZoneId": "America/Guayaquil"
}
}
Usage Examples
Distributed Tracing
using System.Diagnostics;
public class OrderService
{
private readonly ActivitySource _activitySource;
private readonly ILogger<OrderService> _logger;
public OrderService(ActivitySource activitySource, ILogger<OrderService> logger)
{
_activitySource = activitySource;
_logger = logger;
}
public async Task<Order> ProcessOrderAsync(int orderId)
{
// Create a custom span for this operation
using var activity = _activitySource.StartActivity("ProcessOrder");
// Add tags for filtering and analysis
activity?.SetTag("order.id", orderId);
activity?.SetTag("order.priority", "high");
_logger.LogInformation("Processing order {OrderId}", orderId);
try
{
// Simulate processing
var order = await GetOrderAsync(orderId);
await ValidateOrderAsync(order);
await ChargePaymentAsync(order);
activity?.SetStatus(ActivityStatusCode.Ok);
activity?.AddEvent(new ActivityEvent("OrderCompleted"));
return order;
}
catch (Exception ex)
{
// Record exception in trace
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
activity?.RecordException(ex);
_logger.LogError(ex, "Failed to process order {OrderId}", orderId);
throw;
}
}
private async Task<Order> GetOrderAsync(int orderId)
{
// This creates a child span automatically
using var activity = _activitySource.StartActivity("GetOrder");
activity?.SetTag("db.system", "sqlserver");
// Database call (auto-instrumented if SQL instrumentation enabled)
return await _dbContext.Orders.FindAsync(orderId);
}
}
Custom Metrics
using System.Diagnostics.Metrics;
public class PaymentService
{
private readonly Counter<long> _paymentsProcessed;
private readonly Histogram<double> _paymentAmount;
private readonly Histogram<double> _processingDuration;
private readonly ILogger<PaymentService> _logger;
public PaymentService(Meter meter, ILogger<PaymentService> logger)
{
_logger = logger;
// Create custom metrics
_paymentsProcessed = meter.CreateCounter<long>(
"payments.processed",
"payments",
"Total number of payments processed");
_paymentAmount = meter.CreateHistogram<double>(
"payment.amount",
"USD",
"Payment amount in USD");
_processingDuration = meter.CreateHistogram<double>(
"payment.processing.duration",
"ms",
"Payment processing duration");
}
public async Task<PaymentResult> ProcessPaymentAsync(Payment payment)
{
var stopwatch = Stopwatch.StartNew();
try
{
var result = await ChargePaymentGatewayAsync(payment);
// Record metrics
_paymentsProcessed.Add(1,
new KeyValuePair<string, object?>("status", "success"),
new KeyValuePair<string, object?>("gateway", payment.Gateway));
_paymentAmount.Record(payment.Amount,
new KeyValuePair<string, object?>("currency", payment.Currency),
new KeyValuePair<string, object?>("type", payment.Type));
_processingDuration.Record(stopwatch.ElapsedMilliseconds,
new KeyValuePair<string, object?>("gateway", payment.Gateway));
_logger.LogInformation("Payment processed successfully: {PaymentId}", payment.Id);
return result;
}
catch (Exception ex)
{
_paymentsProcessed.Add(1,
new KeyValuePair<string, object?>("status", "failed"),
new KeyValuePair<string, object?>("gateway", payment.Gateway));
_logger.LogError(ex, "Payment processing failed: {PaymentId}", payment.Id);
throw;
}
}
}
Using Helper Classes
public class InventoryService
{
private readonly TracingHelper _tracing;
private readonly MetricsHelper _metrics;
private readonly Counter<long> _stockUpdates;
public InventoryService(TracingHelper tracing, MetricsHelper metrics)
{
_tracing = tracing;
_metrics = metrics;
_stockUpdates = _metrics.CreateCounter<long>("inventory.stock.updates", "updates");
}
public async Task UpdateStockAsync(int productId, int quantity)
{
using var activity = _tracing.StartActivity("UpdateStock");
_tracing.AddTag("product.id", productId);
_tracing.AddTag("quantity", quantity);
try
{
await _repository.UpdateStockAsync(productId, quantity);
_stockUpdates.Add(1,
new KeyValuePair<string, object?>("product_id", productId),
new KeyValuePair<string, object?>("operation", "update"));
_tracing.AddEvent("StockUpdated");
}
catch (Exception ex)
{
_tracing.RecordException(ex);
throw;
}
}
}
Observability Backends Setup
Jaeger (Distributed Tracing)
Jaeger provides distributed tracing with interactive dashboards for analyzing request flows across microservices.
Docker (recommended — includes UI + OTLP receiver):
docker run -d --name jaeger \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
jaegertracing/jaeger:latest
Configuration (appsettings.json):
{
"OpenTelemetry": {
"Enabled": true,
"EnableOtlpExporter": true,
"OtlpEndpoint": "http://localhost:4317",
"OtlpProtocol": "grpc"
}
}
UseOtlpExportersends traces, metrics, and logs to Jaeger automatically — no per-signal config needed.
Access Jaeger UI:
- Open browser: http://localhost:16686
- Search for traces by service name, operation, tags
- Analyze trace timelines and dependencies
Benefits:
- 🔍 Visual trace timeline with span details
- 🌐 Service dependency graph
- 📊 Operation statistics and latencies
- 🔗 Trace comparison and analysis
Dynatrace (Enterprise APM Platform)
Dynatrace is a comprehensive enterprise observability platform with AI-powered insights, automatic discovery, and full-stack monitoring. It supports traces, metrics, and logs via OTLP protocol.
Setup:
Get Dynatrace Environment:
- Sign up for Dynatrace SaaS: https://www.dynatrace.com/trial/
- Note your environment ID:
{your-environment-id}.live.dynatrace.com
Create API Token:
- Go to Settings → Integration → Dynatrace API
- Create token with permissions:
openTelemetryTrace.ingest,metrics.ingest,logs.ingest - Copy the API token
Configuration (appsettings.json):
Note: Dynatrace requires a different OTLP endpoint per signal (traces/metrics/logs). The library detects this automatically and uses per-signal
AddOtlpExporterinstead of the globalUseOtlpExporter. You can also combineEnableOtlpExporter(e.g., for Jaeger) with Dynatrace simultaneously.
{
"OpenTelemetry": {
"Enabled": true,
"Tracing": {
"EnableAspNetCoreInstrumentation": true,
"EnableHttpClientInstrumentation": true,
"EnableSqlClientInstrumentation": true,
"EnableDynatraceExporter": true,
"DynatraceEndpoint": "https://{your-environment-id}.live.dynatrace.com/api/v2/otlp/v1/traces",
"DynatraceApiToken": "dt0c01.***.***.***"
},
"Metrics": {
"EnableAspNetCoreInstrumentation": true,
"EnableHttpClientInstrumentation": true,
"EnableRuntimeInstrumentation": true,
"EnableDynatraceExporter": true,
"DynatraceEndpoint": "https://{your-environment-id}.live.dynatrace.com/api/v2/otlp/v1/metrics",
"DynatraceApiToken": "dt0c01.***.***.***"
},
"Logging": {
"EnableDynatraceExporter": true,
"DynatraceEndpoint": "https://{your-environment-id}.live.dynatrace.com/api/v2/otlp/v1/logs",
"DynatraceApiToken": "dt0c01.***.***.***"
}
}
}
Access Dynatrace:
- Open your Dynatrace environment:
https://{your-environment-id}.live.dynatrace.com - Navigate to:
- Distributed traces: Applications → Distributed traces
- Service flow: Applications → Service flow
- Metrics: Observe and explore → Metrics
- Logs: Observe and explore → Logs
Benefits:
- 🤖 AI-powered insights: Automatic problem detection and root cause analysis
- 🔍 Full-stack observability: From frontend to database
- 📊 Smart dashboards: Pre-built and customizable dashboards
- 🎯 Service dependency mapping: Automatic service topology
- 🚨 Intelligent alerting: AI-driven anomaly detection
- 📈 Business analytics: Custom metrics and business KPIs
- 🔒 Enterprise security: SOC 2, ISO 27001 certified
Prometheus + Grafana (Metrics & Dashboards)
Use OTLP exporter with OpenTelemetry Collector to expose Prometheus metrics endpoint.
1. Run OpenTelemetry Collector with Prometheus:
Create otel-collector-config.yaml:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
exporters:
prometheus:
endpoint: "0.0.0.0:8889"
logging:
loglevel: debug
service:
pipelines:
metrics:
receivers: [otlp]
exporters: [prometheus, logging]
Run OpenTelemetry Collector:
docker run -d --name otel-collector \
-p 4317:4317 -p 4318:4318 -p 8889:8889 \
-v $(pwd)/otel-collector-config.yaml:/etc/otel-collector-config.yaml \
otel/opentelemetry-collector:latest \
--config=/etc/otel-collector-config.yaml
2. Run Prometheus:
Create prometheus.yml:
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'otel-collector'
static_configs:
- targets: ['host.docker.internal:8889']
Run Prometheus:
docker run -d --name prometheus \
-p 9090:9090 \
-v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \
prom/prometheus:latest
3. Run Grafana:
docker run -d --name grafana \
-p 3000:3000 \
grafana/grafana:latest
4. Application Configuration:
{
"OpenTelemetry": {
"Enabled": true,
"EnableOtlpExporter": true,
"OtlpEndpoint": "http://localhost:4317",
"OtlpProtocol": "grpc"
}
}
5. Configure Grafana:
- Open http://localhost:3000 (admin/admin)
- Add Prometheus data source: http://prometheus:9090
- Import dashboard or create custom panels
- Visualize: request rates, latencies, error rates, custom metrics
Recommended Metrics to Monitor:
http_server_request_duration- Request latencieshttp_server_active_requests- Current active requests- Custom business metrics from your application
Complete Observability Stack (All-in-One)
Docker Compose Setup:
Create docker-compose.yml:
version: '3.8'
services:
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- "16686:16686" # Jaeger UI
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
depends_on:
- prometheus
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
ports:
- "9200:9200"
environment:
- discovery.type=single-node
- xpack.security.enabled=false
kibana:
image: docker.elastic.co/kibana/kibana:8.11.0
ports:
- "5601:5601"
depends_on:
- elasticsearch
Run the stack:
docker-compose up -d
Access Points:
- Jaeger UI: http://localhost:16686 (Traces)
- Grafana: http://localhost:3000 (Metrics)
- Prometheus: http://localhost:9090 (Metrics backend)
- Kibana: http://localhost:5601 (Logs)
- Elasticsearch: http://localhost:9200 (Logs backend)
Full Configuration:
{
"AdvancedLogging": {
"EnableLocalFile": true,
"EnableElasticsearchLogging": true,
"ElasticsearchUrl": "http://localhost:9200",
"Formatter": "Json"
},
"OpenTelemetry": {
"Enabled": true,
"EnableOtlpExporter": true,
"OtlpEndpoint": "http://localhost:4317",
"OtlpProtocol": "grpc",
"ServiceName": "YourServiceName",
"ServiceVersion": "1.0.0",
"Tracing": {
"EnableAspNetCoreInstrumentation": true,
"EnableHttpClientInstrumentation": true,
"EnableSqlClientInstrumentation": true
},
"Metrics": {
"EnableAspNetCoreInstrumentation": true,
"EnableHttpClientInstrumentation": true,
"EnableRuntimeInstrumentation": true
}
}
}
Now you have:
- ✅ Distributed Tracing → Jaeger
- ✅ Metrics Monitoring → Prometheus + Grafana
- ✅ Log Analytics → Elasticsearch + Kibana
- ✅ Unified Observability → Complete picture of your system
Configuration
Configuration Options
File Logging
- EnableLocalFile (bool): Enables or disables storing logs in local files.
- Shared (bool): Enables or disables shared log files (multiple processes can write to the same file).
- Buffered (bool): Enables or disables buffered logging for local files (improves performance by writing in chunks).
- LocalFilePath (string): Path to the log file. Supports rolling file patterns.
- RollingInterval (string): Interval to roll log files. Values:
Year,Month,Day,Hour,Minute. - RetainedFileCountLimit (int): Number of historical log files to keep.
- FileSizeLimitBytes (int): Maximum size of a single log file in bytes before it rolls over.
Database Logging
- EnableDatabaseLogging (bool): Enables or disables storing logs in a database.
- DatabaseConnectionString (string): Connection string to the database where logs will be stored.
Elasticsearch Logging
- EnableElasticsearchLogging (bool): Enables or disables storing logs in Elasticsearch for ELK stack integration.
- ElasticsearchUrl (string): URL of the Elasticsearch instance (e.g., "http://localhost:9200").
- ElasticsearchIndexFormat (string): Index format for Elasticsearch (default: "logs-{0:yyyy.MM.dd}").
- ElasticsearchUsername (string): Username for Elasticsearch authentication (optional).
- ElasticsearchPassword (string): Password for Elasticsearch authentication (optional).
General Settings
- TimeZoneId (string): Time zone ID for the custom timestamp enricher (e.g., "America/Guayaquil", "UTC").
Usage
Basic Integration
Integrate Acontplus.Logging in your Program.cs:
using Acontplus.Logging;
using Serilog;
public class Program
{
public static void Main(string[] args)
{
// Bootstrap logger for early startup issues
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateBootstrapLogger();
try
{
var builder = WebApplication.CreateBuilder(args);
var environment = builder.Environment.EnvironmentName;
// Configure Serilog with advanced logging
builder.Host.UseSerilog((hostContext, services, loggerConfiguration) =>
{
loggerConfiguration.ConfigureAdvancedLogger(hostContext.Configuration, environment);
loggerConfiguration.ReadFrom.Configuration(hostContext.Configuration);
loggerConfiguration.ReadFrom.Services(services);
});
// Register logging options
builder.Services.AddAdvancedLoggingOptions(builder.Configuration);
var app = builder.Build();
// Add request logging middleware
app.UseSerilogRequestLogging();
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly.");
}
finally
{
Log.CloseAndFlush();
}
}
}
Advanced Configuration
For more advanced Serilog configuration, add a Serilog section to your appsettings.json:
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning",
"Microsoft.AspNetCore": "Warning"
}
},
"Enrich": [
"FromLogContext",
"WithEnvironmentUserName",
"WithMachineName"
],
"Properties": {
"Application": "YourAppName"
}
}
}
Requirements
- .NET 10 or higher
- Proper write permissions if
EnableLocalFileis enabled - Accessible database if
EnableDatabaseLoggingis enabled - Elasticsearch 8.x+ instance if
EnableElasticsearchLoggingis enabled
ELK Stack Integration
This library provides seamless integration with the ELK (Elasticsearch, Logstash, Kibana) stack for advanced log management and analytics:
Benefits of ELK Integration:
- Centralized Log Management: Aggregate logs from multiple services
- Advanced Search & Analytics: Powerful querying capabilities with Elasticsearch
- Real-time Monitoring: Live dashboards and alerts with Kibana
- Scalability: Handle high-volume logging with distributed architecture
- Visualization: Create custom dashboards and reports
- Alerting: Set up automated alerts based on log patterns
Setup Instructions:
- Install Elasticsearch: Deploy Elasticsearch 8.x+ on your infrastructure
- Configure Kibana: Set up Kibana for visualization and monitoring
- Enable Logging: Set
EnableElasticsearchLogging: truein your configuration - Configure Connection: Provide Elasticsearch URL and credentials
- Monitor: Access Kibana to view and analyze your logs
Example ELK Configuration:
{
"AdvancedLogging": {
"EnableElasticsearchLogging": true,
"ElasticsearchUrl": "https://your-elasticsearch-cluster:9200",
"ElasticsearchIndexFormat": "acontplus-logs-{0:yyyy.MM.dd}",
"ElasticsearchUsername": "elastic",
"ElasticsearchPassword": "your-secure-password"
}
}
Best Practices
Distributed Tracing
Use Meaningful Span Names: Name spans based on operations, not implementation details
// ✅ Good using var activity = _activitySource.StartActivity("ProcessPayment"); // ❌ Bad using var activity = _activitySource.StartActivity("Method1");Add Contextual Tags: Include relevant business and technical context
activity?.SetTag("user.id", userId); activity?.SetTag("order.value", orderTotal); activity?.SetTag("payment.method", "credit_card");Record Exceptions: Always capture exceptions in traces
try { // operation } catch (Exception ex) { activity?.RecordException(ex); activity?.SetStatus(ActivityStatusCode.Error, ex.Message); throw; }Use Activity Events: Mark important milestones
activity?.AddEvent(new ActivityEvent("PaymentAuthorized")); activity?.AddEvent(new ActivityEvent("InventoryReserved"));
Metrics
Choose Appropriate Metric Types:
- Counter: Monotonically increasing values (requests, errors)
- Histogram: Value distributions (latencies, sizes)
- Gauge: Current values (queue size, connections)
- UpDownCounter: Values that can increase/decrease (cache size)
Use Consistent Naming: Follow OpenTelemetry semantic conventions
// Good: descriptive, hierarchical "payment.processing.duration" "inventory.stock.level" "order.value.total"Add Dimensions: Use tags for filtering and grouping
_counter.Add(1, new KeyValuePair<string, object?>("status", "success"), new KeyValuePair<string, object?>("region", "us-west"), new KeyValuePair<string, object?>("payment_type", "card"));Avoid High Cardinality: Don't use unique IDs as tags
// ❌ Bad: creates too many time series counter.Add(1, new KeyValuePair<string, object?>("user_id", userId)); // ✅ Good: use categories counter.Add(1, new KeyValuePair<string, object?>("user_type", "premium"));
Logging
Use Structured Logging: Use message templates, not string interpolation
// ✅ Good _logger.LogInformation("Order {OrderId} processed for customer {CustomerId}", orderId, customerId); // ❌ Bad _logger.LogInformation($"Order {orderId} processed for customer {customerId}");Appropriate Log Levels:
- Trace: Very detailed diagnostic info (rarely used)
- Debug: Debugging information (development)
- Information: General informational messages
- Warning: Unexpected but recoverable situations
- Error: Errors and exceptions
- Critical: Critical failures requiring immediate attention
Correlation: Logs automatically include trace context when using OpenTelemetry
// Logs will include TraceId and SpanId for correlation _logger.LogInformation("Processing started");
Performance
Use Async Sinks: Always enable async logging for better performance
{ "AdvancedLogging": { "Buffered": true } }Configure Sampling: Use sampling for high-traffic services (production)
// In OpenTelemetryExtensions.cs, you can customize: builder.SetSampler(new TraceIdRatioBasedSampler(0.1)); // 10% samplingBatch Exports: Exporters batch telemetry for efficiency (configured by default)
Monitor Resource Usage: Check exporter health and adjust batch sizes if needed
Troubleshooting
Traces Not Appearing in Jaeger
Problem: No traces visible in Jaeger UI
Solutions:
Verify OTLP endpoint is accessible:
curl http://localhost:4317Check application logs for OpenTelemetry errors:
# Look for OpenTelemetry initialization messages dotnet run --configuration DevelopmentVerify configuration:
{ "OpenTelemetry": { "Enabled": true, "Tracing": { "Enabled": true, "EnableOtlpExporter": true, "OtlpEndpoint": "http://localhost:4317" } } }Check if ActivitySource is registered:
builder.Services.AddActivitySource("YourServiceName");Ensure service name matches in configuration and ActivitySource
Metrics Not Scraped by Prometheus
Problem: Prometheus shows target as DOWN or no metrics available
Solutions:
Verify OpenTelemetry Collector is receiving metrics:
curl http://localhost:8889/metricsCheck if OTLP exporter is configured:
{ "OpenTelemetry": { "Metrics": { "EnableOtlpExporter": true, "OtlpEndpoint": "http://localhost:4317" } } }Verify Prometheus is scraping from OpenTelemetry Collector:
scrape_configs: - job_name: 'otel-collector' static_configs: - targets: ['host.docker.internal:8889'] # OpenTelemetry Collector endpointCheck Prometheus UI (http://localhost:9090) → Status → Targets
High Memory Usage
Problem: Application consuming too much memory
Solutions:
Reduce log retention:
{ "AdvancedLogging": { "RetainedFileCountLimit": 3 } }Enable sampling for traces:
builder.SetSampler(new TraceIdRatioBasedSampler(0.1));Reduce metric cardinality (avoid unique values in tags)
Adjust batch sizes in exporters
Logs Not Correlating with Traces
Problem: Cannot find related logs for a trace
Solutions:
- Ensure both logging and tracing are enabled
- Use structured logging (
ILogger) - Check that logs include
TraceIdandSpanIdfields - Verify log formatter supports JSON (for Elasticsearch/Kibana)
{
"AdvancedLogging": {
"Formatter": "Json"
}
}
Support & Resources
- GitHub Issues: https://github.com/acontplus/acontplus-dotnet-libs/issues
- OpenTelemetry Docs: https://opentelemetry.io/docs/
- Jaeger Docs: https://www.jaegertracing.io/docs/
- Prometheus Docs: https://prometheus.io/docs/
- Serilog Docs: https://serilog.net/
Contributing
Contributions are welcome! Please submit pull requests or open issues for bugs and feature requests.
License
This library is licensed under the MIT License. See LICENSE file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Elastic.Serilog.Sinks (>= 9.0.0)
- Microsoft.Data.SqlClient (>= 7.0.1)
- Microsoft.IdentityModel.Tokens (>= 8.18.0)
- OpenTelemetry (>= 1.15.3)
- OpenTelemetry.Api (>= 1.15.3)
- OpenTelemetry.Exporter.Console (>= 1.15.3)
- OpenTelemetry.Exporter.OpenTelemetryProtocol (>= 1.15.3)
- OpenTelemetry.Extensions.Hosting (>= 1.15.3)
- OpenTelemetry.Instrumentation.AspNetCore (>= 1.15.2)
- OpenTelemetry.Instrumentation.Http (>= 1.15.1)
- OpenTelemetry.Instrumentation.SqlClient (>= 1.15.2)
- Serilog (>= 4.3.1)
- Serilog.Enrichers.Environment (>= 3.0.1)
- Serilog.Extensions.Logging (>= 10.0.0)
- Serilog.Formatting.Compact (>= 3.0.0)
- Serilog.Settings.Configuration (>= 10.0.0)
- Serilog.Sinks.Async (>= 2.1.0)
- Serilog.Sinks.Console (>= 6.1.1)
- Serilog.Sinks.File (>= 7.0.0)
- Serilog.Sinks.MSSqlServer (>= 9.0.3)
- System.IdentityModel.Tokens.Jwt (>= 8.18.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.0 | 89 | 5/17/2026 |
| 1.2.8 | 108 | 5/3/2026 |
| 1.2.7 | 100 | 4/16/2026 |
| 1.2.6 | 153 | 3/17/2026 |
| 1.2.5 | 143 | 2/22/2026 |
| 1.2.4 | 152 | 1/16/2026 |
| 1.2.3 | 324 | 12/16/2025 |
| 1.2.2 | 259 | 12/11/2025 |
| 1.2.1 | 225 | 11/27/2025 |
| 1.2.0 | 229 | 11/23/2025 |
| 1.1.4 | 240 | 11/5/2025 |
| 1.1.3 | 240 | 11/2/2025 |
| 1.1.2 | 229 | 10/23/2025 |
| 1.1.1 | 239 | 9/9/2025 |
| 1.1.0 | 239 | 9/4/2025 |
| 1.0.8 | 299 | 8/7/2025 |
| 1.0.7 | 293 | 8/5/2025 |
| 1.0.6 | 591 | 7/23/2025 |
| 1.0.5 | 161 | 7/11/2025 |
| 1.0.4 | 208 | 7/9/2025 |
Major observability upgrade: Added full OpenTelemetry support with
distributed tracing (Jaeger, Dynatrace), metrics (Prometheus, Dynatrace), and OTLP
protocol.
Features ActivitySource for tracing, Meter for metrics, automatic instrumentation for ASP.NET
Core,
HTTP, and SQL. Auto-detection of ServiceName and ServiceVersion from assembly metadata.
Includes official Elastic.Serilog.Sinks integration for ELK stack, ECS compliance,
and enterprise-grade observability patterns. Optimized for cloud-native and microservices
architectures.