/

OpenTelemetry Configuration

Configure OpenTelemetry to work with ByteHide Logger for comprehensive observability across your .NET applications. This setup enables automatic trace correlation and standardized log export.

Basic Setup

Program.cs Configuration

C#
using OpenTelemetry;
using OpenTelemetry.Logs;
using OpenTelemetry.Trace;
using Bytehide.Logger;

var builder = WebApplication.CreateBuilder(args);

// Configure ByteHide Logger
Log.SetProjectToken("your-project-token");

// Configure OpenTelemetry
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddSource("YourApp"))
    .WithLogging(logging => logging
        .AddConsoleExporter())
    .WithMetrics(metrics => metrics
        .AddAspNetCoreInstrumentation());

var app = builder.Build();

Activity Source Setup

C#
public class OrderService
{
    private static readonly ActivitySource ActivitySource = new("OrderService");
    
    public async Task ProcessOrderAsync(Order order)
    {
        using var activity = ActivitySource.StartActivity("ProcessOrder");
        activity?.SetTag("order.id", order.Id);
        
        Log.WithCorrelationId(Activity.Current?.TraceId.ToString())
           .WithMetadata("orderId", order.Id)
           .Info("Starting order processing");
        
        try
        {
            await ValidateOrderAsync(order);
            await SaveOrderAsync(order);
            
            Log.WithCorrelationId(Activity.Current?.TraceId.ToString())
               .Info("Order processing completed");
        }
        catch (Exception ex)
        {
            Log.WithCorrelationId(Activity.Current?.TraceId.ToString())
               .Error("Order processing failed", ex);
            throw;
        }
    }
}

ASP.NET Core Integration

Startup Configuration

C#
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Configure ByteHide Logger
        Log.SetProjectToken(Configuration["ByteHide:ProjectToken"]);
        
        // Add OpenTelemetry
        services.AddOpenTelemetry()
            .WithTracing(builder => builder
                .SetSampler(new AlwaysOnSampler())
                .AddAspNetCoreInstrumentation(options =>
                {
                    options.RecordException = true;
                    options.Filter = httpContext => 
                        !httpContext.Request.Path.StartsWithSegments("/health");
                })
                .AddHttpClientInstrumentation()
                .AddEntityFrameworkCoreInstrumentation()
                .AddJaegerExporter());
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // Add correlation middleware
        app.UseMiddleware<CorrelationMiddleware>();
        
        // Other middleware...
    }
}

Correlation Middleware

C#
public class CorrelationMiddleware
{
    private readonly RequestDelegate _next;
    
    public CorrelationMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    
    public async Task InvokeAsync(HttpContext context)
    {
        var correlationId = context.TraceIdentifier;
        
        // Set correlation ID for all logs in this request
        using var scope = new CorrelationScope(correlationId);
        
        Log.WithCorrelationId(correlationId)
           .WithMetadata("requestPath", context.Request.Path)
           .WithMetadata("requestMethod", context.Request.Method)
           .Info("Request started");
        
        try
        {
            await _next(context);
            
            Log.WithCorrelationId(correlationId)
               .WithMetadata("statusCode", context.Response.StatusCode)
               .Info("Request completed");
        }
        catch (Exception ex)
        {
            Log.WithCorrelationId(correlationId)
               .Error("Request failed", ex);
            throw;
        }
    }
}

Configuration Options

ByteHide Logger Settings

C#
// Configure ByteHide Logger for OpenTelemetry
var logSettings = new LogSettings
{
    Persist = true,
    FilePath = "logs/app.log",
    ConsoleEnabled = true,
    IncludeCallerInfo = true,
    MinimumLevel = LogLevel.Info
};

Log.Configure(logSettings);
Log.SetProjectToken("your-project-token");

OpenTelemetry Trace Configuration

C#
services.AddOpenTelemetry()
    .WithTracing(builder => builder
        .SetSampler(new TraceIdRatioBasedSampler(0.1)) // Sample 10% of traces
        .AddAspNetCoreInstrumentation(options =>
        {
            options.RecordException = true;
            options.EnrichWithHttpRequest = (activity, request) =>
            {
                activity.SetTag("http.request.user_agent", request.Headers.UserAgent.ToString());
            };
            options.EnrichWithHttpResponse = (activity, response) =>
            {
                activity.SetTag("http.response.content_type", response.ContentType);
            };
        })
        .AddHttpClientInstrumentation(options =>
        {
            options.RecordException = true;
        }));

Environment-Specific Configuration

Development Environment

C#
if (builder.Environment.IsDevelopment())
{
    builder.Services.AddOpenTelemetry()
        .WithTracing(tracing => tracing
            .AddConsoleExporter()
            .SetSampler(new AlwaysOnSampler()));
    
    // Enable detailed logging
    Log.Configure(new LogSettings
    {
        ConsoleEnabled = true,
        MinimumLevel = LogLevel.Debug,
        IncludeCallerInfo = true
    });
}

Production Environment

C#
if (builder.Environment.IsProduction())
{
    builder.Services.AddOpenTelemetry()
        .WithTracing(tracing => tracing
            .AddJaegerExporter(options =>
            {
                options.AgentHost = "jaeger-agent";
                options.AgentPort = 6831;
            })
            .SetSampler(new TraceIdRatioBasedSampler(0.05))); // 5% sampling
    
    // Production logging settings
    Log.Configure(new LogSettings
    {
        Persist = true,
        FilePath = "/var/log/app/application.log",
        RollingInterval = RollingInterval.Day,
        MinimumLevel = LogLevel.Warn,
        ConsoleEnabled = false
    });
}

Automatic Correlation

HTTP Client Correlation

C#
public class ApiClient
{
    private readonly HttpClient _httpClient;
    
    public ApiClient(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }
    
    public async Task<T> GetAsync<T>(string endpoint)
    {
        using var activity = ActivitySource.StartActivity($"GET {endpoint}");
        var correlationId = Activity.Current?.TraceId.ToString();
        
        Log.WithCorrelationId(correlationId)
           .WithMetadata("endpoint", endpoint)
           .WithTags("http-client", "api-call")
           .Info("Making API request");
        
        try
        {
            var response = await _httpClient.GetAsync(endpoint);
            response.EnsureSuccessStatusCode();
            
            var content = await response.Content.ReadAsStringAsync();
            var result = JsonSerializer.Deserialize<T>(content);
            
            Log.WithCorrelationId(correlationId)
               .WithMetadata("statusCode", (int)response.StatusCode)
               .Info("API request completed");
            
            return result;
        }
        catch (Exception ex)
        {
            Log.WithCorrelationId(correlationId)
               .Error("API request failed", ex);
            throw;
        }
    }
}

Database Operation Correlation

C#
public class OrderRepository
{
    private static readonly ActivitySource ActivitySource = new("OrderRepository");
    
    public async Task<Order> GetOrderAsync(int orderId)
    {
        using var activity = ActivitySource.StartActivity("GetOrder");
        activity?.SetTag("order.id", orderId);
        
        var correlationId = Activity.Current?.TraceId.ToString();
        
        Log.WithCorrelationId(correlationId)
           .WithMetadata("orderId", orderId)
           .WithTags("database", "query")
           .Debug("Querying order from database");
        
        try
        {
            var order = await _context.Orders.FindAsync(orderId);
            
            if (order == null)
            {
                Log.WithCorrelationId(correlationId)
                   .WithMetadata("orderId", orderId)
                   .Warn("Order not found");
                throw new OrderNotFoundException(orderId);
            }
            
            Log.WithCorrelationId(correlationId)
               .Debug("Order retrieved successfully");
            
            return order;
        }
        catch (Exception ex)
        {
            Log.WithCorrelationId(correlationId)
               .Error("Database query failed", ex);
            throw;
        }
    }
}

Configuration Best Practices

Configuration Best Practices

  • Use environment variables: Store sensitive configuration in environment variables
  • Configure sampling: Use appropriate sampling rates for production (5-10%)
  • Enable exception recording: Set RecordException = true for better error tracking
  • Filter health checks: Exclude health check endpoints from tracing
  • Set resource attributes: Include service name, version, and environment information
  • Use correlation IDs consistently: Apply correlation IDs to all related operations

Troubleshooting

Missing Trace Correlation

C#
// Ensure Activity.Current is available
if (Activity.Current == null)
{
    Log.Warn("No active Activity found - trace correlation may be missing");
}

// Use manual correlation ID if needed
var correlationId = Activity.Current?.TraceId.ToString() ?? Guid.NewGuid().ToString();
Log.WithCorrelationId(correlationId).Info("Manual correlation applied");

Performance Impact

C#
// Use appropriate sampling to reduce overhead
services.AddOpenTelemetry()
    .WithTracing(builder => builder
        .SetSampler(new ParentBasedSampler(new TraceIdRatioBasedSampler(0.1))));

// Configure ByteHide Logger for production
Log.Configure(new LogSettings
{
    MinimumLevel = LogLevel.Warn, // Reduce log volume
    DuplicateSuppressionWindow = TimeSpan.FromMinutes(5) // Suppress duplicates
});

Next Steps

Previous
Overview