# 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.

## Features

### Core Architecture
- **Flow-based system** - Route log events to multiple destinations simultaneously. Each flow is independent and can be configured with its own level filters and batching strategy.
- **Booster system** - Automatically enrich log events with contextual metadata like machine name, process ID, thread info, memory usage, uptime, correlation IDs, and custom properties.
- **Pre-build modifiers** - Intercept and mutate log events before they reach any flow using a chainable modifier system.
- **Zero-allocation hot path** - `AggressiveInlining` throughout, `StringBuilder` pooling, and `ref`-based builder pattern for minimal GC pressure during logging.
- **Async-first design** - All flows implement `IAsyncDisposable` and `FlushAsync()` for proper async resource cleanup and batching.

### Advanced Features
- **Resilience patterns** - Built-in `RetryFlow` with exponential backoff, `FailoverFlow` for primary/secondary failover, and `ThrottledFlow` for rate limiting with deduplication.
- **Tamper-evident audit** - SHA-256 hash-chained audit files where deleting or modifying any past entry invalidates all subsequent hashes. Built-in integrity verification.
- **Encrypted file logging** - AES-encrypted log files with password protection. Includes `DecryptFile()` utility to decrypt files on demand.
- **Log compression** - Automatic GZip compression of rolled-over log files to conserve disk space.
- **Category/level-based routing** - Split logs into separate files per category or log level for better organization.
- **Rolling buffers** - Circular buffer with trigger-based flushing. When a critical event occurs, buffer context (previous N lines) is forwarded to a secondary flow.
- **Diagnostics & metrics** - Real-time counters for total logged, total dropped, per-flow statistics, and flow-specific performance data.
- **Live level control** - Dynamically change log level at runtime without recreating the logger.
- **Structured logging** - First-class support for structured properties (tuples, dictionaries) that are included in JSON output where applicable.

### Performance & Reliability
- **Configurable backpressure** - Choose between Wait, Drop, or Block strategies when log queues are full.
- **Batch processing** - Most flows support configurable batch sizes and intervals for efficient I/O.
- **Rate limiting & deduplication** - Protect high-latency sinks (email, Slack, HTTP) from log storms using token-bucket rate limiting and optional message deduplication.
- **Memory flow** - Store recent logs in a circular in-memory buffer for quick diagnostics or fallback output.
- **Lazy initialization** - Flows are only initialized when first used, reducing startup overhead.


### Telemetry & Observability

EonaCat.LogStack includes built-in telemetry primitives for production monitoring:

- **Metrics registry** - Low-overhead counters and snapshots for logging throughput.
- **Telemetry snapshots** - Capture service name, environment, runtime/process metrics, log counters and trace activity.
- **Runtime health data** - Export operational status for dashboards and monitoring systems.
- **Tracing support** - Activity/span integration for distributed request correlation.
- **Custom exporters** - Build exporters around telemetry snapshots for your monitoring backend.

Example:
```csharp
var options = new TelemetryOptions
{
    Enabled = true,
    IncludeRuntimeMetrics = true,
    IncludeTraceMetrics = true
};
```

## Supported Targets

- .NET Standard 2.1
- .NET 8.0
- .NET 9.0
- .NET 10.0
- .NET Framework 4.8

## Installation

```bash
dotnet add package EonaCat.LogStack
```

## Quick Start

### Minimal - Console + File

```csharp
await using var logger = LogBuilder.CreateDefault("MyApp");

logger.Information("Application started");
logger.Warning("Low memory warning");
logger.Error(ex, "Unexpected error occurred");

// Automatic cleanup on disposal
```

`CreateDefault` creates a logger writing to both the console and a `./logs` directory, enriched with machine name and process ID.

### Fluent Configuration

Build a fully customized logger using `LogBuilder`:

```csharp
await using var logger = new LogBuilder("MyApp")
    .WithMinimumLevel(LogLevel.Debug)
    .WithTimestampMode(TimestampMode.Utc)
    .WriteToConsole(useColors: true)
    .WriteToFile("./logs", filePrefix: "app", maxFileSize: 50 * 1024 * 1024)
    .WriteToSlack("https://hooks.slack.com/services/...")
    .BoostWithMachineName()
    .BoostWithProcessId()
    .BoostWithCorrelationId()
    .Build();

try
{
    logger.Information("Application started");
    // Your application code...
}
finally
{
    await logger.FlushAsync();
    await logger.DisposeAsync();
}
```

## Logging Methods

### Basic Logging

```csharp
logger.Trace("Verbose trace message");
logger.Debug("Debug detail");
logger.Information("Something happened");
logger.Warning("Potential problem");
logger.Error("Something failed");
logger.Critical("System is going down");
```

### Logging with Exceptions

```csharp
try
{
    // risky operation
}
catch (Exception ex)
{
    logger.Warning(ex, "Operation failed, attempting retry");
    logger.Error(ex, "Operation failed after retries");
    logger.Critical(ex, "Critical failure, shutting down");
}
```

### Structured Properties

```csharp
// With tuples (preferred for performance)
logger.Log(LogLevel.Information, "User logged in",
    ("UserId", 42),
    ("IP", "192.168.1.1"),
    ("Session", "abc-123"));

// Properties appear in most flows (database, JSON output, etc.)
// In file output: `UserId=42, IP=192.168.1.1, Session=abc-123`
```

### Custom Log Event Modification

```csharp
logger.AddModifier((ref LogEventBuilder builder) =>
{
    builder.WithProperty("RequestId", HttpContext.TraceIdentifier);
    builder.WithProperty("UserId", User.Id);
});

logger.Information("Processing request");  // RequestId and UserId added automatically
```

## Available Flows

Flows are the destinations where log events are written. Each flow is independent and can have its own level filter and configuration.

### Flows Overview

| Flow | Method | Description |
|------|--------|-------------|
| Console | `WriteToConsole()` | Colored console output with customizable templates |
| File | `WriteToFile()` | Batched, rotated, compressed file output with retention policies |
| Encrypted File | `WriteToEncryptedFile()` | AES-encrypted log files with password protection |
| Memory | `WriteToMemory()` | In-memory ring buffer for quick access and diagnostics |
| Audit | `WriteToAudit()` | Tamper-evident hash-chained audit trail with verification |
| Database | `WriteToDatabase()` | ADO.NET database sink with custom table support |
| HTTP | `WriteToHttp()` | Generic HTTP endpoint with custom headers and batching |
| Webhook | `WriteToWebhook()` | Generic webhook POST endpoint |
| Email | `WriteToEmail()` | HTML digest emails via SMTP with configurable batching |
| Slack | `WriteToSlack()` | Slack incoming webhooks with message formatting |
| Discord | `WriteToDiscord()` | Discord webhooks with embed formatting |
| Microsoft Teams | `WriteToMicrosoftTeams()` | Teams incoming webhooks with adaptive cards |
| Telegram | `WriteToTelegram()` | Telegram bot messages |
| SignalR | `WriteToSignalR()` | Real-time SignalR hub push for live dashboards |
| Redis | `RedisFlow()` | Redis Pub/Sub + optional List persistence with reconnect |
| Elasticsearch | `WriteToElasticSearch()` | Elasticsearch index with custom index names |
| Splunk | `WriteToSplunkFlow()` | Splunk HEC (HTTP Event Collector) |
| Graylog | `WriteToGraylogFlow()` | GELF over UDP or TCP |
| Syslog UDP | `WriteToSyslogUdp()` | RFC-5424 Syslog over UDP |
| Syslog TCP | `WriteToSyslogTcp()` | RFC-5424 Syslog over TCP with optional TLS |
| TCP | `WriteToTcp()` | Raw TCP with optional TLS support |
| UDP | `WriteToUdp()` | Raw UDP datagrams |
| SNMP Trap | `WriteToSnmpTrap()` | SNMP v2c traps for network monitoring |
| Zabbix | `WriteToZabbixFlow()` | Zabbix trapper protocol |
| EventLog | `WriteToEventLogFlow()` | Remote Windows event log forwarding |
| Rolling Buffer | `WriteToRollingBuffer()` | Circular buffer with trigger-based context flushing |
| Throttled | `WriteToThrottled()` | Token-bucket rate limiting with optional deduplication |
| Retry | `WriteToRetry()` | Automatic retry with exponential back-off |
| Failover | `WriteToFailover()` | Primary/secondary failover with recovery detection |
| Diagnostics | `WriteDiagnostics()` | Periodic diagnostic snapshots and metrics |
| Status | `WriteToStatusFlow()` | Service health monitoring |
| Conditional | `WriteToConditional()` | Route logs based on custom predicates |
| Circuit Breaker | `WriteToCircuitBreaker()` | Protect against cascading failures |

### Flow Examples

#### Console Output
```csharp
// Basic colored output
.WriteToConsole(useColors: true)

// Minimal console (no colors)
.WriteToConsole(useColors: false)

// Only warnings and above to console
.WriteToConsole(minimumLevel: LogLevel.Warning, useColors: true)
```

#### File Output
```csharp
// Basic file logging
.WriteToFile("./logs")

// Custom configuration
.WriteToFile(
    directory: "./logs",
    filePrefix: "myapp",
    maxFileSize: 100 * 1024 * 1024,  // 100 MB
    maxDirectorySize: 10L * 1024 * 1024 * 1024,  // 10 GB total
    flushIntervalInMilliSeconds: 2000,
    batchSize: 50,
    compression: CompressionFormat.GZip,
    outputFormat: FileOutputFormat.Text)

// Category-based routing (separate files per category)
.WriteToFile(
    directory: "./logs",
    useCategoryRouting: true)

// Level-based routing (separate files per log level)
.WriteToFile(
    directory: "./logs",
    logLevelsForSeparateFiles: new[] { LogLevel.Error, LogLevel.Critical })
```

#### Encrypted File Output
```csharp
// Encrypt logs with password
.WriteToEncryptedFile(
    directory: "./secure-logs",
    password: "MySecurePassword123!")

// Decrypt later when needed
LogBuilder.DecryptFile(
    encryptedPath: "./secure-logs/log.enc",
    outputPath: "./secure-logs/log.txt",
    password: "MySecurePassword123!");
```

#### In-Memory Buffer
```csharp
// Store last 1000 events in memory
.WriteToMemory(capacity: 1000, minimumLevel: LogLevel.Information)

// Access stored events
var memoryFlow = logger.GetFlowOfType<MemoryFlow>();
var events = memoryFlow.GetEvents();
```

#### Slack
```csharp
.WriteToSlack("https://hooks.slack.com/services/YOUR/WEBHOOK/URL")

// Only errors to Slack
.WriteToSlack(
    webhookUrl: "https://hooks.slack.com/services/YOUR/WEBHOOK/URL",
    minimumLevel: LogLevel.Error)
```

#### Discord
```csharp
.WriteToDiscord(
    webHookUrl: "https://discordapp.com/api/webhooks/YOUR/WEBHOOK",
    botName: "ErrorBot")
```

#### Email Digest
```csharp
.WriteToEmail(
    smtpHost: "smtp.gmail.com",
    smtpPort: 587,
    useSsl: true,
    username: "your-email@gmail.com",
    password: "app-password",
    from: "logs@company.com",
    to: "ops@company.com",
    subjectPrefix: "[Production Alerts]",
    digestMinutes: 5,  // Send every 5 minutes
    flushOnCritical: true,  // Send immediately on Critical
    minimumLevel: LogLevel.Error)
```

#### Database (SQL Server, PostgreSQL, MySQL, etc.)
```csharp
.WriteToDatabase(
    connectionFactory: () => new SqlConnection("connection-string"),
    tableName: "ApplicationLogs",
    batchSize: 10)
```

#### Elasticsearch
```csharp
.WriteToElasticSearch(
    elasticSearchUrl: "https://elasticsearch.company.com:9200",
    indexName: "myapp-logs",
    batchSize: 100)
```

#### Redis (Pub/Sub + List)
```csharp
// Pub/Sub only (real-time subscribers)
.RedisFlow(
    host: "redis.company.com",
    port: 6379,
    channel: "eonacat:logs")

// Pub/Sub + List persistence (subscribers + history)
.RedisFlow(
    host: "redis.company.com",
    port: 6379,
    password: "redis-password",
    database: 0,
    channel: "eonacat:logs",
    listKey: "eonacat:logs:history",
    maxListLength: 10000)
```

#### Syslog
```csharp
// RFC-5424 Syslog over UDP
.WriteToSyslogUdp(
    host: "syslog.company.com",
    port: 514)

// RFC-5424 Syslog over TCP with TLS
.WriteToSyslogTcp(
    host: "syslog.company.com",
    port: 514,
    useTls: true)
```

#### HTTP Endpoint
```csharp
.WriteToHttp(
    endpoint: "https://logs.company.com/ingest",
    batchSize: 50,
    batchInterval: TimeSpan.FromSeconds(2),
    headers: new Dictionary<string, string>
    {
        ["Authorization"] = "Bearer token123",
        ["X-API-Key"] = "secret"
    })
```

#### Audit Trail (Tamper-Evident)
```csharp
// Record warnings and above in hash-chained audit trail
.WriteToAudit(
    directory: "./audit",
    auditLevel: AuditLevel.WarningAndAbove,
    includeProperties: true)

// Verify audit file integrity
bool isIntact = AuditFlow.Verify("./audit/audit.audit");
if (!isIntact)
    Console.WriteLine("Audit trail has been tampered with!");
```

#### Rolling Buffer (Context-on-Error)
```csharp
// Buffer 500 events; on Error, flush preceding 100 events to file
.WriteToRollingBuffer(
    capacity: 500,
    minimumLevel: LogLevel.Trace,
    triggerLevel: LogLevel.Error,
    triggerTarget: new FileFlow("./error-context"),
    preContextLines: 100)
```

## Available Boosters

Boosters automatically enrich every log event with additional properties before it reaches any flow. They run once per log event and can add system information, application context, and custom data.

### System Information Boosters

```csharp
.BoostWithMachineName()      // Add computer/host name
.BoostWithProcessId()        // Add current process ID
.BoostWithThreadId()         // Add managed thread ID
.BoostWithThreadName()       // Add thread name (if set)
.BoostWithUser()             // Add current OS user name
.BoostWithOS()               // Add OS description and version
.BoostWithFramework()        // Add .NET runtime description
.BoostWithMemory()           // Add process working set in MB
.BoostWithUptime()           // Add process uptime in seconds
.BoostWithProcStart()        // Add process start time (DateTime)
```

### Date/Time Boosters

```csharp
.BoostWithDate()             // Add current date (yyyy-MM-dd)
.BoostWithTime()             // Add current time (HH:mm:ss.fff)
.BoostWithTicks()            // Add current timestamp ticks (for precise timing)
```

### Application Context Boosters

```csharp
.BoostWithApp()              // Add app name and base directory (auto-detected)
.BoostWithApplication("MyApp", "2.0.0")  // Add explicit app name + version
.BoostWithEnvironment("Production")      // Add environment name (Prod/Dev/Test)
.BoostWithCorrelationId()    // Add Activity.Current correlation ID for distributed tracing
```

### Custom Data Boosters

```csharp
// Single key/value pair
.BoostWithCustomText("Environment", "Production")
.BoostWithCustomText("ServiceVersion", "2.0.1")

// Callback-based booster for dynamic data
.Boost("RequestInfo", () => new Dictionary<string, object?>
{
    ["UserId"] = GetCurrentUserId(),
    ["TenantId"] = GetCurrentTenantId(),
    ["ApiVersion"] = GetApiVersion()
})

// Custom booster implementation
.Boost(new MyCustomBooster())
```

### Complete Booster Configuration Example

```csharp
var logger = new LogBuilder("MyApplication")
    // System info
    .BoostWithMachineName()
    .BoostWithProcessId()
    .BoostWithThreadId()
    .BoostWithOS()
    .BoostWithFramework()
    .BoostWithMemory()

    // Application context
    .BoostWithApplication("MyApp", "1.0.0")
    .BoostWithEnvironment("Production")
    .BoostWithCorrelationId()

    // Startup info
    .BoostWithProcStart()
    .BoostWithUptime()

    // Custom context
    .BoostWithCustomText("DataCenter", "US-East-1")
    .BoostWithCustomText("InstanceId", Environment.MachineName)
    .Boost("Request", () => new Dictionary<string, object?>
    {
        ["TraceId"] = Activity.Current?.Id ?? HttpContext?.TraceIdentifier,
        ["UserId"] = CurrentUser?.Id,
    })

    .WriteToConsole()
    .WriteToFile("./logs")
    .Build();
```

### What Boosters Add to Logs

When you enable boosters, they add structured properties to each log event. In file output, these appear as:

```
[2026-03-27 09:15:00.123] [INFO] [Application=MyApp, Version=1.0.0, Environment=Production, Machine=srv-01, PID=1234, ThreadId=5]
User logged in successfully
```

In JSON outputs (database, Elasticsearch, etc.), boosters add properties like:

```json
{
  "timestamp": "2026-03-27T09:15:00.123Z",
  "level": "Information",
  "message": "User logged in successfully",
  "machine": "srv-01",
  "processId": 1234,
  "threadId": 5,
  "userId": 42,
  "application": "MyApp",
  "version": "1.0.0",
  "environment": "Production"
}
```

## Pre-Build Modifiers

Modifiers run after boosters and can mutate or cancel a log event before it is dispatched to flows. Use modifiers to add request-scoped data, redact sensitive info, or filter events.

### Basic Modifier Example

```csharp
logger.AddModifier((ref LogEventBuilder builder) =>
{
    // Add request context
    builder.WithProperty("RequestId", HttpContext.TraceIdentifier);
});

// Now every log will include RequestId automatically
logger.Information("Processing request");
```

### Multiple Modifiers

```csharp
// Add request context
logger.AddModifier((ref LogEventBuilder builder) =>
{
    builder.WithProperty("RequestId", HttpContext?.TraceIdentifier);
    builder.WithProperty("UserId", User?.Id);
});

// Add custom timing info
logger.AddModifier((ref LogEventBuilder builder) =>
{
    builder.WithProperty("Timestamp", DateTime.UtcNow);
});

// Redact sensitive data (example)
logger.AddModifier((ref LogEventBuilder builder) =>
{
    if (builder.Message?.Contains("password") ?? false)
        builder.WithMessage("[REDACTED - contains sensitive data]");
});
```

### Canceling Events with Modifiers

```csharp
logger.AddModifier((ref LogEventBuilder builder) =>
{
    // Example: Skip verbose trace logs in production
    if (builder.Level == LogLevel.Trace && !IsDebugMode)
        builder.Cancel();  // Event won't be sent to any flow
});
```

## Request Context Integration (ASP.NET Core / Razor Pages)

```csharp
// In Program.cs, register request context booster
builder.Services.AddEonaCatLogging("WebApp", logBuilder =>
{
    logBuilder
        .WriteToConsole()
        .WriteToFile("./logs")
        .BoostWithCorrelationId();  // Distributed tracing support
});

// In your page or controller
public class IndexModel : PageModel
{
    private readonly ILogger _logger;

    public IndexModel(ILogger logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
        // Correlation ID is automatically added by booster
        _logger.Information("Page loaded");
    }
}
```

## Resilience Patterns

### Retry with Exponential Back-off

Automatically retry failed writes with exponential delays. Useful for flaky remote endpoints.

```csharp
.WriteToRetry(
    primaryFlow: new HttpFlow("https://logs.example.com"),
    maxRetries: 5,
    initialDelay: TimeSpan.FromMilliseconds(200),
    exponentialBackoff: true)

// Retry delays: 200ms, 400ms, 800ms, 1.6s, 3.2s
```

### Primary / Secondary Failover

Automatically fall back to a secondary destination if the primary fails.

```csharp
.WriteToFailover(
    primaryFlow: new ElasticSearchFlow("https://es-prod:9200"),
    secondaryFlow: new FileFlow("./fallback-logs"),
    recoveryCheckInterval: TimeSpan.FromSeconds(30),
    failureThreshold: 5)  // Switch after 5 consecutive failures
```

### Token-Bucket Rate Limiting with Deduplication

Protect high-latency sinks (email, Slack, HTTP) from log storms.

```csharp
.WriteToThrottled(
    inner: new SlackFlow(webhookUrl),
    burstCapacity: 10,  // Allow 10 events in a burst
    refillPerSecond: 1.0,  // Refill 1 token per second
    deduplicate: true,  // Collapse identical messages
    dedupWindow: TimeSpan.FromSeconds(60),  // Within 60-second window
    dedupMaxKeys: 1000)  // Track up to 1000 unique messages
```

Example behavior:
- `10` log errors occur simultaneously → first 10 are sent (burst)
- Next error within 1 second is queued (rate-limited to 1/sec)
- Identical errors within 60s → counted, then sent as "N duplicate messages"

### Rolling Buffer - Context-on-Error

Buffer recent logs and flush context when an error occurs. Useful for debugging transient issues.

```csharp
.WriteToRollingBuffer(
    capacity: 500,  // Keep last 500 events
    minimumLevel: LogLevel.Trace,  // Buffer everything
    triggerLevel: LogLevel.Error,  // Trigger on errors
    triggerTarget: new FileFlow("./error-context"),  // Write context to file
    preContextLines: 100)  // Include 100 lines of context before the error
```

Real-world scenario:
```
# In-memory buffer contains: [Trace, Debug, Info, Warning, ...100+ events...]
# Error occurs
# Rolling buffer flushes: previous 100 logs + the error itself
# Result: ./error-context/2026-03-27.log contains the full context
```

### Circuit Breaker

Stop sending to a failing destination and automatically resume when it recovers.

```csharp
.WriteToCircuitBreaker(
    inner: new ElasticSearchFlow("https://es-prod:9200"),
    failureThreshold: 10,  // Open after 10 failures
    successThreshold: 3,  // Close after 3 successes
    timeout: TimeSpan.FromSeconds(30))  // Check recovery every 30s
```

### Combining Patterns

```csharp
await using var logger = new LogBuilder("Production")
    .WriteToConsole()

    // Local file as primary
    .WriteToFile("./logs")

    // Elasticsearch with resilience
    .WriteToRetry(
        primaryFlow: .WriteToFailover(
            primaryFlow: new ElasticSearchFlow("https://es-prod:9200"),
            secondaryFlow: new FileFlow("./fallback")),
        maxRetries: 3)

    // Rate-limited Slack for errors only
    .WriteToThrottled(
        inner: new SlackFlow(webhookUrl),
        burstCapacity: 5,
        refillPerSecond: 0.5,
        minimumLevel: LogLevel.Error)

    // Rolling buffer for debugging
    .WriteToRollingBuffer(
        capacity: 1000,
        triggerLevel: LogLevel.Error,
        triggerTarget: new FileFlow("./error-context"))

    .BoostWithMachineName()
    .BoostWithCorrelationId()
    .Build();
```

## Encrypted File Logging

Encrypt sensitive logs with AES encryption and password protection.

### Writing Encrypted Logs

```csharp
.WriteToEncryptedFile(
    directory: "./secure-logs",
    filePrefix: "encrypted",
    password: "YourStrongPassword123!",
    maxFileSize: 50 * 1024 * 1024)
```

### Decrypting Logs

```csharp
// Decrypt a specific file when needed
LogBuilder.DecryptFile(
    encryptedPath: "./secure-logs/encrypted.enc",
    outputPath:    "./secure-logs/decrypted.txt",
    password:      "YourStrongPassword123!");

// Now you can read the decrypted logs
string logs = File.ReadAllText("./secure-logs/decrypted.txt");
```

### Use Cases

- Compliance requirements (HIPAA, PCI-DSS)
- Storing sensitive user data or API keys in logs
- Secure log storage on shared infrastructure
- Audit trails with encryption

## Audit Trail (Tamper-Evident)

The audit flow produces a tamper-evident file where every entry is SHA-256 hash-chained. Deleting or modifying any past entry invalidates all subsequent hashes.

### Writing Audit Logs

```csharp
.WriteToAudit(
    directory: "./audit",
    filePrefix: "audit",
    auditLevel: AuditLevel.WarningAndAbove,  // Warning, Error, Critical
    includeProperties: true)
```

### Verification

```csharp
// Verify file integrity at any time
bool intact = AuditFlow.Verify("./audit/audit.audit");

if (!intact)
{
    Console.WriteLine("ERROR: Audit trail has been tampered with!");
    // Take appropriate action (alert, disable service, etc.)
}
```

### Audit Levels

```csharp
AuditLevel.All              // Every log event
AuditLevel.WarningAndAbove  // Warning, Error, Critical
AuditLevel.ErrorAndAbove    // Error, Critical
AuditLevel.CriticalOnly     // Critical only
```

### Example Audit File

```
2026-03-27T09:15:00.123Z|WARNING|Low disk space|[hash: sha256(prev_hash + data)]
2026-03-27T09:15:05.456Z|ERROR|Database connection failed|[hash: sha256(prev_hash + data)]
2026-03-27T09:15:10.789Z|CRITICAL|Service shutting down|[hash: sha256(prev_hash + data)]
```

If someone modifies the second entry, the third entry's hash validation fails, indicating tampering.

## Log Message Template

Both `ConsoleFlow` and `FileFlow` accept a customizable template string for formatting output.

### Default Template

```
[{ts}] [Host: {host}] [Category: {category}] [Thread: {thread}] [{logtype}] {message}{props}
```

### Available Tokens

| Token | Description | Example |
|-------|-------------|---------|
| `{ts}` | Timestamp (yyyy-MM-dd HH:mm:ss.fff) | 2026-03-27 09:15:00.123 |
| `{tz}` | Timezone (UTC or local name) | UTC or EST |
| `{host}` | Machine name | srv-prod-01 |
| `{category}` | Logger category | MyApp.Services |
| `{thread}` | Managed thread ID | 5 |
| `{pid}` | Process ID | 1234 |
| `{logtype}` | Log level label | INFO, WARN, ERROR |
| `{message}` | Log message text | User login successful |
| `{props}` | Structured properties | UserId=42, IP=192.168.1.1 |
| `{newline}` | Line break | (actual newline) |

### Custom Templates

```csharp
// Minimal template
.WriteToFile(
    directory: "./logs",
    template: "[{ts}] [{logtype}] {message}")
// Output: [2026-03-27 09:15:00.123] [INFO] User login successful

// Verbose template with all info
.WriteToFile(
    directory: "./logs",
    template: "[{ts}] [{tz}] [{logtype}] [Thread={thread}] [PID={pid}] [Host={host}] {category}: {message}{props}")
// Output: [2026-03-27 09:15:00.123] [UTC] [INFO] [Thread=5] [PID=1234] [Host=srv-01] MyApp: User login successful UserId=42, IP=192.168.1.1

// JSON-like format
.WriteToFile(
    directory: "./logs",
    template: "timestamp={ts}|level={logtype}|category={category}|pid={pid}|message={message}{props}")
```

### Advanced Message Template Features

EonaCat.LogStack supports advanced templating beyond basic property placeholders. These features work with message templates used in logging calls:

```csharp
logger.Information("User {User} logged in from {IP}", user, ipAddress);
```

#### Nested Property Access

Access properties of objects using dot notation:

```csharp
var user = new { Name = "John", Address = new { City = "NYC" } };
logger.Information("User {User.Name} lives in {User.Address.City}", user, user.Address);
// Output: User John lives in NYC
```

#### Array/Collection Indexing

Access specific items in arrays or lists:

```csharp
var items = new[] { "apple", "banana", "cherry" };
logger.Information("First item: {Items[0]}, Second: {Items[1]}", items);
// Output: First item: apple, Second: banana
```

#### Alignment and Padding

Pad values to a specific width for aligned output:

```csharp
logger.Information("{Name,20} {Email,-30}", "John", "john@example.com");
// Output: "                John john@example.com             "
//         (right-aligned 20 chars) (left-aligned 30 chars)
```

#### String Filters

Apply transformations to property values:

```csharp
// Uppercase
logger.Information("Status: {Status|uppercase}", "pending");
// Output: Status: PENDING

// Lowercase
logger.Information("Event: {Event|lowercase}", "UserCreated");
// Output: Event: usercreated

// Trim whitespace
logger.Information("Value: '{Value|trim}'", "  spaces  ");
// Output: Value: 'spaces'

// Truncate with ellipsis
logger.Information("Description: {Description|truncate:50}", veryLongText);
// Output: Description: This is a very long description that …

// Reverse string
logger.Information("Reversed: {Text|reverse}", "hello");
// Output: Reversed: olleh

// Multiple filters (chained)
logger.Information("Result: {Input|trim|uppercase}", "  hello world  ");
// Output: Result: HELLO WORLD
```

#### Fallback Values

Provide default values when properties are null or missing:

```csharp
// Fallback with ?? operator and quotes
logger.Information("User: {User??'Anonymous'}", user);
// Output: User: Anonymous (if user is null)

logger.Information("Email: {Email??'no-email@example.com'}", email);
// Output: Email: no-email@example.com (if email is null)
```

#### Conditional Rendering

Display different text based on boolean properties:

```csharp
logger.Information("Status: {?IsActive:Active|Inactive}", isActive);
// Output: Status: Active (if isActive is true)
// Output: Status: Inactive (if isActive is false)

logger.Information("Result: {?Success:✓ Success|✗ Failed}", success);
// Output: Result: ✓ Success (if success is true)
```

#### Advanced Conditionals

Use comparison operators in conditional templates:

```csharp
// Advanced conditional token with if syntax
// Syntax: {@if:condition:trueOutput|falseOutput}

logger.Information("User role: {@if:RoleId>5:Admin|User}", user);
// Output: User role: Admin (if RoleId > 5)
// Output: User role: User (if RoleId <= 5)

logger.Information("Account: {@if:Status==Premium:Premium Member|Standard}", account);
// Output: Account: Premium Member (if Status equals 'Premium')

// Comparison operators: ==, !=, <, >, <=, >=
logger.Information("{@if:Count>=100:Large|Small}", data);
logger.Information("{@if:Price<50:Budget|Premium}", item);
logger.Information("{@if:IsDeleted!=false:Deleted|Active}", record);
```

#### Loops and Collections

Iterate over arrays and collections in templates:

```csharp
// Syntax: {@loop:CollectionName:itemTemplate:separator}

var items = new[] { "apple", "banana", "cherry" };
logger.Information("Items: {@loop:Items:{Item}|, }", items);
// Output: Items: apple, banana, cherry

// Custom separator
var tags = new[] { "urgent", "high-priority", "production" };
logger.Information("Tags: {@loop:Tags:{Item}| | }", tags);
// Output: Tags: urgent | high-priority | production

// Complex items
var users = new[] 
{
    new { Id = 1, Name = "John" },
    new { Id = 2, Name = "Jane" }
};
logger.Information("Users: {@loop:Users:({Id}:{Name})|, }", users);
// Output: Users: (1:John), (2:Jane)
```

#### Math Filters

Perform arithmetic operations on numeric properties:

```csharp
logger.Information("Total: ${Amount|add:10}", 50);
// Output: Total: $60

logger.Information("Discount: ${Price|multiply:0.9}", 100);
// Output: Discount: $90

logger.Information("Half: {Value|divide:2}", 100);
// Output: Half: 50

logger.Information("Remainder: {Number|modulo:3}", 10);
// Output: Remainder: 1

logger.Information("Absolute: {Change|abs}", -15);
// Output: Absolute: 15

logger.Information("Rounded: {Value|round:2}", 19.9999);
// Output: Rounded: 20

logger.Information("Max: {Value|max:100}", 150);
// Output: Max: 100

logger.Information("Floor: {Decimal|floor}", 19.9);
// Output: Floor: 19

logger.Information("Ceil: {Decimal|ceil}", 19.1);
// Output: Ceil: 20
```

#### String Manipulation Filters

Transform string values with various filters:

```csharp
// Case conversion
logger.Information("Lower: {Text|lowercase}", "HELLO");
// Output: Lower: hello

logger.Information("Upper: {Text|uppercase}", "world");
// Output: Upper: WORLD

// Padding
logger.Information("Padded: |{Name|pad:15}|", "John");
// Output: Padded: |John           |

// Repetition
logger.Information("Repeated: {Char|repeat:5}", "x");
// Output: Repeated: xxxxx

// String replacement
logger.Information("Fixed: {Path|replace:old:new}", "/old/path/old");
// Output: Fixed: /new/path/new

// Substring operations
logger.Information("Skip first 3: {Text|substring:3}", "12345");
// Output: Skip first 3: 45

// Trim variations
logger.Information("Trimmed: '{Text|trim}'", "  spaces  ");
// Output: Trimmed: 'spaces'

logger.Information("Trim start: '{Text|trimstart}'", "  spaces  ");
// Output: Trim start: 'spaces  '

logger.Information("Trim end: '{Text|trimend}'", "  spaces  ");
// Output: Trim end: '  spaces'

// String testing
logger.Information("Starts with 'user': {Email|startswith:user}", "user@example.com");
// Output: Starts with 'user': true

logger.Information("Ends with '.org': {Url|endswith:.org}", "website.org");
// Output: Ends with '.org': true

logger.Information("Contains 'app': {Path|contains:app}", "/app/data");
// Output: Contains 'app': true

// String reversal
logger.Information("Reversed: {Text|reverse}", "hello");
// Output: Reversed: olleh

// Split and join
logger.Information("Split CSV: {Data|split:,}", "a,b,c");
// Output: Split CSV: a, b, c
```

#### Comparison Filters

Compare values and return boolean results:

```csharp
logger.Information("Equals: {Status|equals:Active}", status);
// Output: Equals: true (if status == 'Active')

logger.Information("Less than 100: {Value|lessthan:100}", 50);
// Output: Less than 100: true

logger.Information("Greater than 50: {Value|greaterthan:50}", 100);
// Output: Greater than 50: true

logger.Information("Greater or equal: {Count|gte:10}", 15);
// Output: Greater or equal: true

logger.Information("Less or equal: {Count|lte:20}", 15);
// Output: Less or equal: true

logger.Information("Not equal: {Type|ne:User}", "Admin");
// Output: Not equal: true
```

#### Date & Time Filters

Format and manipulate date/time values:

```csharp
// Date formatting
logger.Information("Date: {CreatedAt|date:yyyy-MM-dd}", DateTime.Now);
// Output: Date: 2026-03-27

logger.Information("Full timestamp: {CreatedAt|date:O}", DateTime.Now);
// Output: Full timestamp: 2026-03-27T09:15:00.1234567Z

logger.Information("Custom format: {CreatedAt|date:dd/MM/yyyy HH:mm:ss}", DateTime.Now);
// Output: Custom format: 27/03/2026 09:15:00

// TimeSpan operations
var duration = TimeSpan.FromSeconds(3661);
logger.Information("Total seconds: {Duration|timespan:totalseconds}", duration);
// Output: Total seconds: 3661

logger.Information("Total minutes: {Duration|timespan:totalminutes}", duration);
// Output: Total minutes: 61.0166...

logger.Information("Total hours: {Duration|timespan:totalhours}", duration);
// Output: Total hours: 1.01388...

logger.Information("Days: {Duration|timespan:days}", duration);
// Output: Days: 0

logger.Information("Hours: {Duration|timespan:hours}", duration);
// Output: Hours: 1

logger.Information("Minutes: {Duration|timespan:minutes}", duration);
// Output: Minutes: 1

logger.Information("Seconds: {Duration|timespan:seconds}", duration);
// Output: Seconds: 1
```

#### Chaining Multiple Filters

Combine filters for complex transformations:

```csharp
// Trim, then uppercase, then truncate
logger.Information("Processed: {Input|trim|uppercase|truncate:10}", "  hello world  ");
// Output: Processed: HELLO WOR…

// Substring, then lowercase
logger.Information("Modified: {Path|substring:5|lowercase}", "/DATA/MyFile.TXT");
// Output: Modified: myfile.txt

// Apply multiple math operations
logger.Information("Calculated: {Value|multiply:2|add:10|divide:3}", 5);
// Output: Calculated: 6.66... (5 * 2 = 10, 10 + 10 = 20, 20 / 3 = 6.66)
```

#### Complete Advanced Template Examples

```csharp
// API request logging with advanced features
var request = new 
{
    Method = "POST",
    Path = "/api/users",
    UserId = 42,
    ResponseTime = 150,
    Success = true,
    Tags = new[] { "api", "users", "production" }
};

logger.Information(
    "[{Timestamp|date:HH:mm:ss}] {Method|uppercase} {Path} - User {UserId} - {ResponseTime|pad:5}ms - {?Success:✓|✗} - Tags: {@loop:Tags:{Item}|, }",
    DateTime.Now, request.Method, request.Path, request.UserId, 
    request.ResponseTime, request.Success, request.Tags
);
// Output: [09:15:00] POST /api/users - User 42 -   150ms - ✓ - Tags: api, users, production

// Conditional status with comparison
var operation = new
{
    Name = "DataSync",
    Status = "Completed",
    Duration = 45000,
    RecordsProcessed = 1500,
    ErrorCount = 0
};

logger.Information(
    "{Name}: {@if:ErrorCount==0:✓ Success|⚠ With Errors} | Duration: {Duration|timespan:totalseconds}s | Records: {RecordsProcessed|add:0} processed",
    operation.Name, operation.ErrorCount, operation.Duration, operation.RecordsProcessed
);
// Output: DataSync: ✓ Success | Duration: 45s | Records: 1500 processed

// Complex nested template
var batch = new
{
    Id = "batch-001",
    Items = new[] { "item1", "item2", "item3" },
    Size = 3,
    Price = 99.99m,
    Discount = 0.15m
};

logger.Information(
    "Batch {Id}: {?Size>5:Large|Small} batch | Items: {@loop:Items:{Item}|, } | Price: ${Price|multiply:Discount|add:0|round:2}",
    batch.Id, batch.Size, batch.Items, batch.Price, batch.Discount
);
// Output: Batch batch-001: Small batch | Items: item1, item2, item3 | Price: $15.00
```

#### Template Features Summary

The templating engine supports:

| Feature | Syntax | Example |
|---------|--------|---------|
| **Basic Property** | `{PropertyName}` | `{UserId}` |
| **Nested Properties** | `{Object.Property.Sub}` | `{User.Address.City}` |
| **Array Indexing** | `{Array[Index]}` | `{Items[0]}` |
| **Alignment** | `{Value,Width}` | `{Name,20}` |
| **Format Specifiers** | `{Value:Format}` | `{Date:yyyy-MM-dd}` |
| **String Filters** | `{Value\|Filter}` | `{Text\|uppercase}` |
| **Math Filters** | `{Value\|add:10}` | `{Price\|multiply:0.9}` |
| **Comparison** | `{Value\|equals:text}` | `{Status\|equals:Active}` |
| **Simple Conditionals** | `{?Property:True\|False}` | `{?IsActive:Active\|Inactive}` |
| **Advanced Conditionals** | `{@if:Condition:T\|F}` | `{@if:Count>10:High\|Low}` |
| **Loops** | `{@loop:Collection:Template}` | `{@loop:Items:{Item}\|, }` |
| **Fallback Values** | `{Value??'Default'}` | `{Name??'Unknown'}` |
| **Chained Filters** | `{Value\|filter1\|filter2}` | `{Text\|trim\|uppercase}` |
| **Destructuring** | `{@Object}` | `{@User}` |

#### Format Specifiers

Apply .NET format strings to values:

```csharp
// Date formatting
logger.Information("Date: {CreatedAt:yyyy-MM-dd}", DateTime.Now);
// Output: Date: 2026-03-27

// Decimal formatting
logger.Information("Price: {Price:C}", 19.99m);
// Output: Price: $19.99

// Numeric formatting
logger.Information("Count: {Count:D5}", 42);
// Output: Count: 00042
```

#### Destructuring

Deep-inspect complex objects to reveal their structure:

```csharp
var user = new { Id = 1, Name = "John", Email = "john@example.com" };

// Default destructuring with @ prefix
logger.Information("User: {@User}", user);
// Output: User: {Id: 1, Name: John, Email: john@example.com}

// Force string conversion with $ prefix
logger.Information("User: {$User}", user);
// Output: User: YourNamespace.User

// Nested destructuring
var order = new 
{ 
    Id = 1,
    Customer = new { Name = "John", City = "NYC" },
    Items = new[] { "Item1", "Item2" }
};

logger.Information("Order: {@Order}", order);
// Output: Order: {Id: 1, Customer: {Name: John, City: NYC}, Items: [...]}
```

#### Complete Advanced Template Examples

```csharp
// API request logging
var request = new 
{
    Method = "POST",
    Path = "/api/users",
    UserId = 42,
    ResponseTime = 150,
    Success = true
};

logger.Information(
    "[{Time|uppercase}] {Method} {Path} - User {UserId} - {ResponseTime,5}ms - {?Success:✓|✗}",
    DateTime.Now.ToString("HH:mm:ss"), request.Method, request.Path, 
    request.UserId, request.ResponseTime, request.Success
);
// Output: [09:15:00] POST /api/users - User 42 -   150ms - ✓

// Database operation with fallback
logger.Information(
    "Database query {QueryName??'Unknown'} by user {UserId??'System'} took {Duration|truncate:10}ms",
    storedProcName, currentUserId, duration
);
// Output: Database query sp_GetUsers by user System took 125ms

// File processing with conditions
logger.Information(
    "File {FileName} processed: {?HasErrors:⚠ ERRORS|✓ OK} - {LineCount,6} lines",
    file.Name, file.HasErrors, file.LineCount
);
// Output: File log.txt processed: ✓ OK -    1024 lines

// Nested property access
var company = new
{
    Name = "Acme Corp",
    HeadOffice = new { City = "New York", Country = "USA" },
    Employees = new[] { "John", "Jane", "Jack" }
};

logger.Information(
    "Company {Company.Name} from {Company.HeadOffice.City}, {Company.HeadOffice.Country} - First employee: {Company.Employees[0]}",
    company
);
// Output: Company Acme Corp from New York, USA - First employee: John
```

## Real-World Configuration Scenarios

### Scenario 1: Development Environment

Console output with verbose logging, local files, and no remote sends.

```csharp
await using var logger = new LogBuilder("MyApp")
    .WithMinimumLevel(LogLevel.Debug)
    .WithTimestampMode(TimestampMode.Local)
    .WriteToConsole(useColors: true)
    .WriteToFile("./logs", filePrefix: "dev")

    .BoostWithThreadId()
    .BoostWithCorrelationId()
    .Build();
```

### Scenario 2: Production - Multi-Destination with Resilience

Files locally, Elasticsearch for search, Slack for alerts, encrypted audit trail.

```csharp
await using var logger = new LogBuilder("ProductionApp")
    .WithMinimumLevel(LogLevel.Information)
    .WithTimestampMode(TimestampMode.Utc)

    // Local backup
    .WriteToFile(
        directory: "./logs",
        maxFileSize: 100 * 1024 * 1024,
        compression: CompressionFormat.GZip)

    // Primary analytics with fallback
    .WriteToFailover(
        primaryFlow: new ElasticSearchFlow("https://elastic.company.com"),
        secondaryFlow: new FileFlow("./fallback-elastic"))

    // Rate-limited alerts
    .WriteToThrottled(
        inner: new SlackFlow(slackWebhookUrl),
        burstCapacity: 5,
        refillPerSecond: 1.0,
        minimumLevel: LogLevel.Error)

    // Compliance audit
    .WriteToAudit(
        directory: "./audit",
        auditLevel: AuditLevel.WarningAndAbove)

    // Encrypted sensitive logs
    .WriteToEncryptedFile(
        directory: "./secure-logs",
        password: Environment.GetEnvironmentVariable("LOG_ENCRYPTION_KEY"))

    // Diagnostics snapshot
    .WriteDiagnostics(
        snapshotInterval: TimeSpan.FromMinutes(5))

    // Boosters
    .BoostWithMachineName()
    .BoostWithProcessId()
    .BoostWithApplication("ProductionApp", "1.0.0")
    .BoostWithEnvironment("Production")
    .BoostWithCorrelationId()
    .Build();
```

### Scenario 3: Microservices / Distributed Tracing

Elasticsearch for centralized logs, Redis for real-time events, correlation IDs.

```csharp
await using var logger = new LogBuilder("OrderService")
    .WithMinimumLevel(LogLevel.Information)

    // Centralized log storage
    .WriteToElasticSearch(
        elasticSearchUrl: "https://elastic-cluster.company.com",
        indexName: "orderservice-logs")

    // Real-time event stream
    .RedisFlow(
        host: "redis.company.com",
        channel: "orderservice:logs",
        listKey: "orderservice:logs:history",
        maxListLength: 10000)

    // Local file backup
    .WriteToFile("./logs")

    // Correlation tracking for distributed tracing
    .BoostWithCorrelationId()  // Automatically includes Activity.Current?.Id
    .BoostWithApplication("OrderService", ServiceVersion)
    .BoostWithProcessId()
    .BoostWithMachineName()

    .Build();
```

### Scenario 4: Real-Time Dashboard

SignalR flow for live log streaming to web dashboard.

```csharp
// Backend: Log streaming to SignalR hub
await using var logger = new LogBuilder("DashboardApp")
    .WriteToFile("./logs")
    .WriteToSignalR(
        hubUrl: "https://dashboard.company.com/loghub",
        hubMethod: "ReceiveLog",
        batchSize: 20,
        batchIntervalMs: 500,
        minimumLevel: LogLevel.Warning)  // Only send warnings+ to dashboard

    .Build();

// Frontend: Receive logs in real-time (JavaScript example)
const connection = new signalR.HubConnectionBuilder()
    .withUrl("https://dashboard.company.com/loghub")
    .withAutomaticReconnect()
    .build();

connection.on("ReceiveLog", (log) => {
    console.log(`[${log.level}] ${log.message}`);
    addToLiveLogUI(log);
});

await connection.start();
```

### Scenario 5: Compliance & Audit

Separate audit trail, encrypted logs, tamper detection.

```csharp
await using var logger = new LogBuilder("ComplianceApp")

    // Tamper-evident audit trail
    .WriteToAudit(
        directory: "./compliance/audit",
        auditLevel: AuditLevel.WarningAndAbove,
        includeProperties: true)

    // Encrypted sensitive data
    .WriteToEncryptedFile(
        directory: "./compliance/encrypted",
        password: GetAuditPassword(),
        maxFileSize: 50 * 1024 * 1024)

    // Database for detailed analysis
    .WriteToDatabase(
        connectionFactory: () => new SqlConnection(connectionString),
        tableName: "AuditLogs",
        batchSize: 10)

    // Regular file backup
    .WriteToFile("./logs")

    // Boost with full context
    .BoostWithUser()
    .BoostWithMachineName()
    .BoostWithProcessId()
    .BoostWithApplication("ComplianceApp", "1.0.0")

    .Build();

// Periodic verification
_ = Task.Run(async () =>
{
    while (true)
    {
        await Task.Delay(TimeSpan.FromHours(1));
        bool isIntact = AuditFlow.Verify("./compliance/audit/audit.audit");
        if (!isIntact)
        {
            // Alert security team
            logger.Critical("SECURITY ALERT: Audit trail tampering detected!");
        }
    }
});
```

### Scenario 6: High-Volume with Batching & Rate Limiting

For services handling high log volume, use batching and throttling.

```csharp
await using var logger = new LogBuilder("HighVolumeApp")
    .WriteToThrottled(
        inner: new HttpFlow("https://logs.company.com/ingest"),
        burstCapacity: 100,
        refillPerSecond: 50.0,  // 50 logs/sec steady state
        deduplicate: true,
        dedupWindow: TimeSpan.FromSeconds(30))

    .WriteToFile(
        directory: "./logs",
        batchSize: 100,  // Batch 100 logs before writing
        flushIntervalInMilliSeconds: 1000)  // Flush every 1 second

    .RedisFlow(
        host: "redis.company.com",
        channel: "app:logs",
        listKey: "app:logs:history")

    .Build();
```


## OpenTelemetry-style Telemetry

EonaCat.LogStack includes a dependency-free telemetry layer inspired by OpenTelemetry concepts.

### Distributed tracing

```csharp
var source = new TelemetryActivitySource();
using var span = source.StartScope("ProcessOrder", telemetry =>
{
    telemetry.Attributes["order.id"] = "123";
});
```

Supported tracing concepts:
- Trace IDs and Span IDs
- Parent span relationships
- Span duration measurement
- Span attributes
- Span events
- Status tracking

### Metrics

```csharp
var meter = new TelemetryMeter();
meter.Count("requests.total");
meter.Record("request.duration", 25.5);
var p95 = meter.GetHistogram("request.duration")?.Percentile(0.95);
```

Telemetry features:
- Counters
- Histograms
- Percentiles
- Runtime snapshots
- Resource attributes
- Baggage/context propagation
- HTTP telemetry ingestion

### Resource metadata

```csharp
var resource = new TelemetryResource()
    .Add("service.name", "OrderService")
    .Add("deployment.environment", "production");
```

## Diagnostics

### Real-Time Metrics

```csharp
// Get current statistics
var stats = logger.GetDiagnostics();

Console.WriteLine($"Total Logged: {stats.TotalLogged}");
Console.WriteLine($"Total Dropped: {stats.TotalDropped}");
Console.WriteLine($"Drop Rate: {(double)stats.TotalDropped / stats.TotalLogged:P2}");

// Per-flow statistics
foreach (var flowStat in stats.FlowStats)
{
    Console.WriteLine($"Flow: {flowStat.Name}");
    Console.WriteLine($"  Processed: {flowStat.Processed}");
    Console.WriteLine($"  Dropped: {flowStat.Dropped}");
    Console.WriteLine($"  Errors: {flowStat.ErrorCount}");
}
```

### Diagnostic Snapshots

Enable periodic diagnostic snapshots to track performance over time.

```csharp
.WriteDiagnostics(
    snapshotInterval: TimeSpan.FromMinutes(5),
    injectIntoEvents: true,  // Include metrics in log events
    writeSnapshotEvents: true,  // Write snapshot to logs
    snapshotCategory: "Diagnostics",
    forwardTo: new FileFlow("./diagnostics"),  // Also forward to file
    minimumLevel: LogLevel.Information,
    customMetrics: () => new Dictionary<string, object>
    {
        ["ActiveRequests"] = GetActiveRequestCount(),
        ["CacheHitRate"] = GetCacheHitRate(),
        ["DatabasePoolSize"] = GetDbPoolSize()
    })
```

### Monitoring Health

```csharp
// Monitor in a background task
_ = Task.Run(async () =>
{
    while (true)
    {
        await Task.Delay(TimeSpan.FromMinutes(1));

        var stats = logger.GetDiagnostics();

        // Alert if drop rate is high
        if (stats.TotalLogged > 0)
        {
            double dropRate = (double)stats.TotalDropped / stats.TotalLogged;
            if (dropRate > 0.01)  // > 1% drop rate
            {
                logger.Warning("High log drop rate detected", ("DropRate", dropRate));
            }
        }

        // Alert on errors
        var hasErrors = stats.FlowStats.Any(f => f.ErrorCount > 0);
        if (hasErrors)
        {
            logger.Warning("Some flows are experiencing errors");
        }
    }
});
```

## Flushing and Disposal

All flows support batching, so events may not be written immediately. Use `FlushAsync()` to ensure all pending events are written before shutdown.

### Explicit Flushing

```csharp
// Flush all pending events synchronously (up to 5 seconds)
await logger.FlushAsync();

// After flush, you can safely dispose
await logger.DisposeAsync();
```

### Automatic Flushing on Disposal

Using `await using` automatically flushes and disposes:

```csharp
await using var logger = new LogBuilder("MyApp")
    .WriteToFile("./logs")
    .Build();

logger.Information("Application running");

// At scope exit: FlushAsync() called automatically, then DisposeAsync()
```

### Best Practices

```csharp
try
{
    await using var logger = new LogBuilder("MyApp").Build();

    // Log events here
    logger.Information("App started");

    // Your application logic
    await RunApplication();
}
catch (Exception ex)
{
    // Ensure errors are logged even if something goes wrong
    logger?.Critical(ex, "Unexpected error during shutdown");
}
finally
{
    // FlushAsync() is called automatically when leaving the using block
}
```

### Custom Disposal Logic

```csharp
var logger = new LogBuilder("MyApp").Build();

try
{
    logger.Information("Processing...");
}
finally
{
    // Manual control
    await logger.FlushAsync();

    // Perform custom cleanup
    CleanupResources();

    await logger.DisposeAsync();
}
```

## Events

Subscribe to log events for real-time processing or custom handling.

### Basic Event Handler

```csharp
logger.OnLog += (sender, message) =>
{
    // Fired for every log event that passes filters
    Console.WriteLine($"[Event] {message.Level}: {message.Message}");
};

logger.Information("This event will be raised");
```

### Advanced Event Handling

```csharp
logger.OnLog += (sender, message) =>
{
    // Log level filtering
    if (message.Level >= LogLevel.Error)
    {
        // Send critical errors to external alert system
        SendAlert($"{message.Level}: {message.Message}");
    }

    // Category filtering
    if (message.Category?.Contains("Payment") ?? false)
    {
        // Audit sensitive operations
        LogToAuditSystem(message);
    }

    // Property-based filtering
    if (message.Properties != null && message.Properties.ContainsKey("UserId"))
    {
        TrackUserActivity(message.Properties["UserId"], message);
    }
};
```

### Multiple Event Handlers

```csharp
// Handler 1: Alert on errors
logger.OnLog += (sender, msg) =>
{
    if (msg.Level == LogLevel.Error)
        SendSlackAlert(msg);
};

// Handler 2: Track metrics
logger.OnLog += (sender, msg) =>
{
    Metrics.Increment($"logs.{msg.Level.ToString().ToLower()}");
};

// Handler 3: Update dashboard
logger.OnLog += (sender, msg) =>
{
    Dashboard.AddLog(msg);
};
```

## Custom Flows

Create custom flows by implementing `IFlow` or extending `FlowBase` for complex scenarios.

### Simple Custom Flow

```csharp
public class MyCustomFlow : FlowBase
{
    public MyCustomFlow() : base("MyCustomFlow", LogLevel.Trace) { }

    public override Task<WriteResult> BlastAsync(LogEvent logEvent, CancellationToken ct = default)
    {
        try
        {
            // Your custom logic here
            string formatted = $"[{logEvent.Timestamp:O}] {logEvent.Level}: {logEvent.Message}";
            MyBackendService.SendLog(formatted);

            return Task.FromResult(WriteResult.Success);
        }
        catch (Exception ex)
        {
            return Task.FromResult(WriteResult.Failure(ex));
        }
    }

    public override Task FlushAsync(CancellationToken ct = default)
    {
        // Optional: implement batching flush logic
        return Task.CompletedTask;
    }
}

// Register with:
new LogBuilder("App")
    .WriteTo(new MyCustomFlow())
    .Build();
```

### Advanced Custom Flow with Batching

```csharp
public class BatchedCustomFlow : FlowBase
{
    private readonly List<LogEvent> _batch = new(100);
    private readonly object _lock = new();

    public BatchedCustomFlow() : base("BatchedCustom", LogLevel.Trace) { }

    public override Task<WriteResult> BlastAsync(LogEvent logEvent, CancellationToken ct = default)
    {
        lock (_lock)
        {
            _batch.Add(logEvent);

            if (_batch.Count >= 100)
            {
                return FlushBatchAsync(ct);
            }
        }

        return Task.FromResult(WriteResult.Success);
    }

    private Task<WriteResult> FlushBatchAsync(CancellationToken ct)
    {
        try
        {
            var toSend = _batch.ToList();
            _batch.Clear();

            // Send batch to external service
            MyBackendService.SendLogBatch(toSend);
            return Task.FromResult(WriteResult.Success);
        }
        catch (Exception ex)
        {
            return Task.FromResult(WriteResult.Failure(ex));
        }
    }

    public override async Task FlushAsync(CancellationToken ct = default)
    {
        lock (_lock)
        {
            if (_batch.Count > 0)
            {
                await FlushBatchAsync(ct);
            }
        }
    }

    public override async ValueTask DisposeAsync()
    {
        await FlushAsync();
        await base.DisposeAsync();
    }
}
```

### Custom Flow with Retry Logic

```csharp
public class RetryableCustomFlow : FlowBase
{
    private const int MaxRetries = 3;

    public RetryableCustomFlow() : base("RetryableCustom", LogLevel.Trace) { }

    public override async Task<WriteResult> BlastAsync(LogEvent logEvent, CancellationToken ct = default)
    {
        int attempts = 0;
        Exception lastEx = null;

        while (attempts < MaxRetries)
        {
            try
            {
                await SendToServiceAsync(logEvent, ct);
                return WriteResult.Success;
            }
            catch (Exception ex)
            {
                lastEx = ex;
                attempts++;

                if (attempts < MaxRetries)
                    await Task.Delay(TimeSpan.FromMilliseconds(Math.Pow(2, attempts) * 100), ct);
            }
        }

        return WriteResult.Failure(lastEx);
    }

    private Task SendToServiceAsync(LogEvent logEvent, CancellationToken ct)
    {
        // Your implementation
        return MyBackendService.SendLogAsync(logEvent, ct);
    }

    public override Task FlushAsync(CancellationToken ct = default) => Task.CompletedTask;
}
```

## Best Practices & Tips

### 1. **Use Appropriate Log Levels**

```csharp
logger.Trace("Very detailed diagnostic info (most verbose)");
logger.Debug("Debug-level diagnostic information");
logger.Information("General informational message (default level)");
logger.Warning("Warning message (potential issue)");
logger.Error("Error occurred, operation failed");
logger.Critical("Critical failure, system may be unstable");
```

### 2. **Use Structured Properties for Queryability**

```csharp
// Good: Structured properties
logger.Information("User logged in",
    ("UserId", user.Id),
    ("Email", user.Email),
    ("IpAddress", request.RemoteIpAddress),
    ("Timestamp", DateTime.UtcNow));

// Avoid: String interpolation in message
logger.Information($"User {user.Id} logged in from {request.RemoteIpAddress}");
```

### 3. **Use Categories for Filtering**

```csharp
// Create category-specific loggers
ILogger serviceLogger = loggerFactory.CreateLogger("MyApp.Services.UserService");
ILogger dataLogger = loggerFactory.CreateLogger("MyApp.Data.Repository");

// Configure file splitting by category
.WriteToFile("./logs", useCategoryRouting: true)
// Results in: logs/MyApp.Services.UserService.log, logs/MyApp.Data.Repository.log
```

### 4. **Always Handle Exceptions**

```csharp
try
{
    // Risky operation
    await database.ExecuteAsync(query);
}
catch (TimeoutException ex)
{
    logger.Warning(ex, "Database timeout, retrying");
}
catch (Exception ex)
{
    logger.Error(ex, "Database error, operation failed");
    throw;  // Re-throw to propagate to caller
}
```

### 5. **Use Correlation IDs for Tracing**

```csharp
builder.Services.AddEonaCatLogging("MyApp", logBuilder =>
{
    logBuilder.BoostWithCorrelationId();  // Automatic tracing
});

// All logs in the same request/operation automatically get the same correlation ID
```

### 6. **Configure Different Levels per Flow**

```csharp
.WithMinimumLevel(LogLevel.Information)  // Global minimum
.WriteToConsole(minimumLevel: LogLevel.Debug)  // More verbose
.WriteToFile(minimumLevel: LogLevel.Warning)   // Less verbose
.WriteToSlack(minimumLevel: LogLevel.Error)    // Errors only
```

### 7. **Use Async Flushing Before Shutdown**

```csharp
var host = builder.Build();

// Graceful shutdown: flush logs before exit
var lifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
lifetime.ApplicationStopping.Register(async () =>
{
    var logger = host.Services.GetRequiredService<ILogger>();
    await logger.FlushAsync();
});

await host.RunAsync();
```

### 8. **Monitor Drop Rates**

```csharp
_ = Task.Run(async () =>
{
    while (true)
    {
        await Task.Delay(TimeSpan.FromMinutes(1));

        var stats = logger.GetDiagnostics();
        if (stats.TotalLogged + stats.TotalDropped > 0)
        {
            double dropRate = (double)stats.TotalDropped / (stats.TotalLogged + stats.TotalDropped);
            if (dropRate > 0.01)  // > 1%
                logger.Warning("High drop rate detected", ("DropRate", dropRate));
        }
    }
});
```

### 9. **Use Rolling Buffers for Post-Mortem**

```csharp
// Before: error happens, context is lost
// After: rolling buffer captures previous 100 logs
.WriteToRollingBuffer(
    capacity: 500,
    triggerLevel: LogLevel.Error,
    triggerTarget: new FileFlow("./error-context"))
```

### 10. **Encrypt Sensitive Data**

```csharp
.WriteToEncryptedFile(
    directory: "./secure-logs",
    password: GetEncryptionPasswordFromVault())
```

## Troubleshooting

### High CPU Usage

**Symptoms**: Excessive CPU while logging

**Solutions**:
- Increase batch size to reduce I/O operations
- Use `WriteToThrottled()` to rate limit
- Check if remote endpoints are responding (network latency)

```csharp
.WriteToFile("./logs", batchSize: 100, flushIntervalInMilliSeconds: 5000)
.WriteToThrottled(inner: new HttpFlow(url), burstCapacity: 50)
```

### High Memory Usage

**Symptoms**: Memory grows linearly with time

**Solutions**:
- Check if logs are being flushed (batches aren't sent)
- Reduce rolling buffer capacity
- Enable compression for file logs

```csharp
.WriteToFile(compression: CompressionFormat.GZip)
.WriteToRollingBuffer(capacity: 250)  // Reduce from 500
```

### Events Being Dropped

**Symptoms**: `TotalDropped > 0` in diagnostics

**Causes & Solutions**:
- Backpressure queue full → increase capacity or reduce send rate
- Flow errors → check flow configuration and connectivity
- Log level filters → verify minimumLevel settings

```csharp
// Investigate drop reasons
var stats = logger.GetDiagnostics();
foreach (var flow in stats.FlowStats.Where(f => f.Dropped > 0))
{
    logger.Warning($"Flow {flow.Name} dropped {flow.Dropped} events");
}
```

### Logs Not Reaching Remote Destination

**Symptoms**: Local logs exist, but remote endpoint has nothing

**Troubleshooting Steps**:
1. Verify endpoint connectivity: `telnet host port`
2. Check authentication (API keys, certificates)
3. Enable retry and failover patterns
4. Ensure events pass level filters

```csharp
.WriteToRetry(
    primaryFlow: new HttpFlow(endpoint),
    maxRetries: 5)

// Or use failover:
.WriteToFailover(
    primaryFlow: new HttpFlow(endpoint),
    secondaryFlow: new FileFlow("./fallback"))
```

### File Size Growing Too Large

**Symptoms**: Log files grow without bound

**Solutions**:
- Configure `maxFileSize` and `maxDirectorySize`
- Enable compression
- Set retention policy

```csharp
.WriteToFile(
    directory: "./logs",
    maxFileSize: 100 * 1024 * 1024,  // 100 MB
    maxDirectorySize: 10L * 1024 * 1024 * 1024,  // 10 GB
    compression: CompressionFormat.GZip,
    fileRetentionPolicy: new FileRetentionPolicy { RetentionDays = 30 })
```



# EonaCat.LogStack.Server

A lightweight, multi-transport log server for the EonaCat LogStack ecosystem.

## Quick start

```csharp
// Minimal - UDP on port 5555
var server = new Server();
await server.Start();

// Full control via ServerOptions
var server = new Server(new ServerOptions
{
    UseTcp            = true,
    UseUdp            = true,
    UseHttp           = true,       // enables POST /ingest + GET /metrics
    Port              = 5555,
    HttpPort          = 5556,
    MinimumLevel      = ServerLogLevel.Information,   // drop Debug/Trace
    RateLimitPerSecond = 100,       // per remote endpoint
    LogRetentionDays  = 30,
    MaxLogDirectorySize = 10L * 1024 * 1024 * 1024,  // 10 GB
    LogsRootDirectory = "logs",
});

server.LogWritten += line => Console.WriteLine("[written] " + line);
server.LogDropped += line => Console.WriteLine("[dropped] " + line);

await server.Start();
```

## Transports

| Transport | Default | Notes |
|-----------|---------|-------|
| TCP | enabled | Streams until connection closes |
| UDP | enabled | Max 65 507 bytes per packet |
| HTTP | disabled | Enable via `UseHttp = true` |

TCP and UDP can run simultaneously on the same port.

## HTTP endpoints (when `UseHttp = true`)

| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/ingest` | Accept a JSON log entry or array |
| `GET`  | `/metrics` | Return live server metrics as JSON |

### POST /ingest - single entry
```json
{ "level": "info", "message": "Hello world", "source": "MyApp", "host": "srv-01" }
```

### POST /ingest - batch
```json
[
  { "level": "warn",  "message": "Disk at 80%",  "source": "monitor" },
  { "level": "error", "message": "DB timeout",   "source": "api", "exception": "TimeoutException…" }
]
```

### GET /metrics response
```json
{
  "totalReceived": 12345,
  "totalWritten": 12300,
  "totalDropped": 45,
  "totalBytes": 4096000,
  "activeTcpConnections": 3,
  "uptimeSeconds": 3600,
  "startedAt": "2026-03-27T08:00:00Z"
}
```

## Structured JSON parsing

If the incoming payload is valid JSON the server parses it and formats each
entry before writing:

```
[2026-03-27T09:15:00Z] [ERROR] [MyApp] host=srv-01 trace=abc123 Something went wrong
  EXCEPTION: System.TimeoutException: The operation timed out.
```

Recognised JSON fields:

| Field | Aliases | Description |
|-------|---------|-------------|
| `timestamp` | - | ISO-8601 timestamp |
| `level` | `Level`, `severity`, `Severity` | Log level string |
| `message` | `Message` | Log message |
| `source` | `application` | App / service name |
| `host` | - | Hostname |
| `traceId` | - | Distributed trace ID |
| `exception` | - | Exception string |

Plain-text payloads are written as-is and always bypass the level filter.

## Log level filtering

```csharp
MinimumLevel = ServerLogLevel.Warning  // only Warning / Error / Critical are stored
```

Levels in order: `Trace → Debug → Information → Warning → Error → Critical`

## Rate limiting

```csharp
RateLimitPerSecond = 100  // per remote IP:port, 0 = disabled
```

Dropped messages are counted in `Metrics.TotalDropped` and raise the `LogDropped` event.

## Metrics

```csharp
var m = server.GetMetrics();
Console.WriteLine($"Written={m.TotalWritten} Dropped={m.TotalDropped} Uptime={m.Uptime}");
```

## Log file layout

```
logs/
  20260327/
    EonaCatLogs.log          ← active file (≤ 200 MB)
    EonaCatLogs_1.log        ← rolled over
  20260326/
    EonaCatLogs.log
```

Daily directories older than `LogRetentionDays` are deleted automatically.
The total directory is also capped at `MaxLogDirectorySize`.

## Events

```csharp
server.LogWritten += line => NotifyDashboard(line);
server.LogDropped += line => Metrics.Increment("dropped");
```

## Graceful shutdown

```csharp
Console.CancelKeyPress += (_, e) => { e.Cancel = true; server.Stop(); };
```

`Stop()` prints a throughput summary and disposes all listeners cleanly.


## Dependency Injection (DI) registration methods

### 1. Basic Registration (Simplest)

Register with default settings:

```csharp
services.AddEonaCatLogging();
```

This registers:
- `ILoggerFactory` - For creating category-specific loggers
- `Microsoft.Extensions.Logging.ILoggerFactory` - For Microsoft.Extensions.Logging compatibility
- `ILogger` - For injecting the default logger

### 2. Registration with Log Level and Timestamp Mode

```csharp
services.AddEonaCatLogging(
    minimumLevel: LogLevel.Information,
    timestampMode: TimestampMode.Local);
```

### 3. Registration with Pre-built EonaCatLogStack

If you've already created an `EonaCatLogStack` instance:

```csharp
var logStack = new EonaCatLogStack("MyApp");
logStack.AddFlow(new ConsoleFlow());

services.AddEonaCatLogging(logStack);
```

### 4. Registration with Configuration Callback (Recommended)

Configure the logger directly with an `Action<EonaCatLogStack>`:

```csharp
services.AddEonaCatLogging(logStack =>
{
    logStack.AddFlow(new ConsoleFlow());
    logStack.AddFlow(new FileFlow("./logs"));
    logStack.AddBooster(new MachineNameBooster());
});
```

### 5. Registration with LogBuilder (Most Fluent - Recommended)

Use the fluent `LogBuilder` API for the most intuitive configuration:

```csharp
services.AddEonaCatLogging("MyApplication", builder =>
{
    builder
        .WithMinimumLevel(LogLevel.Information)
        .WithTimestampMode(TimestampMode.Local)
        .WriteToConsole()
        .WriteToFile("./logs")
        .BoostWithMachineName()
        .BoostWithProcessId()
        .BoostWithCorrelationId();
});
```

Or with the default "Application" category:

```csharp
services.AddEonaCatLogging(builder =>
{
    builder
        .WriteToConsole()
        .WriteToFile("./logs");
});
```

### 6. Registration with Factory Method (Advanced)

For advanced scenarios where you need access to the service provider:

```csharp
services.AddEonaCatLoggingFactory("MyApplication", builder =>
{
    builder
        .WriteToConsole()
        .WriteToFile("./logs");
});
```

## Using in Your Application

### Injecting ILoggerFactory

```csharp
public class MyService
{
    private readonly ILoggerFactory _loggerFactory;

    public MyService(ILoggerFactory loggerFactory)
    {
        _loggerFactory = loggerFactory;
    }

    public void DoSomething()
    {
        var logger = _loggerFactory.CreateLogger("MyService");
        logger.Log(LogLevel.Information, "Doing something");
    }
}
```

### Injecting ILogger

```csharp
public class MyService
{
    private readonly ILogger _logger;

    public MyService(ILogger logger)
    {
        _logger = logger;
    }

    public void DoSomething()
    {
        _logger.Log(LogLevel.Information, "Doing something");
    }
}
```

### Using with Microsoft.Extensions.Logging.ILogger

```csharp
public class MyService
{
    private readonly Microsoft.Extensions.Logging.ILogger _logger;

    public MyService(Microsoft.Extensions.Logging.ILogger logger)
    {
        _logger = logger;
    }

    public void DoSomething()
    {
        _logger.LogInformation("Doing something");
    }
}
```

## ASP.NET Core / Razor Pages Integration

In your `Program.cs`:

```csharp
var builder = WebApplication.CreateBuilder(args);

// Add EonaCat LogStack to the service collection
builder.Services.AddEonaCatLogging("WebApplication", logBuilder =>
{
    logBuilder
        .WithMinimumLevel(LogLevel.Information)
        .WriteToConsole(useColors: true)
        .WriteToFile("./logs")
        .BoostWithCorrelationId()
        .BoostWithThreadId();
});

// Rest of your configuration...
var app = builder.Build();

// Configure HTTP request pipeline...
app.Run();
```

### In Razor Page Code-Behind

```csharp
public class IndexModel : PageModel
{
    private readonly ILogger _logger;

    public IndexModel(ILogger logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
        _logger.Log(LogLevel.Information, "Index page loaded");
    }
}
```

## Features

### Multiple Output Destinations (Flows)

```csharp
services.AddEonaCatLogging(builder =>
{
    builder
        .WriteToConsole()                          // Console output
        .WriteToFile("./logs")                     // File output
        .WriteToSlack("https://hooks.slack.com/...")  // Slack
        .WriteToDiscord("https://discordapp.com/...")  // Discord
        .WriteToElasticSearch("http://localhost:9200") // Elasticsearch
        .WriteToEmail("smtp.gmail.com", 587, ...)      // Email
        .WriteToMicrosoftTeams("https://...");         // Teams
});
```

### Enriching Log Events (Boosters)

```csharp
services.AddEonaCatLogging(builder =>
{
    builder
        .BoostWithMachineName()      // Adds machine name
        .BoostWithProcessId()        // Adds process ID
        .BoostWithThreadId()         // Adds thread ID
        .BoostWithCorrelationId()    // Adds correlation ID (for distributed tracing)
        .BoostWithMemory()           // Adds memory usage
        .BoostWithOS()               // Adds OS info
        .BoostWithUser()             // Adds username
        .BoostWithCustomText("Environment", "Production"); // Custom properties
});
```

### Log Level Filtering

```csharp
services.AddEonaCatLogging(builder =>
{
    builder
        .WithMinimumLevel(LogLevel.Warning)  // Only log warnings and above
        .WriteToConsole(minimumLevel: LogLevel.Information)  // More verbose for console
        .WriteToFile("./logs", minimumLevel: LogLevel.Error); // Only errors to file
});
```

## Best Practices

1. **Use LogBuilder for Configuration**: The fluent LogBuilder API is the most readable and maintainable approach.

2. **Register Early**: Register logging in `Program.cs` before other services that depend on logging.

3. **Use Appropriate Log Levels**: 
   - `Trace` - Very detailed diagnostic info
   - `Debug` - Debug-level diagnostic info
   - `Information` - General informational messages
   - `Warning` - Warning messages
   - `Error` - Error messages
   - `Critical` - Critical failures

4. **Inject Specific Types**: Prefer injecting `ILoggerFactory` to create category-specific loggers rather than injecting a single shared logger.

5. **Use Categories**: Create loggers with meaningful category names:
   ```csharp
   var logger = loggerFactory.CreateLogger("MyApp.Services.UserService");
   ```

6. **Enable Correlation IDs**: For distributed tracing scenarios:
   ```csharp
   builder.BoostWithCorrelationId()
   ```

## Configuration Examples

### Minimal Setup (Console Only)

```csharp
services.AddEonaCatLogging(b => b.WriteToConsole());
```

### Development Environment

```csharp
services.AddEonaCatLogging(builder =>
{
    builder
        .WithMinimumLevel(LogLevel.Debug)
        .WriteToConsole(useColors: true)
        .WriteToFile("./logs")
        .BoostWithMachineName()
        .BoostWithThreadId();
});
```

### Production Environment

```csharp
services.AddEonaCatLogging("ProductionApp", builder =>
{
    builder
        .WithMinimumLevel(LogLevel.Information)
        .WriteToFile("./logs", minimumLevel: LogLevel.Information)
        .WriteToElasticSearch("https://elastic.company.com")
        .WriteToSlack("https://hooks.slack.com/...")
        .BoostWithCorrelationId()
        .BoostWithMachineName()
        .BoostWithUser();
});
```

## OpenTelemetry-style Telemetry

EonaCat.LogStack includes a dependency-free telemetry layer inspired by OpenTelemetry concepts.

### Distributed tracing

```csharp
var source = new TelemetryActivitySource();
using var span = source.StartScope("ProcessOrder", telemetry =>
{
    telemetry.Attributes["order.id"] = "123";
});
```

Supported tracing concepts:
- Trace IDs and Span IDs
- Parent span relationships
- Span duration measurement
- Span attributes
- Span events
- Status tracking

### Metrics

```csharp
var meter = new TelemetryMeter();
meter.Count("requests.total");
meter.Record("request.duration", 25.5);
var p95 = meter.GetHistogram("request.duration")?.Percentile(0.95);
```

Telemetry features:
- Counters
- Histograms
- Percentiles
- Runtime snapshots
- Resource attributes
- Baggage/context propagation
- HTTP telemetry ingestion

### Resource metadata

```csharp
var resource = new TelemetryResource()
    .Add("service.name", "OrderService")
    .Add("deployment.environment", "production");
```

## Diagnostics

```csharp
public class DiagnosticsService
{
    private readonly ILoggerFactory _loggerFactory;

    public DiagnosticsService(ILoggerFactory loggerFactory)
    {
        _loggerFactory = loggerFactory;
    }

    public void PrintDiagnostics()
    {
        var diagnostics = _loggerFactory.GetDiagnostics();
        Console.WriteLine($"Total Logged: {diagnostics.TotalLogged}");
        Console.WriteLine($"Total Dropped: {diagnostics.TotalDropped}");
        Console.WriteLine($"Total Exceptions: {diagnostics.TotalExceptions}");
    }
}
```

## Statistics & Metrics

EonaCat.LogStack provides comprehensive statistics and metrics tracking for monitoring and optimizing your logging infrastructure.

### Per-Level Metrics

Track events logged at each level:

```csharp
var metrics = loggerFactory.GetMetrics();

Console.WriteLine($"Trace:       {metrics.TraceCount}");
Console.WriteLine($"Debug:       {metrics.DebugCount}");
Console.WriteLine($"Information: {metrics.InformationCount}");
Console.WriteLine($"Warning:     {metrics.WarningCount}");
Console.WriteLine($"Error:       {metrics.ErrorCount}");
Console.WriteLine($"Critical:    {metrics.CriticalCount}");
```

### Performance Metrics

Monitor throughput and latency:

```csharp
var metrics = loggerFactory.GetMetrics();

Console.WriteLine($"Events/Second:       {metrics.WritesPerSecond:F2}");
Console.WriteLine($"Total Bytes:         {metrics.TotalBytes:N0}");
Console.WriteLine($"Avg Bytes/Event:     {metrics.AverageBytesPerEvent:F2}");
Console.WriteLine($"Success Rate:        {metrics.SuccessRate:F2}%");
Console.WriteLine($"Uptime:              {TimeSpan.FromMilliseconds(metrics.UptimeMilliseconds):hh\\:mm\\:ss}");
```

### Exception Tracking

Monitor exceptions logged:

```csharp
var metrics = loggerFactory.GetMetrics();
Console.WriteLine($"Total Exceptions:    {metrics.TotalExceptions}");
Console.WriteLine($"Exceptions in Errors: {metrics.ErrorCount} errors logged");
```

### Flow-Specific Statistics

Each flow tracks its own performance:

```csharp
var metrics = loggerFactory.GetMetrics();

foreach (var flow in metrics.FlowMetrics)
{
    Console.WriteLine($"{flow.FlowName} ({flow.FlowType}):");
    Console.WriteLine($"  Processed: {flow.EventsProcessed}");
    Console.WriteLine($"  Failed: {flow.EventsFailed}");
    Console.WriteLine($"  Success Rate: {flow.SuccessRate:F2}%");
    Console.WriteLine($"  Events/Second: {flow.EventsPerSecond:F2}");
    Console.WriteLine($"  P95 Latency: {flow.P95LatencyMs:F3}ms");
    Console.WriteLine($"  P99 Latency: {flow.P99LatencyMs:F3}ms");
}
```

### System-Wide Metrics Collection

Use `AdvancedMetricsCollector` to aggregate metrics across multiple loggers:

```csharp
var collector = app.Services.GetRequiredService<AdvancedMetricsCollector>();

// Register loggers for collection
var logStack = app.Services.GetRequiredService<EonaCatLogStack>();
collector.RegisterLogger(logStack);

// Get aggregated metrics
var aggregated = collector.GetAggregatedMetrics();
Console.WriteLine($"Total Logged (All): {aggregated.TotalLoggedAcrossAll:N0}");
Console.WriteLine($"Overall Success Rate: {aggregated.OverallSuccessRate:F2}%");

// Get performance comparison
var comparison = collector.GetPerformanceComparison();
Console.WriteLine($"Highest Throughput: {comparison.HighestThroughputLogger?.WritesPerSecond:F2} events/sec");
Console.WriteLine($"Average Throughput: {comparison.AverageWritesPerSecond:F2} events/sec");

// Get health report
var health = collector.GetHealthReport();
Console.WriteLine($"System Status: {health.OverallStatus}");
foreach (var warning in health.Warnings)
    Console.WriteLine($"  ⚠️ {warning}");
```

## Dependency Injection Integration

EonaCat.LogStack seamlessly integrates with Microsoft Dependency Injection for ASP.NET Core and other .NET applications.

### Basic Registration

```csharp
var builder = Host.CreateApplicationBuilder();
builder.Services.AddEonaCatLogging();
```

This registers:
- `ILoggerFactory` (EonaCat interface)
- `Microsoft.Extensions.Logging.ILoggerFactory` (standard interface)
- `ILogger` (EonaCat interface)
- `Microsoft.Extensions.Logging.ILogger` (standard interface)

### With Fluent Configuration

```csharp
var builder = Host.CreateApplicationBuilder();
builder.Services.AddEonaCatLogging(b =>
{
    b.WithMinimumLevel(LogLevel.Information)
        .WriteToConsole()
        .WriteToFile("./logs")
        .BoostWithCorrelationId();
});
```

### With HostApplicationBuilder (Recommended)

```csharp
var builder = Host.CreateApplicationBuilder();

builder.AddEonaCatLogging(b =>
{
    b.WithMinimumLevel(LogLevel.Information)
        .WriteToConsole()
        .WriteToFile("./logs");
});

var app = builder.Build();
```

### Advanced Features with AdvancedLoggerFactory

For category-specific configuration and context propagation:

```csharp
var builder = Host.CreateApplicationBuilder();

builder.Services.AddAdvancedEonaCatLogging(factory =>
{
    factory.ConfigureCategory("Database", config =>
    {
        config.MinimumLevel = LogLevel.Debug;
        config.Boosters.Add(new CorrelationIdBooster());
    });

    factory.ConfigureCategory("Security", config =>
    {
        config.MinimumLevel = LogLevel.Warning;
    });
});

var app = builder.Build();

// Get loggers
var dbLogger = app.Services.GetRequiredService<ILoggerFactory>().CreateLogger("Database");
var secLogger = app.Services.GetRequiredService<ILoggerFactory>().CreateLogger("Security");
```

### With Metrics Collection

```csharp
var builder = Host.CreateApplicationBuilder();

builder.AddEonaCatLogging(b =>
{
    b.WithMinimumLevel(LogLevel.Information)
        .WriteToConsole()
        .WriteToFile("./logs");
});

builder.AddEonaCatMetricsCollection();

var app = builder.Build();

// Access metrics
var collector = app.Services.GetRequiredService<AdvancedMetricsCollector>();
var metrics = collector.GetAggregatedMetrics();
Console.WriteLine(metrics);
```

### Using Microsoft.Extensions.Logging

The registered adapters allow you to use standard Microsoft logging interfaces:

```csharp
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
    private readonly Microsoft.Extensions.Logging.ILogger<UserController> _logger;

    public UserController(Microsoft.Extensions.Logging.ILogger<UserController> logger)
    {
        _logger = logger;
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetUser(int id)
    {
        _logger.LogInformation("Fetching user {UserId}", id);
        // ...
    }
}
```

The request is automatically routed through EonaCat.LogStack flows (file, console, Slack, etc.).

### Context Propagation

With AdvancedLoggerFactory, you can propagate context across async operations:

```csharp
var factory = app.Services.GetRequiredService<AdvancedLoggerFactory>();

// Set context in request scope
factory.SetContextData("RequestId", context.TraceIdentifier);
factory.SetContextData("UserId", user.Id);

// Logger can access this context
var logger = factory.CreateLogger("MyCategory");
logger.Log(LogLevel.Information, "Processing request");
// Context is included automatically

// Clear context when done
factory.ClearContextData();
```

## Disposing of the Logger

The logger is registered as a Singleton in the DI container, so it will be automatically disposed when the application shuts down. You can also manually access and dispose it:

```csharp
var loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
await loggerFactory.DisposeAsync();
```

## Telemetry Enhancements

The telemetry subsystem provides production observability features:

- **Runtime health snapshots** - expose processed events, failures, active spans, uptime and custom health checks.
- **Telemetry exporters** - plug in exporters for Prometheus, OpenTelemetry collectors, dashboards or custom monitoring platforms.
- **Telemetry snapshots** - collect metrics, resource information and health state into a single export model.
- **Custom instrumentation support** - extend telemetry with application-specific counters and tracing data.

Example:

```csharp
var snapshot = new TelemetrySnapshot
{
    Metrics = logger.GetMetrics(),
    Health = new TelemetryHealth
    {
        IsHealthy = true,
        Checks = { ["storage"] = "ok" }
    }
};

await exporter.ExportAsync(snapshot);
```


## Telemetry

EonaCat.LogStack includes an enhanced telemetry layer focused on operational intelligence:

- **Adaptive telemetry engine** - dynamically tracks signals and adjusts analysis based on runtime behavior.
- **Built-in anomaly detection** - detects unusual latency, throughput, and value deviations without external analysis systems.
- **Zero-dependency telemetry snapshots** - capture complete runtime state without requiring a collector.
- **Signal intelligence** - calculates count, min, max, averages, and runtime trends for every signal.
- **Telemetry-first logging correlation** - combines logs, metrics, traces, and context into a single pipeline.
- **Runtime diagnostics** - exposes health, performance, and flow statistics from the same engine.
- **Self-contained observability** - designed to work without mandatory agents or external telemetry infrastructure.

Example:

```csharp
var telemetry = new AdaptiveTelemetryEngine();

telemetry.Record("request.duration.ms", 42);
telemetry.Record("cache.hit.rate", 0.98);

var snapshot = telemetry.Snapshot();
```



---

# Additional Usage Guide

This section expands on practical usage patterns, production configuration, telemetry, diagnostics, and operational guidance.

## Complete Application Startup Example

A typical production application can configure logging once during startup and reuse the same logger instance:

```csharp
await using var logger = new LogBuilder("InventoryService")
    .WithMinimumLevel(LogLevel.Information)
    .WithTimestampMode(TimestampMode.Utc)

    .WriteToConsole(useColors: true)

    .WriteToFile(
        directory: "./logs",
        filePrefix: "inventory",
        maxFileSize: 250 * 1024 * 1024,
        compression: CompressionFormat.GZip,
        batchSize: 100)

    .WriteToElasticSearch(
        elasticSearchUrl: "https://elastic.example.com:9200",
        indexName: "inventory")

    .BoostWithApplication("InventoryService", "3.0.0")
    .BoostWithEnvironment("Production")
    .BoostWithMachineName()
    .BoostWithProcessId()
    .BoostWithCorrelationId()

    .Build();

logger.Information("Service started");

await RunApplicationAsync();

await logger.FlushAsync();
```

## Telemetry Explained

Telemetry in EonaCat.LogStack provides operational insight into the logging pipeline itself.

Telemetry can answer questions such as:

- How many log events were created?
- Are any events being dropped?
- Which flow is slow?
- Are remote sinks failing?
- How much memory is the process using?
- How long are operations taking?

### Enable Telemetry

```csharp
var telemetry = new TelemetryOptions
{
    Enabled = true,
    IncludeRuntimeMetrics = true,
    IncludeTraceMetrics = true
};
```

### Runtime Metrics

Runtime metrics include information such as:

- Process uptime
- Memory usage
- Garbage collection information
- Thread information
- CPU/runtime counters where available

Example:

```csharp
var snapshot = logger.GetTelemetrySnapshot();

Console.WriteLine(snapshot.Runtime.ProcessId);
Console.WriteLine(snapshot.Runtime.MemoryUsage);
Console.WriteLine(snapshot.Runtime.Uptime);
```

## Flow Metrics

Every flow maintains statistics.

Example:

```csharp
var diagnostics = logger.GetDiagnostics();

foreach (var flow in diagnostics.FlowStats)
{
    Console.WriteLine(flow.Name);
    Console.WriteLine($"Processed: {flow.Processed}");
    Console.WriteLine($"Dropped: {flow.Dropped}");
    Console.WriteLine($"Errors: {flow.ErrorCount}");
}
```

Useful production alerts:

```csharp
if (diagnostics.TotalDropped > 0)
{
    logger.Warning(
        "Logs are being dropped",
        ("Dropped", diagnostics.TotalDropped));
}
```

## Distributed Tracing

Correlation IDs connect logs from the same request or operation.

Enable it:

```csharp
new LogBuilder("Api")
    .BoostWithCorrelationId()
    .Build();
```

A request might then produce:

```
TraceId=abc123
Request started

TraceId=abc123
Database query executed

TraceId=abc123
Request completed
```

This makes debugging distributed applications easier.

## Custom Telemetry Export

Telemetry snapshots can be forwarded to custom monitoring systems.

Example:

```csharp
var snapshot = logger.GetTelemetrySnapshot();

SendToMonitoringSystem(new
{
    Service = snapshot.ServiceName,
    Environment = snapshot.Environment,
    Metrics = snapshot.Metrics
});
```

## Logging HTTP Requests

Example request logging:

```csharp
logger.Information(
    "HTTP request completed",
    ("Method", "GET"),
    ("Path", "/api/products"),
    ("StatusCode", 200),
    ("DurationMs", 42));
```

Output:

```
HTTP request completed
Method=GET
Path=/api/products
StatusCode=200
DurationMs=42
```

## Recommended Production Layout

A common production setup:

```
Application
 |
 +--> Console (developer visibility)
 |
 +--> Local File (fallback)
 |
 +--> Elasticsearch (search)
 |
 +--> Audit Flow (compliance)
 |
 +--> Slack/Teams (alerts)
 |
 +--> Diagnostics (monitoring)
```

Example:

```csharp
await using var logger = new LogBuilder("Payments")

    .WriteToConsole()

    .WriteToFile("./logs")

    .WriteToAudit(
        directory: "./audit",
        auditLevel: AuditLevel.WarningAndAbove)

    .WriteToThrottled(
        inner: new SlackFlow(slackUrl),
        minimumLevel: LogLevel.Error)

    .WriteDiagnostics(
        snapshotInterval: TimeSpan.FromMinutes(1))

    .BoostWithCorrelationId()
    .Build();
```

## Shutdown Handling

Always flush buffered logs during shutdown:

```csharp
try
{
    await Application.RunAsync();
}
finally
{
    await logger.FlushAsync();
    await logger.DisposeAsync();
}
```

## Debugging Logging Problems

Check diagnostics first:

```csharp
var stats = logger.GetDiagnostics();

Console.WriteLine(
    $"Written: {stats.TotalLogged}");

Console.WriteLine(
    $"Dropped: {stats.TotalDropped}");
```

If events are missing:

1. Check minimum log levels.
2. Check flow filters.
3. Check dropped counters.
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.
