diff --git a/README.md b/README.md index 2586cf9..7b56f95 100644 --- a/README.md +++ b/README.md @@ -3585,6 +3585,357 @@ finally } ``` +## 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() + .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 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(); + + var sp = services.BuildServiceProvider(); + var orderService = sp.GetRequiredService(); + var order = await orderService.ProcessOrderAsync("ORD-001", 99.99m); + + var telemetry = sp.GetRequiredService(); + 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 + { + ["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(); +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 +{ + { "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")); +} +``` + ## Debugging Logging Problems Check diagnostics first: @@ -3607,380 +3958,4 @@ If events are missing: 4. Check remote destination connectivity. 5. Enable retry/failover. -## Summary - -EonaCat.LogStack combines: - -- High performance logging -- Structured events -- Multi-target flows -- Runtime telemetry -- Distributed tracing support -- Diagnostics -- Resilience features -- 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() - .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 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(); - - var sp = services.BuildServiceProvider(); - var orderService = sp.GetRequiredService(); - var order = await orderService.ProcessOrderAsync("ORD-001", 99.99m); - - var telemetry = sp.GetRequiredService(); - 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 - { - ["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(); -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 -{ - { "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 +--- \ No newline at end of file