Added multiple providers

This commit is contained in:
2025-04-26 10:56:32 +02:00
parent 83e2d37f41
commit b50ab3784c
23 changed files with 848 additions and 4 deletions

View File

@@ -3,7 +3,7 @@
<TargetFrameworks>.netstandard2.1; net6.0; net7.0; net8.0; net4.8;</TargetFrameworks> <TargetFrameworks>.netstandard2.1; net6.0; net7.0; net8.0; net4.8;</TargetFrameworks>
<ApplicationIcon>icon.ico</ApplicationIcon> <ApplicationIcon>icon.ico</ApplicationIcon>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<FileVersion>1.4.6</FileVersion> <FileVersion>1.4.7</FileVersion>
<Authors>EonaCat (Jeroen Saey)</Authors> <Authors>EonaCat (Jeroen Saey)</Authors>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Company>EonaCat (Jeroen Saey)</Company> <Company>EonaCat (Jeroen Saey)</Company>
@@ -24,7 +24,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<EVRevisionFormat>1.4.6+{chash:10}.{c:ymd}</EVRevisionFormat> <EVRevisionFormat>1.4.7+{chash:10}.{c:ymd}</EVRevisionFormat>
<EVDefault>true</EVDefault> <EVDefault>true</EVDefault>
<EVInfo>true</EVInfo> <EVInfo>true</EVInfo>
<EVTagMatch>v[0-9]*</EVTagMatch> <EVTagMatch>v[0-9]*</EVTagMatch>

View File

@@ -0,0 +1,187 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.Common;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace EonaCat.Logger.EonaCatCoreLogger
{
public class BatchingDatabaseLogger : ILogger, IDisposable
{
private readonly string _categoryName;
private readonly BatchingDatabaseLoggerOptions _options;
private readonly LoggerScopedContext _context = new();
private readonly BlockingCollection<LogEntry> _queue;
private readonly CancellationTokenSource _cts;
private readonly Task _processingTask;
public bool IncludeCorrelationId { get; set; }
public event EventHandler<Exception> OnException;
public BatchingDatabaseLogger(string categoryName, BatchingDatabaseLoggerOptions options)
{
_categoryName = categoryName;
_options = options;
IncludeCorrelationId = options.IncludeCorrelationId;
_queue = new BlockingCollection<LogEntry>(new ConcurrentQueue<LogEntry>());
_cts = new CancellationTokenSource();
_processingTask = Task.Run(ProcessQueueAsync);
}
public IDisposable BeginScope<TState>(TState state) => null;
public bool IsEnabled(LogLevel logLevel) => _options.IsEnabled;
public void SetContext(string key, string value) => _context.Set(key, value);
public void ClearContext() => _context.Clear();
public string GetContext(string key) => _context.Get(key);
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel) || formatter == null) return;
var message = formatter(state, exception);
var correlationId = IncludeCorrelationId
? _context.Get("CorrelationId") ?? Guid.NewGuid().ToString()
: null;
if (correlationId != null)
{
_context.Set("CorrelationId", correlationId);
}
_queue.Add(new LogEntry
{
Timestamp = DateTime.UtcNow,
LogLevel = logLevel.ToString(),
Category = _categoryName,
Message = message,
Exception = exception?.ToString(),
CorrelationId = correlationId
});
}
private async Task ProcessQueueAsync()
{
var batch = new List<LogEntry>();
var timeoutMs = (int)Math.Min(_options.BatchInterval.TotalMilliseconds, int.MaxValue);
while (!_cts.Token.IsCancellationRequested)
{
try
{
if (_queue.TryTake(out var logEntry, timeoutMs, _cts.Token))
{
batch.Add(logEntry);
// Drain the queue quickly without waiting
while (_queue.TryTake(out var additionalEntry))
{
batch.Add(additionalEntry);
}
}
if (batch.Count > 0)
{
await InsertBatchSafelyAsync(batch);
}
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
}
}
// Final flush outside the loop
if (batch.Count > 0)
{
await InsertBatchSafelyAsync(batch);
}
}
private async Task InsertBatchSafelyAsync(List<LogEntry> batch)
{
try
{
await InsertBatchAsync(batch);
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
}
finally
{
batch.Clear();
}
}
private async Task InsertBatchAsync(List<LogEntry> batch)
{
using var connection = _options.DbProviderFactory.CreateConnection();
if (connection == null)
{
throw new InvalidOperationException("Failed to create database connection.");
}
connection.ConnectionString = _options.ConnectionString;
await connection.OpenAsync();
foreach (var entry in batch)
{
using var command = connection.CreateCommand();
command.CommandText = _options.InsertCommand;
command.Parameters.Clear();
command.Parameters.Add(CreateParameter(command, "Timestamp", entry.Timestamp));
command.Parameters.Add(CreateParameter(command, "LogLevel", entry.LogLevel));
command.Parameters.Add(CreateParameter(command, "Category", entry.Category));
command.Parameters.Add(CreateParameter(command, "Message", entry.Message));
command.Parameters.Add(CreateParameter(command, "Exception", entry.Exception));
command.Parameters.Add(CreateParameter(command, "CorrelationId", entry.CorrelationId));
await command.ExecuteNonQueryAsync();
}
}
private DbParameter CreateParameter(DbCommand command, string name, object value)
{
var param = command.CreateParameter();
param.ParameterName = $"@{name}";
param.Value = value ?? DBNull.Value;
return param;
}
public void Dispose()
{
_cts.Cancel();
_queue.CompleteAdding();
try
{
_processingTask.Wait(_options.ShutdownTimeout);
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
}
}
private class LogEntry
{
public DateTime Timestamp { get; set; }
public string LogLevel { get; set; }
public string Category { get; set; }
public string Message { get; set; }
public string Exception { get; set; }
public string CorrelationId { get; set; }
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Data.Common;
namespace EonaCat.Logger.EonaCatCoreLogger
{
public class BatchingDatabaseLoggerOptions
{
public DbProviderFactory DbProviderFactory { get; set; }
public string ConnectionString { get; set; }
public string InsertCommand { get; set; } =
@"INSERT INTO Logs (Timestamp, LogLevel, Category, Message, Exception, CorrelationId)
VALUES (@Timestamp, @LogLevel, @Category, @Message, @Exception, @CorrelationId)";
public bool IsEnabled { get; set; } = true;
public bool IncludeCorrelationId { get; set; } = true;
public TimeSpan BatchInterval { get; set; } = TimeSpan.FromSeconds(5); // 5 sec batch
public TimeSpan ShutdownTimeout { get; set; } = TimeSpan.FromSeconds(10); // wait on shutdown
}
}

View File

@@ -0,0 +1,24 @@
using Microsoft.Extensions.Logging;
namespace EonaCat.Logger.EonaCatCoreLogger
{
public class BatchingDatabaseLoggerProvider : ILoggerProvider
{
private readonly BatchingDatabaseLoggerOptions _options;
public BatchingDatabaseLoggerProvider(BatchingDatabaseLoggerOptions options)
{
_options = options;
}
public ILogger CreateLogger(string categoryName)
{
return new BatchingDatabaseLogger(categoryName, _options);
}
public void Dispose()
{
// Will be disposed in BatchingDatabaseLogger itself
}
}
}

View File

@@ -0,0 +1,83 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Text;
namespace EonaCat.Logger.EonaCatCoreLogger
{
public class DatabaseLogger : ILogger
{
private readonly string _categoryName;
private readonly DatabaseLoggerOptions _options;
private readonly LoggerScopedContext _context = new();
public bool IncludeCorrelationId { get; set; }
public event EventHandler<Exception> OnException;
public DatabaseLogger(string categoryName, DatabaseLoggerOptions options)
{
_categoryName = categoryName;
_options = options;
IncludeCorrelationId = options.IncludeCorrelationId;
}
public IDisposable BeginScope<TState>(TState state) => null;
public bool IsEnabled(LogLevel logLevel) => _options.IsEnabled;
public void SetContext(string key, string value) => _context.Set(key, value);
public void ClearContext() => _context.Clear();
public string GetContext(string key) => _context.Get(key);
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel) || formatter == null) return;
var message = formatter(state, exception);
if (IncludeCorrelationId)
{
var correlationId = _context.Get("CorrelationId") ?? Guid.NewGuid().ToString();
_context.Set("CorrelationId", correlationId);
}
try
{
using var connection = _options.DbProviderFactory.CreateConnection();
if (connection == null)
{
throw new InvalidOperationException("Failed to create database connection.");
}
connection.ConnectionString = _options.ConnectionString;
connection.Open();
using var command = connection.CreateCommand();
command.CommandText = _options.InsertCommand;
command.Parameters.Clear();
command.Parameters.Add(CreateParameter(command, "Timestamp", DateTime.UtcNow));
command.Parameters.Add(CreateParameter(command, "LogLevel", logLevel.ToString()));
command.Parameters.Add(CreateParameter(command, "Category", _categoryName));
command.Parameters.Add(CreateParameter(command, "Message", message));
command.Parameters.Add(CreateParameter(command, "Exception", exception?.ToString()));
command.Parameters.Add(CreateParameter(command, "CorrelationId", _context.Get("CorrelationId")));
command.ExecuteNonQuery();
}
catch (Exception e)
{
OnException?.Invoke(this, e);
}
}
private DbParameter CreateParameter(DbCommand command, string name, object value)
{
var param = command.CreateParameter();
param.ParameterName = $"@{name}";
param.Value = value ?? DBNull.Value;
return param;
}
}
}

View File

@@ -0,0 +1,17 @@
using System.Data.Common;
namespace EonaCat.Logger.EonaCatCoreLogger
{
public class DatabaseLoggerOptions
{
public DbProviderFactory DbProviderFactory { get; set; }
public string ConnectionString { get; set; }
public string InsertCommand { get; set; } =
@"INSERT INTO Logs (Timestamp, LogLevel, Category, Message, Exception, CorrelationId)
VALUES (@Timestamp, @LogLevel, @Category, @Message, @Exception, @CorrelationId)";
public bool IsEnabled { get; set; } = true;
public bool IncludeCorrelationId { get; set; } = true;
}
}

View File

@@ -0,0 +1,24 @@
using Microsoft.Extensions.Logging;
namespace EonaCat.Logger.EonaCatCoreLogger
{
public class DatabaseLoggerProvider : ILoggerProvider
{
private readonly DatabaseLoggerOptions _options;
public DatabaseLoggerProvider(DatabaseLoggerOptions options)
{
_options = options;
}
public ILogger CreateLogger(string categoryName)
{
return new DatabaseLogger(categoryName, _options);
}
public void Dispose()
{
// Nothing to dispose
}
}
}

View File

@@ -0,0 +1,80 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Text.Json;
namespace EonaCat.Logger.EonaCatCoreLogger
{
public class DiscordLogger : ILogger
{
private readonly string _categoryName;
private readonly DiscordLoggerOptions _options;
private readonly LoggerScopedContext _context = new();
public bool IncludeCorrelationId { get; set; }
public event EventHandler<Exception> OnException;
public DiscordLogger(string categoryName, DiscordLoggerOptions options)
{
_categoryName = categoryName;
_options = options;
IncludeCorrelationId = options.IncludeCorrelationId;
}
public IDisposable BeginScope<TState>(TState state) => null;
public bool IsEnabled(LogLevel logLevel) => _options.IsEnabled;
public void SetContext(string key, string value) => _context.Set(key, value);
public void ClearContext() => _context.Clear();
public string GetContext(string key) => _context.Get(key);
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel) || formatter == null) return;
var message = formatter(state, exception);
if (IncludeCorrelationId)
{
var correlationId = _context.Get("CorrelationId") ?? Guid.NewGuid().ToString();
_context.Set("CorrelationId", correlationId);
}
var logParts = new List<string>
{
$"**[{DateTime.UtcNow:u}]**",
$"**[{logLevel}]**",
$"**[{_categoryName}]**",
$"Message: {message}",
};
foreach (var kvp in _context.GetAll())
{
logParts.Add($"`{kvp.Key}`: {kvp.Value}");
}
if (exception != null)
{
logParts.Add($"Exception: {exception}");
}
string fullMessage = string.Join("\n", logParts);
try
{
using var client = new HttpClient();
var payload = new { content = fullMessage };
var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
client.PostAsync(_options.WebhookUrl, content).Wait();
}
catch (Exception e)
{
OnException?.Invoke(this, e);
}
}
}
}

View File

@@ -0,0 +1,9 @@
namespace EonaCat.Logger.EonaCatCoreLogger
{
public class DiscordLoggerOptions
{
public string WebhookUrl { get; set; }
public bool IsEnabled { get; set; } = true;
public bool IncludeCorrelationId { get; set; } = true;
}
}

View File

@@ -0,0 +1,24 @@
using Microsoft.Extensions.Logging;
namespace EonaCat.Logger.EonaCatCoreLogger
{
public class DiscordLoggerProvider : ILoggerProvider
{
private readonly DiscordLoggerOptions _options;
public DiscordLoggerProvider(DiscordLoggerOptions options)
{
_options = options;
}
public ILogger CreateLogger(string categoryName)
{
return new DiscordLogger(categoryName, _options);
}
public void Dispose()
{
// Nothing to dispose
}
}
}

View File

@@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
namespace EonaCat.Logger.EonaCatCoreLogger.Extensions
{
public static class BatchingDatabaseLoggerFactoryExtensions
{
public static ILoggingBuilder AddEonaCatBatchingDatabaseLogger(this ILoggingBuilder builder, Action<BatchingDatabaseLoggerOptions> configure)
{
var options = new BatchingDatabaseLoggerOptions();
configure?.Invoke(options);
builder.Services.AddSingleton<ILoggerProvider>(new BatchingDatabaseLoggerProvider(options));
return builder;
}
}
}

View File

@@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
namespace EonaCat.Logger.EonaCatCoreLogger.Extensions
{
public static class DatabaseLoggerFactoryExtensions
{
public static ILoggingBuilder AddEonaCatDatabaseLogger(this ILoggingBuilder builder, Action<DatabaseLoggerOptions> configure)
{
var options = new DatabaseLoggerOptions();
configure?.Invoke(options);
builder.Services.AddSingleton<ILoggerProvider>(new DatabaseLoggerProvider(options));
return builder;
}
}
}

View File

@@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
namespace EonaCat.Logger.EonaCatCoreLogger.Extensions
{
public static class DiscordLoggerFactoryExtensions
{
public static ILoggingBuilder AddEonaCatDiscordLogger(this ILoggingBuilder builder, Action<DiscordLoggerOptions> configure)
{
var options = new DiscordLoggerOptions();
configure?.Invoke(options);
builder.Services.AddSingleton<ILoggerProvider>(new DiscordLoggerProvider(options));
return builder;
}
}
}

View File

@@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
namespace EonaCat.Logger.EonaCatCoreLogger.Extensions
{
public static class SlackLoggerFactoryExtensions
{
public static ILoggingBuilder AddEonaCatSlackLogger(this ILoggingBuilder builder, Action<SlackLoggerOptions> configure)
{
var options = new SlackLoggerOptions();
configure?.Invoke(options);
builder.Services.AddSingleton<ILoggerProvider>(new SlackLoggerProvider(options));
return builder;
}
}
}

View File

@@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
namespace EonaCat.Logger.EonaCatCoreLogger.Extensions
{
public static class TelegramLoggerFactoryExtensions
{
public static ILoggingBuilder AddEonaCatTelegramLogger(this ILoggingBuilder builder, Action<TelegramLoggerOptions> configure)
{
var options = new TelegramLoggerOptions();
configure?.Invoke(options);
builder.Services.AddSingleton<ILoggerProvider>(new TelegramLoggerProvider(options));
return builder;
}
}
}

View File

@@ -27,8 +27,6 @@ public class FileLoggerProvider : BatchingLoggerProvider
private readonly int _maxTries; private readonly int _maxTries;
private readonly string _path; private readonly string _path;
private string _logFile; private string _logFile;
private bool _rollingOver;
private int _rollOverCount;
private ConcurrentDictionary<string,string> _buffer = new ConcurrentDictionary<string, string>(); private ConcurrentDictionary<string,string> _buffer = new ConcurrentDictionary<string, string>();
private readonly LoggerScopedContext _context = new(); private readonly LoggerScopedContext _context = new();

View File

@@ -0,0 +1,83 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Text.Json;
// 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.Logger.EonaCatCoreLogger
{
public class SlackLogger : ILogger
{
private readonly string _categoryName;
private readonly SlackLoggerOptions _options;
private readonly LoggerScopedContext _context = new();
public bool IncludeCorrelationId { get; set; }
public event EventHandler<Exception> OnException;
public SlackLogger(string categoryName, SlackLoggerOptions options)
{
_categoryName = categoryName;
_options = options;
IncludeCorrelationId = options.IncludeCorrelationId;
}
public IDisposable BeginScope<TState>(TState state) => null;
public bool IsEnabled(LogLevel logLevel) => _options.IsEnabled;
public void SetContext(string key, string value) => _context.Set(key, value);
public void ClearContext() => _context.Clear();
public string GetContext(string key) => _context.Get(key);
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel) || formatter == null) return;
var message = formatter(state, exception);
if (IncludeCorrelationId)
{
var correlationId = _context.Get("CorrelationId") ?? Guid.NewGuid().ToString();
_context.Set("CorrelationId", correlationId);
}
var logParts = new List<string>
{
$"*[{DateTime.UtcNow:u}]*",
$"*[{logLevel}]*",
$"*[{_categoryName}]*",
$"Message: {message}",
};
foreach (var kvp in _context.GetAll())
{
logParts.Add($"_{kvp.Key}_: {kvp.Value}");
}
if (exception != null)
{
logParts.Add($"Exception: {exception}");
}
string fullMessage = string.Join("\n", logParts);
try
{
using var client = new HttpClient();
var payload = new { text = fullMessage };
var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
client.PostAsync(_options.WebhookUrl, content).Wait();
}
catch (Exception e)
{
OnException?.Invoke(this, e);
}
}
}
}

View File

@@ -0,0 +1,13 @@
namespace EonaCat.Logger.EonaCatCoreLogger
{
// 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 SlackLoggerOptions
{
public string WebhookUrl { get; set; }
public bool IsEnabled { get; set; } = true;
public bool IncludeCorrelationId { get; set; } = true;
}
}

View File

@@ -0,0 +1,27 @@
using Microsoft.Extensions.Logging;
// 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.Logger.EonaCatCoreLogger
{
public class SlackLoggerProvider : ILoggerProvider
{
private readonly SlackLoggerOptions _options;
public SlackLoggerProvider(SlackLoggerOptions options)
{
_options = options;
}
public ILogger CreateLogger(string categoryName)
{
return new SlackLogger(categoryName, _options);
}
public void Dispose()
{
// Nothing to dispose
}
}
}

View File

@@ -0,0 +1,57 @@
using Microsoft.Extensions.Logging;
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
// 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.Logger.EonaCatCoreLogger
{
public class TelegramLogger : ILogger
{
private readonly string _categoryName;
private readonly TelegramLoggerOptions _options;
private readonly HttpClient _httpClient = new HttpClient();
public event EventHandler<Exception> OnException;
public TelegramLogger(string categoryName, TelegramLoggerOptions options)
{
_categoryName = categoryName;
_options = options;
}
public IDisposable BeginScope<TState>(TState state) => null;
public bool IsEnabled(LogLevel logLevel) => _options.IsEnabled;
public async void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel)) return;
var message = $"[{DateTime.UtcNow:u}] [{logLevel}] {_categoryName}: {formatter(state, exception)}";
if (exception != null)
{
message += $"\nException: {exception}";
}
var url = $"https://api.telegram.org/bot{_options.BotToken}/sendMessage";
var payload = new
{
chat_id = _options.ChatId,
text = message
};
var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
try
{
await _httpClient.PostAsync(url, content);
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
}
}
}
}

View File

@@ -0,0 +1,9 @@
namespace EonaCat.Logger.EonaCatCoreLogger
{
public class TelegramLoggerOptions
{
public string BotToken { get; set; }
public string ChatId { get; set; }
public bool IsEnabled { get; set; } = true;
}
}

View File

@@ -0,0 +1,24 @@
using Microsoft.Extensions.Logging;
namespace EonaCat.Logger.EonaCatCoreLogger
{
public class TelegramLoggerProvider : ILoggerProvider
{
private readonly TelegramLoggerOptions _options;
public TelegramLoggerProvider(TelegramLoggerOptions options)
{
_options = options;
}
public ILogger CreateLogger(string categoryName)
{
return new TelegramLogger(categoryName, _options);
}
public void Dispose()
{
// Nothing to dispose
}
}
}

View File

@@ -380,6 +380,81 @@ private void LogHelper_OnException(object sender, ErrorMessage e)
} }
``` ```
Example of Discord Logging:
```csharp
loggingBuilder.AddEonaCatDiscordLogger(options =>
{
options.WebhookUrl = "https://discord.com/api/webhooks/your_webhook_here";
});
```
Example of Slack Logging:
```csharp
loggingBuilder.AddEonaCatSlackLogger(options =>
{
options.WebhookUrl = "https://hooks.slack.com/services/your_webhook_here";
});
```
Example of DatabaseLogging:
Create the table:
```csharp
CREATE TABLE Logs (
Id INT IDENTITY(1,1) PRIMARY KEY, -- SERIAL for PostgreSQL, AUTO_INCREMENT for MySQL
Timestamp DATETIME NOT NULL,
LogLevel NVARCHAR(50),
Category NVARCHAR(255),
Message NVARCHAR(MAX),
Exception NVARCHAR(MAX),
CorrelationId NVARCHAR(255)
);
```
MySQL:
```csharp
using MySql.Data.MySqlClient;
loggingBuilder.AddEonaCatDatabaseLogger(options =>
{
options.DbProviderFactory = MySqlClientFactory.Instance;
options.ConnectionString = "Server=localhost;Database=EonaCatLogs;User=root;Password=yourpassword;";
});
```
SQL Server:
```csharp
using Microsoft.Data.SqlClient;
loggingBuilder.AddEonaCatDatabaseLogger(options =>
{
options.DbProviderFactory = SqlClientFactory.Instance;
options.ConnectionString = "Server=localhost;Database=EonaCatLogs;User Id=sa;Password=yourpassword;";
});
```
PostgreSQL:
```csharp
using Npgsql;
loggingBuilder.AddEonaCatDatabaseLogger(options =>
{
options.DbProviderFactory = NpgsqlFactory.Instance;
options.ConnectionString = "Host=localhost;Database=EonaCatLogs;Username=postgres;Password=yourpassword;";
});
```
Example of batching database logging:
```csharp
loggingBuilder.AddEonaCatBatchingDatabaseLogger(options =>
{
options.DbProviderFactory = MySql.Data.MySqlClient.MySqlClientFactory.Instance;
options.ConnectionString = "Server=localhost;Database=EonaCatLogs;User=root;Password=yourpassword;";
options.BatchInterval = TimeSpan.FromSeconds(10); // Flush every 10 seconds
});
```
Example of adding custom context to the log messages: Example of adding custom context to the log messages:
```csharp ```csharp