A high-performance log4net appender that sends log messages directly to Grafana Loki, the horizontally-scalable, highly-available log aggregation system. This library provides seamless integration with Loki's HTTP API, allowing you to centralize your application logs with minimal configuration.
- Features
- Compatibility
- Installation
- Quick Start
- Configuration
- Usage Examples
- Advanced Configuration
- Troubleshooting
- Contributing
- License
- β Native Loki Integration - Uses the latest Loki HTTP API POST /loki/api/v1/push
- β JSON Formatting - Structured logging with JSON message format
- β High Performance - Configurable buffering to minimize network overhead
- β Secure Communication - Built-in support for Basic Authentication
- β GZip Compression - Reduce bandwidth usage with automatic compression
- β SSL/TLS Support - Option to trust self-signed certificates for development
- β Global Labels - Add custom labels (environment, application name, etc.) to all log streams
- β
Dynamic Labels - Declare any number of extra custom labels directly from your
log4net.config - β Cross-Platform - Supports .NET Framework 4.6.2+, .NET Core, and .NET 5+
| Platform | Version | Status |
|---|---|---|
| .NET Framework | 4.6.2 - 4.8 | β Supported |
| .NET Standard | 2.0+ | β Supported |
| .NET Core | 2.0+ | β Supported |
| .NET | 5.0+ | β Supported |
Dependencies:
- log4net β₯ 3.3.0
- Newtonsoft.Json β₯ 13.0.3
Install-Package Log4Net.Appender.Grafana.Lokidotnet add package Log4Net.Appender.Grafana.Loki<PackageReference Include="Log4Net.Appender.Grafana.Loki" Version="1.0.0" />NuGet Package: Log4Net.Appender.Grafana.Loki
Add the Loki appender to your log4net.config or app.config:
<log4net>
<appender name="loki" type="Log4Net.Appender.Loki.LokiAppender, Log4Net.Appender.Grafana.Loki">
<ServiceUrl value="http://localhost:3100" />
<Application value="MyApp" />
<Environment value="Production" />
</appender>
<root>
<level value="INFO" />
<appender-ref ref="loki" />
</root>
</log4net>using log4net;
public class Program
{
private static readonly ILog log = LogManager.GetLogger(typeof(Program));
static void Main(string[] args)
{
log4net.Config.XmlConfigurator.Configure();
log.Info("Application started");
log.Debug("Debug message");
log.Warn("Warning message");
log.Error("Error message");
log.Info("Application finished");
}
}Your logs will appear in Grafana Loki with labels:
application="MyApp"environment="Production"level="INFO"(or DEBUG, WARN, ERROR, etc.)
| Property | Type | Default | Description |
|---|---|---|---|
ServiceUrl |
string | Required | Loki server URL (e.g., http://localhost:3100) |
Application |
string | null |
Global label for application name |
Environment |
string | null |
Global label for environment (Dev, Staging, Production) |
Label |
LokiLabel | null |
Extra custom label added to all log streams (repeatable) |
BufferSize |
int | 512 |
Number of log entries to buffer before sending |
<log4net>
<appender name="loki" type="Log4Net.Appender.Loki.LokiAppender, Log4Net.Appender.Grafana.Loki">
<!-- Required -->
<ServiceUrl value="http://localhost:3100" />
<!-- Global Labels -->
<Application value="MyWebApp" />
<Environment value="Production" />
<!-- Extra Custom Labels (repeatable) -->
<Label>
<Key value="Team" />
<Value value="Backend" />
</Label>
<Label>
<Key value="Region" />
<Value value="eu-west-1" />
</Label>
<!-- Performance -->
<BufferSize value="100" />
<!-- Authentication (if required) -->
<BasicAuthUserName value="admin" />
<BasicAuthPassword value="secret" />
<!-- Compression -->
<GZipCompression value="true" />
<!-- SSL/TLS -->
<TrustSelfSignedCerts value="false" />
</appender>
<root>
<level value="ALL" />
<appender-ref ref="loki" />
</root>
</log4net>The base URL of your Loki instance.
<ServiceUrl value="http://localhost:3100" />
<ServiceUrl value="https://loki.example.com" />Adds a custom application label to all log streams.
<Application value="MyWebService" />Adds a custom environment label to all log streams.
<Environment value="Development" />
<Environment value="Staging" />
<Environment value="Production" />Adds an extra custom label to all log streams. Can be specified multiple times to add as many labels as needed. Each <Label> element requires a Key and a Value child element.
<Label>
<Key value="Team" />
<Value value="Backend" />
</Label>
<Label>
<Key value="Region" />
<Value value="eu-west-1" />
</Label>All configured labels (including Application, Environment, and any Label entries) will appear on every log stream sent to Loki, making them available for filtering in Grafana.
Number of log entries to accumulate before sending to Loki. Higher values reduce network calls but increase memory usage.
<BufferSize value="512" /> <!-- Default -->
<BufferSize value="100" /> <!-- Lower latency -->
<BufferSize value="1000" /> <!-- Higher throughput -->Credentials for Basic Authentication if your Loki instance requires it.
<BasicAuthUserName value="admin" />
<BasicAuthPassword value="mySecurePassword" />Enable GZip compression to reduce network bandwidth.
<GZipCompression value="true" /> <!-- Recommended for production -->
<GZipCompression value="false" /> <!-- Default -->Allow connections to Loki instances with self-signed SSL certificates. Use only in development!
<TrustSelfSignedCerts value="false" /> <!-- Default - Recommended -->
<TrustSelfSignedCerts value="true" /> <!-- Development only -->See the complete example in Example/ folder.
using System;
using log4net;
namespace ConsoleApp
{
class Program
{
private static readonly ILog log = LogManager.GetLogger(typeof(Program));
static void Main(string[] args)
{
log4net.Config.XmlConfigurator.Configure(new System.IO.FileInfo("log4net.config"));
log.Info("Starting application");
try
{
// Your application logic
DoWork();
}
catch (Exception ex)
{
log.Error("Application error", ex);
}
log.Info("Application completed");
}
static void DoWork()
{
log.Debug("Doing work...");
// Work logic here
}
}
}See the complete example in Example.NetCore/ folder.
using log4net;
using log4net.Config;
using System.IO;
using System.Reflection;
namespace NetCoreApp
{
class Program
{
private static readonly ILog log = LogManager.GetLogger(typeof(Program));
static void Main(string[] args)
{
var logRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
XmlConfigurator.Configure(logRepository, new FileInfo("log4net.config"));
log.Info("Application started");
log.Warn("This is a warning");
log.Error("This is an error");
log.Info("Application finished");
}
}
}<!-- Web.config or app.config -->
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="loki" type="Log4Net.Appender.Loki.LokiAppender, Log4Net.Appender.Grafana.Loki">
<ServiceUrl value="https://loki.mycompany.com" />
<Application value="MyWebApp" />
<Environment value="Production" />
<BufferSize value="50" />
<GZipCompression value="true" />
</appender>
<root>
<level value="INFO" />
<appender-ref ref="loki" />
</root>
</log4net>
</configuration>// Global.asax.cs
protected void Application_Start()
{
log4net.Config.XmlConfigurator.Configure();
}You can combine the Loki appender with other appenders:
<log4net>
<!-- Loki Appender -->
<appender name="loki" type="Log4Net.Appender.Loki.LokiAppender, Log4Net.Appender.Grafana.Loki">
<ServiceUrl value="http://localhost:3100" />
<Application value="MyApp" />
</appender>
<!-- Console Appender -->
<appender name="console" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
<!-- File Appender -->
<appender name="file" type="log4net.Appender.RollingFileAppender">
<file value="logs/app.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="5" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="loki" />
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
</log4net>// Configure based on environment
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
var configFile = new FileInfo($"log4net.{environment}.config");
if (!configFile.Exists)
{
configFile = new FileInfo("log4net.config");
}
XmlConfigurator.Configure(logRepository, configFile);-
Check Loki URL: Ensure
ServiceUrlis correct and Loki is runningcurl http://localhost:3100/ready
-
Check network connectivity: Verify firewall rules and network access
-
Enable log4net internal debugging:
<configuration> <appSettings> <add key="log4net.Internal.Debug" value="true"/> </appSettings> </configuration>
-
Check buffer size: If
BufferSizeis large, logs may be delayed. Try a smaller value like10for testing.
If you see HTTP 401 errors:
- Verify
BasicAuthUserNameandBasicAuthPasswordare correct - Check if your Loki instance requires authentication
For self-signed certificates in development only:
<TrustSelfSignedCerts value="true" />TrustSelfSignedCerts="true" in production!
- Increase
BufferSizeto reduce network calls - Enable
GZipCompressionto reduce bandwidth - Consider using async appender wrapper if available
Contributions are welcome! Please feel free to submit a Pull Request.
-
Clone the repository:
git clone https://github.com/gabrielcerutti/log4net.Appender.Loki.git cd log4net.Appender.Loki -
Build the solution:
dotnet build
-
Run tests:
dotnet test
- Follow existing code style
- Add unit tests for new features
- Update documentation for any configuration changes
- Test against .NET Framework and .NET Core
This project is licensed under the MIT License - see the LICENSE file for details.
- Original implementation by Anas El Hajjaji
- Maintained by Gabriel Cerutti
- Built with log4net
- Integrates with Grafana Loki
β If you find this project useful, please consider giving it a star on GitHub! β