This commit is contained in:
2026-07-21 11:46:29 +02:00
committed by Jeroen Saey
parent 5d43be8657
commit 9484891cc0
12 changed files with 3188 additions and 3 deletions
+372 -3
View File
@@ -1,7 +1,13 @@
# EonaCat.LogStack
# EonaCat.LogStack 📊
**EonaCat.LogStack** is a flow-based, high-performance logging library for .NET, designed for zero-allocation logging paths and superior memory efficiency.
It features a rich fluent API for routing log events to dozens of destinations - from console and file to Slack, Discord, Redis, Elasticsearch, and beyond.
**EonaCat.LogStack** is a flow-based, high-performance logging library for .NET built for production environments. It features zero-allocation hot paths, superior memory efficiency, and a rich fluent API for routing log events to 50+ destinations including console, file, Slack, Discord, Redis, Elasticsearch, databases, and more.
**Comprehensive Suite Includes:**
- **Core Library** - High-performance logging engine with flows and boosters
- **LogClient** - Centralized remote logging client for distributed systems
- **Status Service** - Self-hosted monitoring, log aggregation, and health dashboard
- **Windows Event Log Flow** - Enterprise Windows event log integration
- **OpenTelemetry Flow** - Observability and distributed tracing support
## Features
@@ -3615,3 +3621,366 @@ EonaCat.LogStack combines:
- Security-focused audit logging
The library is designed to scale from small console tools to distributed production services.
---
## Complete Code Examples
### Example 1: Production ASP.NET Core Application
```csharp
using EonaCat.LogStack;
// In Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add EonaCat logging
builder.Services.AddEonaCatLogging(config =>
{
config
.WithApplicationName("MyApi")
.WithMinimumLevel(LogLevel.Information)
.WriteToConsole(useColors: true)
.WriteToFile(
directory: "./logs",
maxFileSize: 100 * 1024 * 1024,
maxDirectorySize: 5L * 1024 * 1024 * 1024,
compression: CompressionFormat.GZip)
.WriteToSlack(
webhookUrl: builder.Configuration["Slack:WebhookUrl"],
minimumLevel: LogLevel.Error)
.WriteToElasticSearch(
elasticSearchUrl: builder.Configuration["Elasticsearch:Url"],
indexName: "myapi-logs")
.BoostWithMachineName()
.BoostWithProcessId()
.BoostWithCorrelationId()
.BoostWithCallerInfo();
});
// Enable advanced telemetry
builder.Services.AddEonaCatNamedLoggers(new LoggerDIOptions
{
EnableTelemetry = true,
EnableTracing = true,
EnablePerformanceMonitoring = true,
EnableHealthMonitoring = true
});
builder.Services.AddEonaCatTracing();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
var logger = app.Services.GetRequiredService<ILoggerFactory>()
.CreateLogger("Application");
logger.Information("Application starting",
("Version", "1.0.0"),
("Environment", app.Environment.EnvironmentName));
app.Run();
```
### Example 2: Console Application with Multiple Flows
```csharp
using EonaCat.LogStack;
class Program
{
static async Task Main(string[] args)
{
await using var logger = new LogBuilder("ConsoleApp")
.WithMinimumLevel(LogLevel.Debug)
.WithTimestampMode(TimestampMode.Utc)
.WriteToConsole(useColors: true)
.WriteToFile("./logs", filePrefix: "app", maxFileSize: 50 * 1024 * 1024)
.WriteToAudit("./audit", auditLevel: AuditLevel.WarningAndAbove)
.WriteToMemory(capacity: 100)
.WriteToEmail(
smtpHost: "smtp.gmail.com", smtpPort: 587, useSsl: true,
username: "email@gmail.com", password: "app-password",
from: "logs@company.com", to: "admin@company.com",
subjectPrefix: "[CRITICAL]", minimumLevel: LogLevel.Critical)
.RedisFlow(host: "redis.local", channel: "myapp:logs")
.BoostWithMachineName()
.BoostWithProcessId()
.BoostWithCorrelationId()
.Build();
try
{
logger.Information("Application started");
await ProcessDataAsync(logger);
logger.Information("Application completed successfully");
}
catch (Exception ex)
{
logger.Critical(ex, "Unhandled exception");
}
finally
{
await logger.FlushAsync();
}
}
static async Task ProcessDataAsync(ILogger logger)
{
logger.Information("Processing started");
logger.Debug("Starting batch", ("BatchId", 123), ("Count", 5000));
await Task.Delay(1000);
logger.Information("Batch processed", ("Imported", 4998), ("Errors", 2));
}
}
```
### Example 3: Service with Dependency Injection & Telemetry
```csharp
using EonaCat.LogStack;
using Microsoft.Extensions.DependencyInjection;
public class OrderService
{
private readonly ILogger _logger;
private readonly ITelemetryAggregator _telemetry;
private readonly ISpanFactory _spans;
public OrderService(ILogger logger, ITelemetryAggregator telemetry, ISpanFactory spans)
{
_logger = logger;
_telemetry = telemetry;
_spans = spans;
}
public async Task<Order> ProcessOrderAsync(string orderId, decimal amount)
{
using var span = _spans.CreateSpanScope("ProcessOrder");
span.SetAttribute("OrderId", orderId);
try
{
_logger.Information("Processing order", ("OrderId", orderId), ("Amount", amount));
_telemetry.RecordCounter("orders.started", 1);
var order = new Order { Id = orderId, Amount = amount };
await Task.Delay(500);
_logger.Information("Order processed", ("Status", "Completed"));
_telemetry.RecordCounter("orders.completed", 1);
_telemetry.RecordGauge("order.revenue", amount);
return order;
}
catch (Exception ex)
{
_telemetry.RecordCounter("orders.failed", 1);
span.RecordException(ex);
_logger.Error(ex, "Order processing failed", ("OrderId", orderId));
throw;
}
}
}
class Program
{
static async Task Main(string[] args)
{
var services = new ServiceCollection();
services.AddEonaCatLogging(config =>
{
config
.WithApplicationName("OrderService")
.WriteToConsole()
.WriteToFile("./logs")
.WriteToDatabase(
connectionFactory: () => new SqlConnection("Server=.;Database=Logs;"),
tableName: "OrderLogs")
.BoostWithCorrelationId();
});
services.AddEonaCatTelemetryAggregation();
services.AddEonaCatTracing();
services.AddScoped<OrderService>();
var sp = services.BuildServiceProvider();
var orderService = sp.GetRequiredService<OrderService>();
var order = await orderService.ProcessOrderAsync("ORD-001", 99.99m);
var telemetry = sp.GetRequiredService<ITelemetryAggregator>();
var snapshot = telemetry.GetSnapshot();
Console.WriteLine($"Completed: {snapshot.RecordedMetrics["orders.completed"]}");
}
}
record Order { public required string Id { get; init; } public required decimal Amount { get; init; } }
```
### Example 4: Advanced Resilience Patterns
```csharp
using EonaCat.LogStack;
await using var logger = new LogBuilder("ResilientApp")
.WriteToConsole()
// Retry with exponential backoff
.WriteToRetry(
primary: cfg => cfg.WriteToHttp("https://primary-logs.company.com/ingest", batchSize: 50),
maxRetries: 3, initialDelayMs: 1000, maxDelayMs: 30000)
// Failover to secondary
.WriteToFailover(
primary: cfg => cfg.WriteToElasticSearch("https://es-primary.company.com:9200", "logs"),
secondary: cfg => cfg.WriteToFile("./logs/failover"),
recoveryCheckIntervalSeconds: 60)
// Rate limiting with deduplication
.WriteToThrottled(
target: cfg => cfg.WriteToSlack("https://hooks.slack.com/...", LogLevel.Warning),
requestsPerSecond: 10, enableDeduplication: true, minimumLevel: LogLevel.Warning)
// Circuit breaker
.WriteToCircuitBreaker(
inner: cfg => cfg.WriteToDatabase(() => new SqlConnection("..."), "Logs"),
failureThreshold: 5, successThresholdToClose: 3, timeoutSeconds: 60)
.Build();
logger.Information("Started with resilience");
logger.Error(new Exception("Test"), "Error occurred");
```
### Example 5: Remote Logging with LogClient
```csharp
using EonaCat.LogStack.LogClient;
var clientOptions = new LogCentralOptions
{
ServerUrl = "https://logcentral.company.com",
ApiKey = "your-api-key",
ApplicationName = "DistributedService",
ApplicationVersion = "2.1.0",
Environment = "Production",
BatchSize = 100,
FlushIntervalSeconds = 5
};
await using var logger = new LogBuilder("DistributedApp")
.WriteToConsole()
.WriteToFile("./logs")
.WriteToHttp(
endpoint: $"{clientOptions.ServerUrl}/api/logs/ingest",
batchSize: clientOptions.BatchSize,
headers: new Dictionary<string, string>
{
["X-API-Key"] = clientOptions.ApiKey,
["X-App-Name"] = clientOptions.ApplicationName
})
.BoostWithMachineName()
.Build();
logger.Information("Connected to central logging", ("Server", clientOptions.ServerUrl));
```
### Example 6: Log Processing and Analysis
```csharp
using EonaCat.LogStack;
await using var logger = new LogBuilder("AnalysisApp")
.WriteToConsole()
.WriteToMemory(capacity: 10000)
.Build();
for (int i = 0; i < 100; i++)
{
if (i % 10 == 0)
logger.Warning($"Iteration {i}");
else
logger.Information($"Processing item {i}");
}
var memoryFlow = logger.GetFlowOfType<MemoryFlow>();
var events = memoryFlow.GetEvents();
var errorCount = events.Count(e => e.Level == LogLevel.Error);
var warningCount = events.Count(e => e.Level == LogLevel.Warning);
Console.WriteLine($"Errors: {errorCount}, Warnings: {warningCount}");
```
### Example 7: Custom Modifiers for Universal Enrichment
```csharp
using EonaCat.LogStack;
var requestId = "REQ-" + Guid.NewGuid().ToString().Substring(0, 8);
var userId = "USER-123";
await using var logger = new LogBuilder("EnrichedApp")
.WriteToConsole()
.WriteToFile("./logs")
.AddModifier((ref LogEventBuilder builder) =>
{
builder.WithProperty("RequestId", requestId);
builder.WithProperty("UserId", userId);
builder.WithProperty("Host", Environment.MachineName);
})
.Build();
logger.Information("Operation started");
logger.Warning("Potential issue detected");
logger.Error(new Exception("Failed"), "Operation failed");
// All logs automatically include RequestId, UserId, Host
```
### Example 8: Structured Logging and Complex Properties
```csharp
using EonaCat.LogStack;
await using var logger = new LogBuilder("StructuredApp")
.WriteToConsole()
.WriteToFile("./logs", outputFormat: FileOutputFormat.Json)
.Build();
// Simple tuple properties (fast)
logger.Information("User action",
("UserId", 42),
("Action", "login"),
("Ip", "192.168.1.1"));
// Dictionary properties
var contextDict = new Dictionary<string, object>
{
{ "OrderId", "ORD-123" },
{ "Items", 5 },
{ "Total", 199.99m }
};
logger.Information("Order processed", contextDict);
// Exception with context
try
{
throw new InvalidOperationException("Database connection failed");
}
catch (Exception ex)
{
logger.Error(ex, "Operation failed",
("Retries", 3),
("TimeoutMs", 5000),
("DatabaseName", "ProductDb"));
}
```
---
## Additional Resources
- **[GitHub Repository](https://git.saey.me/EonaCat/EonaCat.logstack)** - Source code and issues
- **[NuGet Package](https://www.nuget.org/packages/EonaCat.LogStack)** - Official package
- **[Supported Targets](#supported-targets)** - .NET versions and frameworks
- **[Installation](#installation)** - Getting started guide