LogTide

C# / .NET SDK

Official .NET SDK for LogTide with automatic batching, retry logic with exponential backoff, circuit breaker pattern, query API, distributed tracing, and ASP.NET Core middleware support.

Installation

.NET CLI

dotnet add package LogTide.SDK

Package Manager

Install-Package LogTide.SDK

Quick Start

using LogTide.SDK;
using LogTide.SDK.Models;

var client = new LogTideClient(new ClientOptions
{
  ApiUrl = "http://localhost:8080",
  ApiKey = "lp_your_api_key_here"
});

// Send logs
client.Info("api-gateway", "Server started", new() { ["port"] = 3000 });
client.Error("database", "Connection failed", new Exception("Timeout"));

// Graceful shutdown
await client.DisposeAsync();

Features

  • ✅ Automatic batching with configurable size and interval
  • ✅ Retry logic with exponential backoff
  • ✅ Circuit breaker pattern for fault tolerance
  • ✅ Max buffer size with drop policy to prevent memory leaks
  • ✅ Query API for searching and filtering logs
  • ✅ Trace ID context for distributed tracing
  • ✅ Global metadata added to all logs
  • ✅ Structured error serialization
  • ✅ Internal metrics (logs sent, errors, latency)
  • ✅ ASP.NET Core middleware for auto-logging HTTP requests
  • ✅ Dependency injection support
  • ✅ Full async/await support
  • ✅ Thread-safe

Configuration

var client = new LogTideClient(new ClientOptions
{
  // Required
  ApiUrl = "http://localhost:8080",
  ApiKey = "lp_your_api_key_here",

  // Batching
  BatchSize = 100,              // Max logs per batch (default: 100)
  FlushIntervalMs = 5000,       // Flush interval in ms (default: 5000)

  // Buffer management
  MaxBufferSize = 10000,        // Max logs in buffer (default: 10000)

  // Retry with exponential backoff (1s → 2s → 4s)
  MaxRetries = 3,               // Max retry attempts (default: 3)
  RetryDelayMs = 1000,          // Initial retry delay (default: 1000)

  // Circuit breaker
  CircuitBreakerThreshold = 5,  // Failures before circuit opens (default: 5)
  CircuitBreakerResetMs = 30000, // Circuit reset timeout (default: 30000)

  // Metrics & debugging
  EnableMetrics = true,         // Enable metrics collection (default: true)
  Debug = false,                // Enable debug logging (default: false)

  // Global context
  GlobalMetadata = new()
  {
      ["env"] = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"),
      ["version"] = "1.0.0",
      ["hostname"] = Environment.MachineName
  },

  // Auto trace IDs
  AutoTraceId = false,          // Auto-generate trace IDs (default: false)

  // HTTP settings
  HttpTimeoutSeconds = 30       // HTTP request timeout (default: 30)
});

Logging Methods

Basic Logging

// Log levels: debug, info, warn, error, critical
client.Debug("service-name", "Debug message");
client.Info("service-name", "Info message", new() { ["userId"] = 123 });
client.Warn("service-name", "Warning message");
client.Error("service-name", "Error message", new() { ["custom"] = "data" });
client.Critical("service-name", "Critical message");

Error Logging with Auto-Serialization

The SDK automatically serializes exceptions with full stack traces:

try
{
  throw new InvalidOperationException("Database timeout");
}
catch (Exception ex)
{
  // Automatically serializes error with stack trace
  client.Error("database", "Query failed", ex);
}

// Generated metadata:
// {
//   "error": {
//     "name": "InvalidOperationException",
//     "message": "Database timeout",
//     "stack": "at Program.Main() in ..."
//   }
// }

Custom Log Entry

client.Log(new LogEntry
{
  Service = "custom-service",
  Level = LogLevel.Info,
  Message = "Custom log",
  Time = DateTime.UtcNow.ToString("O"),
  Metadata = new() { ["key"] = "value" },
  TraceId = "custom-trace-id"
});

Trace ID Context

Track requests across services with trace IDs for distributed tracing.

Manual Trace ID

client.SetTraceId("request-123");

client.Info("api", "Request received");
client.Info("database", "Querying users");
client.Info("api", "Response sent");

client.SetTraceId(null); // Clear context

Scoped Trace ID

client.WithTraceId("request-456", () =>
{
  client.Info("api", "Processing in context");
  client.Warn("cache", "Cache miss");
});
// Trace ID automatically restored after block

Auto-Generated Trace ID

client.WithNewTraceId(() =>
{
  client.Info("worker", "Background job started");
  client.Info("worker", "Job completed");
});

Query API

Search and retrieve logs programmatically.

Basic Query

var result = await client.QueryAsync(new QueryOptions
{
  Service = "api-gateway",
  Level = LogLevel.Error,
  From = DateTime.UtcNow.AddDays(-1),
  To = DateTime.UtcNow,
  Limit = 100,
  Offset = 0
});

Console.WriteLine($"Found {result.Total} logs");
foreach (var log in result.Logs)
{
  Console.WriteLine($"{log.Time}: {log.Message}");
}
var result = await client.QueryAsync(new QueryOptions
{
  Query = "timeout",
  Limit = 50
});

Get Logs by Trace ID

var logs = await client.GetByTraceIdAsync("trace-123");
Console.WriteLine($"Trace has {logs.Count} logs");

Aggregated Statistics

var stats = await client.GetAggregatedStatsAsync(new AggregatedStatsOptions
{
  From = DateTime.UtcNow.AddDays(-7),
  To = DateTime.UtcNow,
  Interval = "1h", // "1m" | "5m" | "1h" | "1d"
  Service = "api-gateway" // Optional
});

Console.WriteLine("Time series:");
foreach (var entry in stats.Timeseries)
{
  Console.WriteLine($"  {entry.Bucket}: {entry.Total} logs");
}

Metrics

Track SDK performance and health with built-in metrics.

var metrics = client.GetMetrics();

Console.WriteLine($"Logs sent: {metrics.LogsSent}");
Console.WriteLine($"Logs dropped: {metrics.LogsDropped}");
Console.WriteLine($"Errors: {metrics.Errors}");
Console.WriteLine($"Retries: {metrics.Retries}");
Console.WriteLine($"Avg latency: {metrics.AvgLatencyMs}ms");
Console.WriteLine($"Circuit breaker trips: {metrics.CircuitBreakerTrips}");

// Get circuit breaker state
Console.WriteLine($"Circuit state: {client.GetCircuitBreakerState()}"); // Closed | Open | HalfOpen

// Reset metrics
client.ResetMetrics();

ASP.NET Core Integration

Setup with Dependency Injection

Add LogTide to your ASP.NET Core application with full middleware support:

// Program.cs
using LogTide.SDK;
using LogTide.SDK.Middleware;
using LogTide.SDK.Models;

var builder = WebApplication.CreateBuilder(args);

// Add LogTide
builder.Services.AddLogTide(new ClientOptions
{
  ApiUrl = builder.Configuration["LogTide:ApiUrl"]!,
  ApiKey = builder.Configuration["LogTide:ApiKey"]!,
  GlobalMetadata = new()
  {
      ["env"] = builder.Environment.EnvironmentName
  }
});

var app = builder.Build();

// Add middleware for auto-logging HTTP requests
app.UseLogTide(options =>
{
  options.ServiceName = "my-api";
  options.LogRequests = true;
  options.LogResponses = true;
  options.LogErrors = true;
  options.SkipHealthCheck = true;
  options.SkipPaths.Add("/metrics");
});

app.MapGet("/", () => "Hello World!");

app.Run();

Middleware Options

Option Type Default Description
ServiceName string "aspnet-api" Service name in logs
LogRequests bool true Log incoming requests
LogResponses bool true Log outgoing responses
LogErrors bool true Log unhandled exceptions
IncludeHeaders bool false Include request headers
SkipHealthCheck bool true Skip /health endpoints
SkipPaths HashSet<string> {}{'}' Paths to skip
TraceIdHeader string "X-Trace-Id" Header for trace ID

Using LogTide in Controllers

[ApiController]
[Route("[controller]")]
public class WeatherController : ControllerBase
{
  private readonly LogTideClient _logger;

  public WeatherController(LogTideClient logger)
  {
      _logger = logger;
  }

  [HttpGet]
  public IActionResult Get()
  {
      _logger.Info("weather-api", "Fetching weather data");

      try
      {
          // ... business logic
          return Ok(new { Temperature = 25 });
      }
      catch (Exception ex)
      {
          _logger.Error("weather-api", "Failed to fetch weather", ex);
          throw;
      }
  }
}

Best Practices

1. Always Dispose on Shutdown
Always call client.DisposeAsync() on shutdown to ensure buffered logs are flushed. With ASP.NET Core, register a shutdown handler:
app.Lifetime.ApplicationStopping.Register(async () =>
{
  var logger = app.Services.GetRequiredService<LogTideClient>();
  await logger.FlushAsync();
});
2. Use Global Metadata
Set global metadata (environment, version, hostname) at initialization to avoid repeating it in every log call.
3. Enable Debug Mode in Development
var client = new LogTideClient(new ClientOptions
{
  ApiUrl = "...",
  ApiKey = "...",
  Debug = builder.Environment.IsDevelopment()
});
4. Monitor Metrics in Production
// Periodic health check
_ = Task.Run(async () =>
{
  while (true)
  {
      await Task.Delay(TimeSpan.FromMinutes(1));

      var metrics = client.GetMetrics();

      if (metrics.LogsDropped > 0)
      {
          Console.WriteLine($"Warning: {metrics.LogsDropped} logs dropped");
      }

      if (client.GetCircuitBreakerState() == CircuitState.Open)
      {
          Console.WriteLine("Error: Circuit breaker is OPEN!");
      }
  }
});

Supported Frameworks

  • .NET 6.0
  • .NET 7.0
  • .NET 8.0
Esc

Type to search across all documentation pages