Initial version
This commit is contained in:
39
EonaCat.LogStack.LogClient/EonaCat.LogStack.LogClient.csproj
Normal file
39
EonaCat.LogStack.LogClient/EonaCat.LogStack.LogClient.csproj
Normal file
@@ -0,0 +1,39 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageId>EonaCat.LogStack.LogClient</PackageId>
|
||||
<Version>0.0.1</Version>
|
||||
<Authors>EonaCat (Jeroen Saey)</Authors>
|
||||
<Description>Logging client for the EonaCat Logger LogServer LogStack</Description>
|
||||
<PackageTags>logging;monitoring;analytics;diagnostics</PackageTags>
|
||||
<Copyright>EonaCat (Jeroen Saey)</Copyright>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
<PackageReadmeFile>readme.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://git.saey.me/EonaCat/EonaCat.LogStack.LogClient</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\EonaCat.LogStack\icon.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
<None Include="..\LICENSE">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Net.Http.Json" Version="10.0.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\EonaCat.LogStack\EonaCat.LogStack.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="readme.md">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
207
EonaCat.LogStack.LogClient/LogCentralClient.cs
Normal file
207
EonaCat.LogStack.LogClient/LogCentralClient.cs
Normal file
@@ -0,0 +1,207 @@
|
||||
using EonaCat.LogStack.LogClient.Models;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
||||
|
||||
namespace EonaCat.LogStack.LogClient
|
||||
{
|
||||
public class LogCentralClient : IDisposable
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly LogCentralOptions _options;
|
||||
private readonly ConcurrentQueue<LogEntry> _logQueue;
|
||||
private readonly Timer _flushTimer;
|
||||
private readonly SemaphoreSlim _flushSemaphore;
|
||||
private bool _disposed;
|
||||
|
||||
public LogCentralClient(LogCentralOptions options)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_httpClient = new HttpClient { BaseAddress = new Uri(_options.ServerUrl) };
|
||||
_httpClient.DefaultRequestHeaders.Add("X-API-Key", _options.ApiKey);
|
||||
_logQueue = new ConcurrentQueue<LogEntry>();
|
||||
_flushSemaphore = new SemaphoreSlim(1, 1);
|
||||
_flushTimer = new Timer(async _ => await FlushAsync(), null,
|
||||
TimeSpan.FromSeconds(_options.FlushIntervalSeconds),
|
||||
TimeSpan.FromSeconds(_options.FlushIntervalSeconds));
|
||||
}
|
||||
|
||||
public async Task LogAsync(LogEntry entry)
|
||||
{
|
||||
entry.ApplicationName = _options.ApplicationName;
|
||||
entry.ApplicationVersion = _options.ApplicationVersion;
|
||||
entry.Environment = _options.Environment;
|
||||
entry.Timestamp = DateTime.UtcNow;
|
||||
|
||||
entry.MachineName ??= Environment.MachineName;
|
||||
entry.Category ??= entry.Category ?? "Default";
|
||||
entry.Message ??= entry.Message ?? "";
|
||||
|
||||
_logQueue.Enqueue(entry);
|
||||
|
||||
if (_logQueue.Count >= _options.BatchSize)
|
||||
{
|
||||
await FlushAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LogExceptionAsync(Exception ex, string message = "",
|
||||
Dictionary<string, object>? properties = null)
|
||||
{
|
||||
await LogAsync(new LogEntry
|
||||
{
|
||||
Level = (int)LogLevel.Error,
|
||||
Category = "Exception",
|
||||
Message = message,
|
||||
Exception = ex.ToString(),
|
||||
StackTrace = ex.StackTrace,
|
||||
Properties = properties
|
||||
});
|
||||
}
|
||||
|
||||
public async Task LogSecurityEventAsync(string eventType, string message,
|
||||
Dictionary<string, object>? properties = null)
|
||||
{
|
||||
await LogAsync(new LogEntry
|
||||
{
|
||||
Level = (int)LogLevel.Security,
|
||||
Category = "Security",
|
||||
Message = $"[{eventType}] {message}",
|
||||
Properties = properties
|
||||
});
|
||||
}
|
||||
|
||||
public async Task LogAnalyticsAsync(string eventName,
|
||||
Dictionary<string, object>? properties = null)
|
||||
{
|
||||
await LogAsync(new LogEntry
|
||||
{
|
||||
Level = (int)LogLevel.Analytics,
|
||||
Category = "Analytics",
|
||||
Message = eventName,
|
||||
Properties = properties
|
||||
});
|
||||
}
|
||||
|
||||
private async Task FlushAsync()
|
||||
{
|
||||
if (_logQueue.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _flushSemaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
var batch = new List<LogEntry>();
|
||||
while (batch.Count < _options.BatchSize && _logQueue.TryDequeue(out var entry))
|
||||
{
|
||||
batch.Add(entry);
|
||||
}
|
||||
|
||||
if (batch.Count > 0)
|
||||
{
|
||||
await SendBatchAsync(batch);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_flushSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendBatchAsync(List<LogEntry> entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Map EF entities to DTOs for API
|
||||
var dtos = entries.Select(e => new LogEntryDto
|
||||
{
|
||||
Id = e.Id,
|
||||
Timestamp = e.Timestamp,
|
||||
ApplicationName = e.ApplicationName,
|
||||
ApplicationVersion = e.ApplicationVersion,
|
||||
Environment = e.Environment,
|
||||
MachineName = e.MachineName,
|
||||
Level = e.Level,
|
||||
Category = e.Category,
|
||||
Message = e.Message,
|
||||
Exception = e.Exception,
|
||||
StackTrace = e.StackTrace,
|
||||
Properties = e.Properties,
|
||||
UserId = e.UserId,
|
||||
SessionId = e.SessionId,
|
||||
RequestId = e.RequestId,
|
||||
CorrelationId = e.CorrelationId
|
||||
}).ToList();
|
||||
|
||||
var response = await _httpClient.PostAsJsonAsync("/api/logs/batch", dtos);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (_options.EnableFallbackLogging)
|
||||
{
|
||||
Console.WriteLine($"[LogCentral] Failed to send logs: {ex.Message}");
|
||||
}
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
_logQueue.Enqueue(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task FlushAndDisposeAsync()
|
||||
{
|
||||
await FlushAsync();
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_flushTimer?.Dispose();
|
||||
FlushAsync().GetAwaiter().GetResult();
|
||||
_httpClient?.Dispose();
|
||||
_flushSemaphore?.Dispose();
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class LogEntryDto
|
||||
{
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||
public DateTime Timestamp { get; set; }
|
||||
public string ApplicationName { get; set; } = default!;
|
||||
public string ApplicationVersion { get; set; } = default!;
|
||||
public string Environment { get; set; } = default!;
|
||||
public string MachineName { get; set; } = default!;
|
||||
public int Level { get; set; }
|
||||
public string Category { get; set; } = default!;
|
||||
public string Message { get; set; } = default!;
|
||||
public string? Exception { get; set; }
|
||||
public string? StackTrace { get; set; }
|
||||
public Dictionary<string, object>? Properties { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string? SessionId { get; set; }
|
||||
public string? RequestId { get; set; }
|
||||
public string? CorrelationId { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
63
EonaCat.LogStack.LogClient/LogCentralEonaCatAdapter.cs
Normal file
63
EonaCat.LogStack.LogClient/LogCentralEonaCatAdapter.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using EonaCat.LogStack.Configuration;
|
||||
using EonaCat.LogStack.LogClient.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
||||
|
||||
namespace EonaCat.LogStack.LogClient
|
||||
{
|
||||
public class LogCentralEonaCatAdapter : IDisposable
|
||||
{
|
||||
private readonly LogCentralClient _client;
|
||||
private LogBuilder _logBuilder;
|
||||
|
||||
public LogCentralEonaCatAdapter(LogBuilder logBuilder, LogCentralClient client)
|
||||
{
|
||||
_client = client;
|
||||
_logBuilder.OnLog += LogSettings_OnLog;
|
||||
}
|
||||
|
||||
private void LogSettings_OnLog(object sender, LogMessage e)
|
||||
{
|
||||
var entry = new LogEntry
|
||||
{
|
||||
Level = (int)MapLogLevel(e.Level),
|
||||
Category = e.Category ?? "General",
|
||||
Message = e.Message,
|
||||
Properties = new Dictionary<string, object>
|
||||
{
|
||||
{ "Source", e.Origin ?? "Unknown" }
|
||||
}
|
||||
};
|
||||
|
||||
if (e.Exception != null)
|
||||
{
|
||||
entry.Exception = e.Exception.ToString();
|
||||
entry.StackTrace = e.Exception.StackTrace;
|
||||
}
|
||||
|
||||
_client.LogAsync(entry).ConfigureAwait(false);
|
||||
}
|
||||
private static LogLevel MapLogLevel(Core.LogLevel logType)
|
||||
{
|
||||
return logType switch
|
||||
{
|
||||
Core.LogLevel.Trace => LogLevel.Trace,
|
||||
Core.LogLevel.Debug => LogLevel.Debug,
|
||||
Core.LogLevel.Information => LogLevel.Information,
|
||||
Core.LogLevel.Warning => LogLevel.Warning,
|
||||
Core.LogLevel.Error => LogLevel.Error,
|
||||
Core.LogLevel.Critical => LogLevel.Critical,
|
||||
_ => LogLevel.Information
|
||||
};
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_logBuilder.OnLog -= LogSettings_OnLog;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
EonaCat.LogStack.LogClient/LogCentralOptions.cs
Normal file
21
EonaCat.LogStack.LogClient/LogCentralOptions.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
||||
|
||||
namespace EonaCat.LogStack.LogClient
|
||||
{
|
||||
public class LogCentralOptions
|
||||
{
|
||||
public string ServerUrl { get; set; } = "http://localhost:5000";
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
public string ApplicationName { get; set; } = string.Empty;
|
||||
public string ApplicationVersion { get; set; } = "1.0.0";
|
||||
public string Environment { get; set; } = "Production";
|
||||
public int BatchSize { get; set; } = 50;
|
||||
public int FlushIntervalSeconds { get; set; } = 5;
|
||||
public bool EnableFallbackLogging { get; set; } = true;
|
||||
}
|
||||
}
|
||||
18
EonaCat.LogStack.LogClient/LogLevel.cs
Normal file
18
EonaCat.LogStack.LogClient/LogLevel.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace EonaCat.LogStack.LogClient
|
||||
{
|
||||
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
||||
|
||||
public enum LogLevel
|
||||
{
|
||||
Trace = 0,
|
||||
Debug = 1,
|
||||
Information = 2,
|
||||
Warning = 3,
|
||||
Error = 4,
|
||||
Critical = 5,
|
||||
Traffic = 6,
|
||||
Security = 7,
|
||||
Analytics = 8
|
||||
}
|
||||
}
|
||||
67
EonaCat.LogStack.LogClient/Models/LogEntry.cs
Normal file
67
EonaCat.LogStack.LogClient/Models/LogEntry.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using EonaCat.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text;
|
||||
|
||||
namespace EonaCat.LogStack.LogClient.Models
|
||||
{
|
||||
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
||||
|
||||
public class LogEntry
|
||||
{
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
public string ApplicationName { get; set; } = default!;
|
||||
public string ApplicationVersion { get; set; } = default!;
|
||||
public string Environment { get; set; } = default!;
|
||||
public string MachineName { get; set; } = default!;
|
||||
public int Level { get; set; }
|
||||
public string Category { get; set; } = default!;
|
||||
public string Message { get; set; } = default!;
|
||||
|
||||
public string? Exception { get; set; }
|
||||
public string? StackTrace { get; set; }
|
||||
|
||||
[Column(TypeName = "TEXT")]
|
||||
public string? PropertiesJson { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public Dictionary<string, object>? Properties
|
||||
{
|
||||
get => string.IsNullOrEmpty(PropertiesJson)
|
||||
? null
|
||||
: JsonHelper.ToObject<Dictionary<string, object>>(PropertiesJson);
|
||||
set => PropertiesJson = value == null ? null : JsonHelper.ToJson(value);
|
||||
}
|
||||
|
||||
public string? UserId { get; set; }
|
||||
public string? SessionId { get; set; }
|
||||
public string? RequestId { get; set; }
|
||||
public string? CorrelationId { get; set; }
|
||||
|
||||
|
||||
public static LogEntryDto ToDto(LogEntry entry) => new LogEntryDto()
|
||||
{
|
||||
Id = entry.Id,
|
||||
Timestamp = entry.Timestamp,
|
||||
ApplicationName = entry.ApplicationName,
|
||||
ApplicationVersion = entry.ApplicationVersion,
|
||||
Environment = entry.Environment,
|
||||
MachineName = entry.MachineName,
|
||||
Level = entry.Level,
|
||||
Category = entry.Category,
|
||||
Message = entry.Message,
|
||||
Exception = entry.Exception,
|
||||
StackTrace = entry.StackTrace,
|
||||
Properties = entry.Properties,
|
||||
UserId = entry.UserId,
|
||||
SessionId = entry.SessionId,
|
||||
RequestId = entry.RequestId,
|
||||
CorrelationId = entry.CorrelationId
|
||||
};
|
||||
}
|
||||
}
|
||||
389
EonaCat.LogStack.LogClient/readme.md
Normal file
389
EonaCat.LogStack.LogClient/readme.md
Normal file
@@ -0,0 +1,389 @@
|
||||
# EonaCat.LogStack.LogClient
|
||||
|
||||
### Client Installation
|
||||
|
||||
#### Via NuGet Package Manager:
|
||||
```bash
|
||||
dotnet add package EonaCat.LogStack.LogClient
|
||||
```
|
||||
|
||||
#### Via Package Manager Console:
|
||||
```powershell
|
||||
Install-Package EonaCat.LogStack.LogClient
|
||||
```
|
||||
|
||||
## 📖 Usage Examples
|
||||
|
||||
### Basic Setup
|
||||
|
||||
```csharp
|
||||
using EonaCat.LogStack.LogClient;
|
||||
using EonaCat.LogStack.LogClient.Models;
|
||||
|
||||
// Configure the client
|
||||
var options = new LogCentralOptions
|
||||
{
|
||||
ServerUrl = "https://your-logcentral-server.com",
|
||||
ApiKey = "your-api-key-here",
|
||||
ApplicationName = "MyAwesomeApp",
|
||||
ApplicationVersion = "1.0.0",
|
||||
Environment = "Production",
|
||||
BatchSize = 50,
|
||||
FlushIntervalSeconds = 5
|
||||
};
|
||||
|
||||
var logClient = new LogCentralClient(options);
|
||||
```
|
||||
|
||||
### Integration with EonaCat.LogStack
|
||||
|
||||
```csharp
|
||||
using EonaCat.LogStack;
|
||||
using EonaCat.LogStack.LogClient.Integration;
|
||||
|
||||
var loggerSettings = new LoggerSettings();
|
||||
loggerSettings.UseLocalTime = true;
|
||||
loggerSettings.Id = "TEST";
|
||||
var logger = new LogManager(loggerSettings);
|
||||
|
||||
// Create the adapter
|
||||
var adapter = new LogCentralEonaCatAdapter(loggerSettings, logClient);
|
||||
|
||||
// Now all EonaCat.LogStack logs will be sent to LogCentral automatically
|
||||
logger.Log("Application started", LogLevel.Info);
|
||||
logger.Log("User logged in", LogLevel.Info, "Authentication");
|
||||
```
|
||||
|
||||
### Manual Logging
|
||||
|
||||
```csharp
|
||||
// Simple log
|
||||
await logClient.LogAsync(new LogEntry
|
||||
{
|
||||
Level = LogLevel.Information,
|
||||
Category = "Startup",
|
||||
Message = "Application started successfully"
|
||||
});
|
||||
|
||||
// Log with properties
|
||||
await logClient.LogAsync(new LogEntry
|
||||
{
|
||||
Level = LogLevel.Information,
|
||||
Category = "UserAction",
|
||||
Message = "User performed action",
|
||||
UserId = "user123",
|
||||
Properties = new Dictionary<string, object>
|
||||
{
|
||||
["Action"] = "Purchase",
|
||||
["Amount"] = 99.99,
|
||||
["ProductId"] = "prod-456"
|
||||
}
|
||||
});
|
||||
|
||||
// Log exception
|
||||
try
|
||||
{
|
||||
// Your code
|
||||
throw new Exception("Something went wrong");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logClient.LogExceptionAsync(ex, "Error processing order",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
["OrderId"] = "12345",
|
||||
["CustomerId"] = "cust-789"
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Security Event Logging
|
||||
|
||||
```csharp
|
||||
await logClient.LogSecurityEventAsync(
|
||||
"LoginAttempt",
|
||||
"Failed login attempt detected",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
["Username"] = "admin",
|
||||
["IPAddress"] = "192.168.1.100",
|
||||
["Attempts"] = 5
|
||||
}
|
||||
);
|
||||
|
||||
await logClient.LogSecurityEventAsync(
|
||||
"UnauthorizedAccess",
|
||||
"Unauthorized API access attempt",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
["Endpoint"] = "/api/admin/users",
|
||||
["Method"] = "DELETE",
|
||||
["UserId"] = "user456"
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Analytics Logging
|
||||
|
||||
```csharp
|
||||
// Track user events
|
||||
await logClient.LogAnalyticsAsync("PageView",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
["Page"] = "/products/electronics",
|
||||
["Duration"] = 45.2,
|
||||
["Source"] = "Google"
|
||||
}
|
||||
);
|
||||
|
||||
await logClient.LogAnalyticsAsync("Purchase",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
["ProductId"] = "prod-123",
|
||||
["Price"] = 299.99,
|
||||
["Category"] = "Electronics",
|
||||
["PaymentMethod"] = "CreditCard"
|
||||
}
|
||||
);
|
||||
|
||||
await logClient.LogAnalyticsAsync("FeatureUsage",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
["Feature"] = "DarkMode",
|
||||
["Enabled"] = true,
|
||||
["Platform"] = "iOS"
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### ASP.NET Core Integration
|
||||
|
||||
```csharp
|
||||
// Program.cs or Startup.cs
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Register LogCentral
|
||||
var logCentralOptions = new LogCentralOptions
|
||||
{
|
||||
ServerUrl = builder.Configuration["LogCentral:ServerUrl"],
|
||||
ApiKey = builder.Configuration["LogCentral:ApiKey"],
|
||||
ApplicationName = "MyWebApp",
|
||||
ApplicationVersion = "1.0.0",
|
||||
Environment = builder.Environment.EnvironmentName
|
||||
};
|
||||
|
||||
var logClient = new LogCentralClient(logCentralOptions);
|
||||
builder.Services.AddSingleton(logClient);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Use middleware to log requests
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
|
||||
await logClient.LogAsync(new LogEntry
|
||||
{
|
||||
Level = LogLevel.Information,
|
||||
Category = "HTTP",
|
||||
Message = $"{context.Request.Method} {context.Request.Path}",
|
||||
RequestId = requestId,
|
||||
Properties = new Dictionary<string, object>
|
||||
{
|
||||
["Method"] = context.Request.Method,
|
||||
["Path"] = context.Request.Path.Value,
|
||||
["QueryString"] = context.Request.QueryString.Value
|
||||
}
|
||||
});
|
||||
|
||||
await next();
|
||||
});
|
||||
|
||||
app.Run();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Windows Service / Console App
|
||||
|
||||
```csharp
|
||||
using EonaCat.LogStack.LogClient;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
public class Worker : BackgroundService
|
||||
{
|
||||
private readonly LogCentralClient _logClient;
|
||||
|
||||
public Worker(LogCentralClient logClient)
|
||||
{
|
||||
_logClient = logClient;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await _logClient.LogAsync(new LogEntry
|
||||
{
|
||||
Level = LogLevel.Information,
|
||||
Category = "Service",
|
||||
Message = "Worker service started"
|
||||
});
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Your work here
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await _logClient.LogExceptionAsync(ex, "Error in worker");
|
||||
}
|
||||
}
|
||||
|
||||
await _logClient.FlushAndDisposeAsync();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### WPF / WinForms Application
|
||||
|
||||
```csharp
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private readonly LogCentralClient _logClient;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_logClient = new LogCentralClient(new LogCentralOptions
|
||||
{
|
||||
ServerUrl = "https://logs.mycompany.com",
|
||||
ApiKey = "your-api-key",
|
||||
ApplicationName = "MyDesktopApp",
|
||||
ApplicationVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(),
|
||||
Environment = "Production"
|
||||
});
|
||||
|
||||
Application.Current.DispatcherUnhandledException += OnUnhandledException;
|
||||
}
|
||||
|
||||
private async void OnUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
await _logClient.LogExceptionAsync(e.Exception, "Unhandled exception in UI");
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
protected override async void OnClosing(CancelEventArgs e)
|
||||
{
|
||||
await _logClient.FlushAndDisposeAsync();
|
||||
base.OnClosing(e);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 Advanced Features
|
||||
|
||||
### Correlation IDs for Distributed Tracing
|
||||
|
||||
```csharp
|
||||
var correlationId = Guid.NewGuid().ToString();
|
||||
|
||||
await logClient.LogAsync(new LogEntry
|
||||
{
|
||||
Level = LogLevel.Information,
|
||||
Category = "OrderProcessing",
|
||||
Message = "Order created",
|
||||
CorrelationId = correlationId,
|
||||
Properties = new Dictionary<string, object> { ["OrderId"] = "12345" }
|
||||
});
|
||||
|
||||
// In another service
|
||||
await logClient.LogAsync(new LogEntry
|
||||
{
|
||||
Level = LogLevel.Information,
|
||||
Category = "PaymentProcessing",
|
||||
Message = "Payment processed",
|
||||
CorrelationId = correlationId, // Same ID
|
||||
Properties = new Dictionary<string, object> { ["Amount"] = 99.99 }
|
||||
});
|
||||
```
|
||||
|
||||
### Performance Monitoring
|
||||
|
||||
```csharp
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
try
|
||||
{
|
||||
// Your operation
|
||||
await SomeSlowOperation();
|
||||
}
|
||||
finally
|
||||
{
|
||||
stopwatch.Stop();
|
||||
|
||||
await logClient.LogAsync(new LogEntry
|
||||
{
|
||||
Level = LogLevel.Information,
|
||||
Category = "Performance",
|
||||
Message = "Operation completed",
|
||||
Properties = new Dictionary<string, object>
|
||||
{
|
||||
["Operation"] = "DatabaseQuery",
|
||||
["DurationMs"] = stopwatch.ElapsedMilliseconds,
|
||||
["Status"] = "Success"
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 Dashboard Features
|
||||
|
||||
- **Real-time monitoring**: Auto-refreshes every 30 seconds
|
||||
- **Advanced search**: Full-text search across all log fields
|
||||
- **Filtering**: By application, environment, level, date range
|
||||
- **Charts**: Visual representation of log levels and trends
|
||||
- **Export**: Download logs as CSV or JSON
|
||||
- **Alerts**: Configure notifications for critical events (planned)
|
||||
|
||||
## 🔒 Security Best Practices
|
||||
|
||||
1. **Use HTTPS** for production deployments
|
||||
2. **Rotate API keys** regularly
|
||||
3. **Limit API key permissions** by application
|
||||
4. **Store API keys** in secure configuration (Azure Key Vault, AWS Secrets Manager)
|
||||
5. **Enable authentication** for dashboard access (add authentication middleware)
|
||||
|
||||
## 🚀 Deployment
|
||||
|
||||
### Docker Deployment
|
||||
|
||||
```dockerfile
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||
WORKDIR /app
|
||||
COPY --from=build /app/publish .
|
||||
ENTRYPOINT ["dotnet", "EonaCat.LogStack.LogServer.dll"]
|
||||
```
|
||||
|
||||
### Azure Deployment
|
||||
|
||||
```bash
|
||||
az webapp create --resource-group MyResourceGroup --plan MyPlan --name logcentral --runtime "DOTNETCORE:8.0"
|
||||
az webapp deployment source config-zip --resource-group MyResourceGroup --name logcentral --src logcentral.zip
|
||||
```
|
||||
|
||||
## 📈 Scalability
|
||||
|
||||
For high-volume applications:
|
||||
|
||||
1. Use **Redis** for caching
|
||||
2. Implement **Elasticsearch** for faster searches
|
||||
3. Use **message queues** (RabbitMQ, Azure Service Bus) for async processing
|
||||
4. Partition database by date ranges
|
||||
5. Implement log archival and retention policies
|
||||
Reference in New Issue
Block a user