Raycoon.Serilog.Sinks.SQLite
1.0.7
See the version list below for details.
dotnet add package Raycoon.Serilog.Sinks.SQLite --version 1.0.7
NuGet\Install-Package Raycoon.Serilog.Sinks.SQLite -Version 1.0.7
<PackageReference Include="Raycoon.Serilog.Sinks.SQLite" Version="1.0.7" />
<PackageVersion Include="Raycoon.Serilog.Sinks.SQLite" Version="1.0.7" />
<PackageReference Include="Raycoon.Serilog.Sinks.SQLite" />
paket add Raycoon.Serilog.Sinks.SQLite --version 1.0.7
#r "nuget: Raycoon.Serilog.Sinks.SQLite, 1.0.7"
#:package Raycoon.Serilog.Sinks.SQLite@1.0.7
#addin nuget:?package=Raycoon.Serilog.Sinks.SQLite&version=1.0.7
#tool nuget:?package=Raycoon.Serilog.Sinks.SQLite&version=1.0.7
Raycoon.Serilog.Sinks.SQLite
A modern, high-performance Serilog sink for SQLite databases. Developed for .NET 8+ with full AnyCPU support.
Features
- AnyCPU compatible - Uses
Microsoft.Data.Sqlite(no native SQLite required) - .NET 8.0, .NET 9.0 & .NET 10.0 support
- Asynchronous batching - Optimal performance through batch writing
- Automatic retention - By time, count, or database size
- Custom columns - Store structured data in dedicated columns
- WAL mode - Optimized for high write load
- Thread-safe - Fully suitable for parallel logging
- Configurable - Extensive options for every use case
Installation
dotnet add package Raycoon.Serilog.Sinks.SQLite
Quick Start
Basic Usage
using Serilog;
var logger = new LoggerConfiguration()
.WriteTo.SQLite("logs/app.db")
.CreateLogger();
logger.Information("Hello, SQLite!");
logger.Error(new Exception("Oops!"), "An error occurred");
// Important: Dispose the logger at the end
await Log.CloseAndFlushAsync();
Advanced Configuration
using Serilog;
using Serilog.Events;
using Raycoon.Serilog.Sinks.SQLite.Options;
var logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.Enrich.FromLogContext()
.Enrich.WithThreadId()
.WriteTo.SQLite("logs/app.db", options =>
{
// Table name
options.TableName = "ApplicationLogs";
// Retention: Delete logs older than 30 days
options.RetentionPeriod = TimeSpan.FromDays(30);
// Retention: Keep maximum 100,000 entries
options.RetentionCount = 100_000;
// Retention: Database max. 100 MB
options.MaxDatabaseSize = 100 * 1024 * 1024;
// Performance tuning
options.BatchSizeLimit = 200;
options.BatchPeriod = TimeSpan.FromSeconds(1);
options.QueueLimit = 50000;
// SQLite optimizations
options.JournalMode = SQLiteJournalMode.Wal;
options.SynchronousMode = SQLiteSynchronousMode.Normal;
// Store timestamps in UTC
options.StoreTimestampInUtc = true;
// Minimum log level for this sink
options.RestrictedToMinimumLevel = LogEventLevel.Information;
})
.CreateLogger();
Custom Columns
Store structured data in dedicated columns for better queries:
var logger = new LoggerConfiguration()
.WriteTo.SQLite("logs/app.db", options =>
{
options.CustomColumns.Add(new CustomColumn
{
ColumnName = "UserId",
DataType = "TEXT",
PropertyName = "UserId",
CreateIndex = true // Index for fast searches
});
options.CustomColumns.Add(new CustomColumn
{
ColumnName = "RequestId",
DataType = "TEXT",
PropertyName = "RequestId"
});
options.CustomColumns.Add(new CustomColumn
{
ColumnName = "Duration",
DataType = "REAL",
PropertyName = "DurationMs"
});
})
.CreateLogger();
// Usage
logger
.ForContext("UserId", "user123")
.ForContext("RequestId", Guid.NewGuid())
.ForContext("DurationMs", 42.5)
.Information("Request processed");
Error Handling
var logger = new LoggerConfiguration()
.WriteTo.SQLite("logs/app.db", options =>
{
options.OnError = ex =>
{
Console.WriteLine($"SQLite Error: {ex.Message}");
// Or: Use a fallback logger
};
// Throw exception on critical errors
options.ThrowOnError = false; // Default: false
})
.CreateLogger();
JSON Configuration (appsettings.json)
The sink supports full configuration via appsettings.json using Serilog.Settings.Configuration:
dotnet add package Serilog.Settings.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json
Basic JSON Configuration
{
"Serilog": {
"Using": ["Raycoon.Serilog.Sinks.SQLite"],
"MinimumLevel": "Information",
"WriteTo": [
{
"Name": "SQLite",
"Args": {
"databasePath": "logs/app.db"
}
}
]
}
}
Full JSON Configuration
{
"Serilog": {
"Using": ["Raycoon.Serilog.Sinks.SQLite"],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "SQLite",
"Args": {
"databasePath": "logs/app.db",
"tableName": "ApplicationLogs",
"restrictedToMinimumLevel": "Information",
"storeTimestampInUtc": true,
"autoCreateDatabase": true,
"storePropertiesAsJson": true,
"storeExceptionDetails": true,
"maxMessageLength": 10000,
"maxExceptionLength": 20000,
"maxPropertiesLength": 10000,
"batchSizeLimit": 200,
"batchPeriod": "00:00:01",
"queueLimit": 50000,
"retentionPeriod": "30.00:00:00",
"retentionCount": 100000,
"maxDatabaseSize": 104857600,
"cleanupInterval": "01:00:00",
"journalMode": "Wal",
"synchronousMode": "Normal",
"throwOnError": false,
"customColumns": [
{
"columnName": "UserId",
"dataType": "TEXT",
"propertyName": "UserId",
"allowNull": true,
"createIndex": true
},
{
"columnName": "RequestId",
"dataType": "TEXT",
"propertyName": "RequestId",
"allowNull": true,
"createIndex": false
},
{
"columnName": "Duration",
"dataType": "REAL",
"propertyName": "DurationMs",
"allowNull": true,
"createIndex": false
}
]
}
}
],
"Enrich": ["FromLogContext"]
}
}
C# Setup for JSON Configuration
using Microsoft.Extensions.Configuration;
using Serilog;
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
TimeSpan Format in JSON
| Format | Example | Description |
|---|---|---|
hh:mm:ss |
"00:00:02" |
2 seconds |
hh:mm:ss.fff |
"00:00:00.500" |
500 milliseconds |
d.hh:mm:ss |
"7.00:00:00" |
7 days |
d.hh:mm:ss |
"30.00:00:00" |
30 days |
Enum Values in JSON
| Property | Valid Values |
|---|---|
journalMode |
"Delete", "Truncate", "Persist", "Memory", "Wal", "Off" |
synchronousMode |
"Off", "Normal", "Full", "Extra" |
restrictedToMinimumLevel |
"Verbose", "Debug", "Information", "Warning", "Error", "Fatal" |
Limitations
Note: This is a fundamental limitation of the Serilog ecosystem, not specific to this sink. All Serilog sinks face this constraint because
Serilog.Settings.Configurationcan only bind serializable types from JSON.
The following options are not available via JSON configuration:
OnErrorcallback (delegates cannot be serialized)AdditionalConnectionParameters(dictionary binding is complex)
Option 1: Full Programmatic Configuration (Recommended when callbacks are needed)
Use the action-based overload for complete control:
var logger = new LoggerConfiguration()
.WriteTo.SQLite("logs/app.db", options =>
{
// All settings configured programmatically
options.TableName = "ApplicationLogs";
options.RetentionPeriod = TimeSpan.FromDays(30);
options.BatchSizeLimit = 200;
options.JournalMode = SQLiteJournalMode.Wal;
// Callback options (not available via JSON)
options.OnError = ex => Console.WriteLine($"SQLite error: {ex.Message}");
options.AdditionalConnectionParameters["Password"] = "mySecurePassword";
})
.CreateLogger();
Option 2: Hybrid Approach (JSON for other sinks, programmatic for SQLite)
If you need JSON configuration for other parts of your logging pipeline:
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration) // Loads MinimumLevel, other sinks, enrichers
.WriteTo.SQLite("logs/app.db", options =>
{
// SQLite-specific settings with callback support
options.TableName = "ApplicationLogs";
options.OnError = ex => MyErrorHandler(ex);
})
.CreateLogger();
Why can't I just add callbacks to a JSON-configured sink?
Serilog's architecture creates sinks during
ReadFrom.Configuration()and doesn't expose them for modification afterwards. This is by design for thread-safety and immutability.
Database Schema
The sink automatically creates the following table:
| Column | Type | Description |
|---|---|---|
Id |
INTEGER | Primary key (auto-increment) |
Timestamp |
TEXT | ISO 8601 timestamp |
Level |
INTEGER | Log level (0-5) |
LevelName |
TEXT | Log level name |
Message |
TEXT | Rendered message |
MessageTemplate |
TEXT | Original message template |
Exception |
TEXT | Exception details (if present) |
Properties |
TEXT | Properties as JSON |
SourceContext |
TEXT | Logger name / source |
MachineName |
TEXT | Computer name |
ThreadId |
INTEGER | Thread ID |
Plus all configured custom columns.
Querying Logs
-- All errors from the last 24 hours
SELECT * FROM Logs
WHERE Level >= 4
AND Timestamp > datetime('now', '-1 day')
ORDER BY Timestamp DESC;
-- Logs by UserId (if custom column configured)
SELECT * FROM Logs
WHERE UserId = 'user123'
ORDER BY Timestamp DESC
LIMIT 100;
-- Aggregation by level
SELECT LevelName, COUNT(*) as Count
FROM Logs
GROUP BY Level;
-- Search through properties (JSON)
SELECT * FROM Logs
WHERE json_extract(Properties, '$.RequestId') = 'abc123';
Performance Tips
1. Optimize Batch Size
options.BatchSizeLimit = 500; // For high-volume
options.BatchPeriod = TimeSpan.FromMilliseconds(100);
2. Use WAL Mode (Default)
options.JournalMode = SQLiteJournalMode.Wal;
3. Adjust Synchronous Mode
// Faster, but less safe in case of power failure
options.SynchronousMode = SQLiteSynchronousMode.Normal;
// Or for maximum performance (only if data loss is acceptable)
options.SynchronousMode = SQLiteSynchronousMode.Off;
4. Set Queue Limit
// Prevents memory overflow during burst traffic
options.QueueLimit = 100000;
Comparison to Other SQLite Sinks
| Feature | Raycoon.Serilog.Sinks.SQLite | Serilog.Sinks.SQLite |
|---|---|---|
| AnyCPU Support | Yes (Microsoft.Data.Sqlite) | No (System.Data.SQLite) |
| .NET 8/9/10 | Yes | Partial (.NET 7 only) |
| Async Batching | Yes | Yes |
| Retention Policies | Yes (time, count, size) | No |
| Custom Columns | Yes | No |
| WAL Mode | Yes | Yes |
API Reference
SQLiteSinkOptions
Database
| Property | Type | Default | Description |
|---|---|---|---|
DatabasePath |
string | "logs.db" | Path to the SQLite database file |
TableName |
string | "Logs" | Name of the log table |
AutoCreateDatabase |
bool | true | Auto-create database file if not exists |
Logging Behavior
| Property | Type | Default | Description |
|---|---|---|---|
RestrictedToMinimumLevel |
LogEventLevel | Verbose | Minimum log level to capture |
StoreTimestampInUtc |
bool | true | Store timestamps in UTC (false = local time) |
StorePropertiesAsJson |
bool | true | Store log event properties as JSON |
StoreExceptionDetails |
bool | true | Store exception details in separate column |
Data Limits
| Property | Type | Default | Description |
|---|---|---|---|
MaxMessageLength |
int? | null | Max rendered message length (null = unlimited) |
MaxExceptionLength |
int? | null | Max exception text length (null = unlimited) |
MaxPropertiesLength |
int? | null | Max properties JSON length (null = unlimited) |
Batching & Performance
| Property | Type | Default | Description |
|---|---|---|---|
BatchSizeLimit |
int | 100 | Max events per batch write |
BatchPeriod |
TimeSpan | 2s | Interval between batch writes |
QueueLimit |
int? | 10000 | Max events in memory queue (null = unlimited) |
Retention Policy
| Property | Type | Default | Description |
|---|---|---|---|
RetentionPeriod |
TimeSpan? | null | Delete logs older than this (null = disabled) |
RetentionCount |
long? | null | Keep only this many logs (null = disabled) |
MaxDatabaseSize |
long? | null | Max database size in bytes (null = disabled) |
CleanupInterval |
TimeSpan | 1h | Interval for retention cleanup checks |
SQLite Configuration
| Property | Type | Default | Description |
|---|---|---|---|
JournalMode |
SQLiteJournalMode | Wal | SQLite journal mode (Wal recommended) |
SynchronousMode |
SQLiteSynchronousMode | Normal | SQLite synchronous mode |
AdditionalConnectionParameters |
Dictionary | {} | Extra SQLite connection string parameters |
License
Apache 2.0 - See LICENSE for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Data.Sqlite (>= 10.0.2)
- Serilog (>= 4.3.0)
- Serilog.Sinks.PeriodicBatching (>= 5.0.0)
-
net8.0
- Microsoft.Data.Sqlite (>= 10.0.2)
- Serilog (>= 4.3.0)
- Serilog.Sinks.PeriodicBatching (>= 5.0.0)
-
net9.0
- Microsoft.Data.Sqlite (>= 10.0.2)
- Serilog (>= 4.3.0)
- Serilog.Sinks.PeriodicBatching (>= 5.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Add JSON configuration support