Skip to content

Here is a guide on how to send .NET application logs to Coralogix by using log4net framework. The main focus of this guide is to integrate an OpenTelemetry .NET SDK to log4net framework by implementing a simple custom appender.

Package dependencies setup

First step, you need to add these packages to your project, apart from log4net:

  • OpenTelemetry
  • OpenTelemetry.Exporter.OpenTelemetryProtocol
  • Microsoft.Extensions.Logging

Here is how your app's csproj file can look like. Please make sure to update the packages to the latest version if you'll use this example for testing.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="log4net" Version="3.2.0" />
    <PackageReference Include="OpenTelemetry" Version="1.14.0" />
    <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.14.0" />
    <PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.1" />
  </ItemGroup>

  <ItemGroup>
    <None Update="log4net.config">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>

</Project>

Create a log4net appender

If you’re using dependency injection, you need to be able to resolve the dependency from ILoggerFactory, but you may have problems resolving it in the standard way, like having a constructor, or trying <something>.Resolve<ILoggerFactory>(). That way you’ll see quickly that it couldn’t be resolved and that may be null.

So here’s a way to achieve this:

var loggerFactory = LogManager.GetRepository().Properties["ILoggerFactory"] as ILoggerFactory;

The entire class LoggerAppender.cs is shown below:

using log4net;
using log4net.Appender;
using log4net.Core;
using Microsoft.Extensions.Logging;

namespace HelloWorldApp
{
    public class LoggerAppender : AppenderSkeleton
    {
        protected override void Append(LoggingEvent loggingEvent)
        {
            var loggerFactory = LogManager.GetRepository().Properties["ILoggerFactory"] as ILoggerFactory;
            if (loggerFactory == null)
            {
                ErrorHandler.Error("ILoggerFactory is not set in log4net repository properties.");
                return;
            }

            var logger = loggerFactory.CreateLogger(loggingEvent.LoggerName);
            var level = loggingEvent.Level;
            var message = loggingEvent.RenderedMessage;

            // Map log4net levels to Microsoft.Extensions.Logging levels
            if (level >= Level.Error)
            {
                logger.LogError(message);
            }
            else if (level >= Level.Warn)
            {
                logger.LogWarning(message);
            }
            else if (level >= Level.Info)
            {
                logger.LogInformation(message);
            }
            else if (level >= Level.Debug)
            {
                logger.LogDebug(message);
            }
            else
            {
                logger.LogTrace(message);
            }
        }
    }
}

Configure the appender on the configuration file log4net.config (Update the namespace to match your appender class):

<?xml version="1.0" encoding="utf-8" ?>
<log4net>
  <appender name="LoggerAppender" type="HelloWorldApp.LoggerAppender, HelloWorldApp">
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger:%line - %message%newline" />
    </layout>
  </appender>
  <root>
    <level value="ALL" />
    <appender-ref ref="LoggerAppender" />
  </root>
</log4net>

Application example

Let's implement the Hello World application in Program.cs as an example of how to send logs using log4net library:

using System;
using log4net;
using log4net.Config;
using Microsoft.Extensions.Logging;
using OpenTelemetry;
using OpenTelemetry.Logs;

namespace HelloWorldApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // Configure OpenTelemetry
            using var loggerFactory = LoggerFactory.Create(builder =>
            {
                builder.AddOpenTelemetry(logging =>
                {
                    logging.AddOtlpExporter(otlpOptions =>
                    {
                        otlpOptions.Endpoint = new Uri(Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT")
                            ?? "http://localhost:4317");
                    });
                });
            });

            // Store ILoggerFactory in log4net repository properties
            LogManager.GetRepository().Properties["ILoggerFactory"] = loggerFactory;

            // Configure log4net
            XmlConfigurator.Configure(new System.IO.FileInfo("log4net.config"));

            // Create a log4net logger
            var log = LogManager.GetLogger(typeof(Program));

            // Log messages at different levels
            log.Info("Hello, World! This is an info message from log4net.");
            log.Debug("This is a debug message.");
            log.Warn("This is a warning message.");
            log.Error("This is an error message.");

            // Log some structured information
            log.InfoFormat("Application started at {0}", DateTime.Now);
            log.Info("Testing OpenTelemetry log shipment via log4net appender");

            // Keep the application running for a bit to ensure logs are sent
            System.Threading.Thread.Sleep(2000);

            Console.WriteLine("Logging complete. Check your OpenTelemetry collector for the logs.");
        }
    }
}

Logging output

With OpenTelemetry SDK, we can send logs from the application either to local OpenTelemetry Collector setup or directly to Coralogix, using an OpenTelemetry endpoint.

OpenTelemetry Collector

Once we configure the environment variable OTEL_EXPORTER_OTLP_ENDPOINT, setting it to the OpenTelemetry collector's endpoint, we should be able to see application logs coming to the target collector, from where we can also send logs to Coralogix.

Coralogix OpenTelemetry endpoint

Once we configure the environment variables, setting it to the Coralogix OpenTelemetry endpoint with authentication header, application and subsystem name, we should be able to see application logs coming to Coralogix.

For setting up authentication, make sure to use the Send-Your-Data API key.

OTEL_EXPORTER_OTLP_ENDPOINT=https://ingress.:443
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer send_your_data_key" \
OTEL_RESOURCE_ATTRIBUTES="cx.application.name=AppName, cx.subsystem.name=SubName"

Support

Need help? We love to assist our customers, simply reach out via our in-app chat, and we will walk you through, step by step.

Additional resources

Coralogix EndpointsCoralogix Endpoints
Was this helpful?