Serilog.Sinks.Loki.YetAnother
3.0.3-alpha
See the version list below for details.
dotnet add package Serilog.Sinks.Loki.YetAnother --version 3.0.3-alpha
NuGet\Install-Package Serilog.Sinks.Loki.YetAnother -Version 3.0.3-alpha
<PackageReference Include="Serilog.Sinks.Loki.YetAnother" Version="3.0.3-alpha" />
paket add Serilog.Sinks.Loki.YetAnother --version 3.0.3-alpha
#r "nuget: Serilog.Sinks.Loki.YetAnother, 3.0.3-alpha"
// Install Serilog.Sinks.Loki.YetAnother as a Cake Addin #addin nuget:?package=Serilog.Sinks.Loki.YetAnother&version=3.0.3-alpha&prerelease // Install Serilog.Sinks.Loki.YetAnother as a Cake Tool #tool nuget:?package=Serilog.Sinks.Loki.YetAnother&version=3.0.3-alpha&prerelease
Serilog.Sinks.Loki.YetAnother
Installation
dotnet add package Serilog.Sinks.Loki.YetAnother
Features
- netstandard2.0 support (sinse v3.0.0)
- Batching support
- trace and span id support
- customizable Exception formatting
- Serilog.Settings.Configuration integration
Example
Log.Logger = new LoggerConfiguration()
.WriteTo.Loki(new LokiSinkConfigurations()
{
Credentials = new LokiCredentials("< login here >", "< password here >"),
Url = new Uri("< uri to loki server here >"),
HandleLogLevelAsLabel = true, // adds Serilog.Events.LogEvent.Level as label (default is true)
PropertiesAsLabels = ["userId"], // adds Serilog.Events.LogEvent.Properties as labels (default is empty)
Labels = //global labels, will be added to each loki log message (default is empty)
[
new LokiLabel("app", "loki"),
]
},
batchSizeLimit: 1000, //The maximum number of events to include in a single batch (default is 1000)
period: TimeSpan.FromMilliseconds(2000), // period between sending batches to loki (default is 2000ms)
queueLimit: 100000, //Maximum number of events to hold in the sink's internal queue, or null for an unbounded queue
httpClient: null) // custom HttpClient instance, (can be used to set proxy, compression etc)
.CreateLogger();
Using Serilog.Settings.Configurations
appsettings.json
{
"Serilog": {
"Using": [ "Serilog.Sinks.Loki" ],
"WriteTo": [
{
"Name": "Loki",
"Args": {
"configurations": {
"Url": "https://logs-prod42.grafana.net",
"Labels": [
{
"key": "app",
"value": "myapp"
}
],
"Credentials": {
"Password": "password",
"Username": "username"
},
"PropertiesAsLabels": [
"app",
"environment"
],
"HandleLogLevelAsLabel": false,
"EnrichTraceId": true, //enrich log with TraceId
"EnrichSpanId": true //enrich log with SpanId
},
"batchSizeLimit": 999
}
}
]
}
}
Program.cs
using Microsoft.Extensions.Configuration;
using Serilog;
var builder = new ConfigurationBuilder()
.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json"))
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder)
.CreateLogger();
Benchmarks
BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4602/23H2/2023Update/SunValley3)
Intel Core i5-14600K, 1 CPU, 20 logical and 14 physical cores
[Host] : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256
ShortRun-.NET 9.0 : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2
ShortRun-.NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256
IterationCount=3 LaunchCount=1 WarmupCount=3
Method | Runtime | Count | Mean | Ratio | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio |
---|---|---|---|---|---|---|---|---|---|
Serilog_Sinks_Grafana_Loki | .NET 9.0 | 50 | 2,053,614.2 us | 0.995 | - | - | - | 16774.91 KB | 19.11 |
Empty | .NET 9.0 | 50 | 246.1 us | 0.000 | 55.1758 | 1.7090 | - | 678.69 KB | 0.77 |
YetAnotherLoki | .NET 9.0 | 50 | 2,063,304.4 us | 1.000 | - | - | - | 877.98 KB | 1.00 |
Serilog_Sinks_Grafana_Loki | .NF 4.8.1 | 50 | 22,956.5 us | 1.96 | 2750.0000 | 1750.0000 | 781.2500 | 21138.92 KB | 4.47 |
Empty | .NF 4.8.1 | 50 | 587.0 us | 0.05 | 110.3516 | 3.9063 | 0.9766 | 682.4 KB | 0.14 |
YetAnotherLoki | .NF 4.8.1 | 50 | 11,752.9 us | 1.00 | 718.7500 | 640.6250 | 515.6250 | 4728.41 KB | 1.00 |
Serilog_Sinks_Grafana_Loki | .NET 9.0 | 100 | 2,059,045.6 us | 1.009 | 1000.0000 | - | - | 33492.23 KB | 19.73 |
Empty | .NET 9.0 | 100 | 492.2 us | 0.000 | 109.3750 | - | - | 1349.08 KB | 0.79 |
YetAnotherLoki | .NET 9.0 | 100 | 2,040,266.7 us | 1.000 | - | - | - | 1697.38 KB | 1.00 |
Serilog_Sinks_Grafana_Loki | .NF 4.8.1 | 100 | 36,404.6 us | 1.43 | 4571.4286 | 1571.4286 | 1000.0000 | 44212.28 KB | 3.83 |
Empty | .NF 4.8.1 | 100 | 1,183.1 us | 0.05 | 218.7500 | 7.8125 | 1.9531 | 1354.86 KB | 0.12 |
YetAnotherLoki | .NF 4.8.1 | 100 | 25,843.7 us | 1.02 | 1200.0000 | 1066.6667 | 800.0000 | 11548.38 KB | 1.00 |
Serilog_Sinks_Grafana_Loki | .NET 9.0 | 1000 | 2,296,610.2 us | 1.059 | 18000.0000 | 10000.0000 | 3000.0000 | 350725.77 KB | 21.40 |
Empty | .NET 9.0 | 1000 | 4,877.4 us | 0.002 | 1093.7500 | 31.2500 | - | 13415.25 KB | 0.82 |
YetAnotherLoki | .NET 9.0 | 1000 | 2,169,810.6 us | 1.000 | 1000.0000 | - | - | 16389.55 KB | 1.00 |
Serilog_Sinks_Grafana_Loki | .NF 4.8.1 | 1000 | 418,773.4 us | 1.28 | 46000.0000 | 16000.0000 | 8000.0000 | 488816.86 KB | 3.38 |
Empty | .NF 4.8.1 | 1000 | 11,611.0 us | 0.04 | 2187.5000 | 46.8750 | 15.6250 | 13458 KB | 0.09 |
YetAnotherLoki | .NF 4.8.1 | 1000 | 327,267.6 us | 1.00 | 4000.0000 | 2000.0000 | 1000.0000 | 144823.27 KB | 1.00 |
Serilog_Sinks_Grafana_Loki | .NET 9.0 | 2000 | 2,511,604.7 us | 1.092 | 37000.0000 | 23000.0000 | 6000.0000 | 701410.65 KB | 21.44 |
Empty | .NET 9.0 | 2000 | 9,679.5 us | 0.004 | 2187.5000 | 46.8750 | - | 26822.43 KB | 0.82 |
YetAnotherLoki | .NET 9.0 | 2000 | 2,299,254.5 us | 1.000 | 2000.0000 | 1000.0000 | - | 32714.22 KB | 1.00 |
Serilog_Sinks_Grafana_Loki | .NF 4.8.1 | 2000 | 764,830.3 us | 1.21 | 85000.0000 | 28000.0000 | 10000.0000 | 977264.74 KB | 3.39 |
Empty | .NF 4.8.1 | 2000 | 23,820.7 us | 0.04 | 4375.0000 | 62.5000 | 31.2500 | 26903.93 KB | 0.09 |
YetAnotherLoki | .NF 4.8.1 | 2000 | 636,940.6 us | 1.01 | 11000.0000 | 7000.0000 | 5000.0000 | 288599.45 KB | 1.00 |
TODO
- improve documentation
- add more examples
- add more tests
I know there are already a few Serilog Loki sinks out there, why another one?
Widly used loki sinks are good and work as expected. But if app produces a lot of logs, in the future this might allocate a lot of objects.
This sink is designed to use less memory and allocate less objects.
The main idea is to use Utf8JsonWriter
to write logs directly to the stream.
Moreover, to decrease GC pressure, and memory allocation, it uses custom TextWriter, that writes message directly to the ut8 stream, instead of usual way - StringWriter.
This approach allows to decrease number of allocations and its avg size, and decrease number of GC cycles as well.
As the result, you can send more logs with less drawbacks.
Inspiration and Credits
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.8.1
- Microsoft.Bcl.HashCode (>= 6.0.0)
- Serilog (>= 4.1.0)
- System.Net.Http (>= 4.3.4)
- System.Text.Json (>= 9.0.0)
- System.Text.RegularExpressions (>= 4.3.1)
-
.NETStandard 2.0
- Microsoft.Bcl.HashCode (>= 6.0.0)
- Serilog (>= 4.1.0)
- System.Net.Http (>= 4.3.4)
- System.Text.Json (>= 9.0.0)
- System.Text.RegularExpressions (>= 4.3.1)
-
net8.0
- Serilog (>= 4.1.0)
-
net9.0
- Serilog (>= 4.1.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
3.0.5-alpha | 77 | 1/25/2025 |
3.0.3-alpha | 86 | 1/11/2025 |
2.0.2 | 308 | 12/4/2024 |
1.3.4-v1-0003-0000 | 119 | 4/28/2024 |
1.2.9 | 1,369 | 2/8/2024 |
1.2.4 | 127 | 2/8/2024 |
1.1.4 | 155 | 1/20/2024 |