log4net: configuration, rolling files, and maintaining .NET applications

Configure log4net in C# or XML, preserve exception and request context, avoid unsafe multiprocess rotation, and collect existing .NET application logs.

Best practices
Illustration of application logs moving through a collection pipeline

log4net is an Apache logging library for .NET, originally based on log4j. For an application that already uses it, the immediate task is often to make configuration, rotation, and collection dependable. Replacing the library is a separate decision that should follow a concrete need, such as a different logging abstraction or structured message templates.

The model has three main parts: a logger creates an event, an appender writes it to a destination, and a layout formats it. Named loggers belong to a hierarchy that controls inherited levels and appenders. This guide covers code and XML configuration, context, and centralized collection. The examples are based on Apache documentation; no .NET runtime or Guance ingestion test was performed for this article.

Configure console logging in code

Install the log4net package in a .NET console project, then use the following Program.cs:

using System;
using log4net;
using log4net.Appender;
using log4net.Config;
using log4net.Layout;
using log4net.Repository.Hierarchy;

var repository = (Hierarchy)LogManager.GetRepository(typeof(Program).Assembly);
var layout = new PatternLayout(
    "%date [%thread] %-5level %logger - %message%newline%exception");
layout.ActivateOptions();

var consoleAppender = new ConsoleAppender { Layout = layout };
consoleAppender.ActivateOptions();
repository.Root.Level = log4net.Core.Level.Info;
BasicConfigurator.Configure(repository, consoleAppender);

var logger = LogManager.GetLogger(typeof(Program));
logger.Debug("Debug message"); // Suppressed by the INFO threshold.
logger.Info("Application started");
logger.Warn("Optional configuration is missing");
try
{
    throw new InvalidOperationException("Example database connection failure");
}
catch (Exception ex)
{
    logger.Error("Database connection failed", ex);
}
logger.Fatal("Example fatal-severity event");

Pass the appender directly to BasicConfigurator.Configure. Adding it to the root and then invoking a separate default configuration can attach an additional console appender and duplicate output. The example explicitly activates the layout and appender before use. Apache configuration guide

The familiar convenience methods run from Debug through Info, Warn, Error, and Fatal. Fatal is a severity label in log4net; it does not terminate the process. Passing a caught exception as the second argument to Error preserves its exception details for the layout.

Load an XML configuration explicitly

For file output, save this as log4net.config and copy it to the application's output directory:

<log4net>
  <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
    <file value="logs/app.log" />
    <appendToFile value="true" />
    <rollingStyle value="Size" />
    <maxSizeRollBackups value="10" />
    <maximumFileSize value="100MB" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger - %message%newline%exception" />
    </layout>
  </appender>
  <root>
    <level value="INFO" />
    <appender-ref ref="RollingFile" />
  </root>
</log4net>

Use this initialization instead of the console configuration above:

using System;
using System.IO;
using log4net;
using log4net.Config;

XmlConfigurator.Configure(
    LogManager.GetRepository(typeof(Program).Assembly),
    new FileInfo(Path.Combine(AppContext.BaseDirectory, "log4net.config")));

The explicit path makes loading independent of the process's working directory. A parameterless Configure() reads the appropriate application configuration mechanism; it does not discover an arbitrary standalone file by name. An assembly attribute is another option, but its file and initialization behavior must match the application.

The rolling appender also supports date-based and combined rotation. In this size-based example, ten backups are retained in addition to the active file; the threshold is a rotation trigger, not an exact hard limit on each write or total disk use. Do not independently rotate the same files with another mechanism. RollingFileAppender documentation

Read and extend a PatternLayout

Pattern Output
%date Timestamp; %date{ISO8601} selects the named format
%-5level Severity, left-aligned in a field at least five characters wide
%logger Logger name
%message Event message
%exception Attached exception details
%property{key} A context property

An ISO8601-named layout does not by itself establish a timezone policy for your entire pipeline. Inspect the actual timestamp and tell the parser which timezone applies. Multiline exceptions also need a collection rule that keeps the stack with its original event.

Add request context without leaking it to later work

Older applications may use MDC APIs. For logical execution context, use the supported LogicalThreadContext properties and restore the prior value when the scope ends. For example, inside code with an existing logger:

var previousUser = log4net.LogicalThreadContext.Properties["user"];
try
{
    log4net.LogicalThreadContext.Properties["user"] = "customer-42";
    logger.Info("Order submitted");
}
finally
{
    if (previousUser is null)
        log4net.LogicalThreadContext.Properties.Remove("user");
    else
        log4net.LogicalThreadContext.Properties["user"] = previousUser;
}

Add %property{user} to the layout to render it. Follow the same scope discipline for a real trace_id obtained from tracing instrumentation. A request ID created independently is not automatically a distributed trace ID. Test propagation across the asynchronous boundaries used by the application.

PatternLayout produces formatted text. A JSON layout or extension can be appropriate when structured output is needed, but check its maintenance, target frameworks, and compatibility with your log4net version. Do not assemble JSON by inserting unescaped messages into a pattern. Existing text can also be parsed reliably; a format migration is not a prerequisite for centralized collection.

Decide whether a library migration is worthwhile

log4net has configurable appenders, hierarchy-based filtering, and an established configuration model. Its traditional formatted-message API differs from the structured message-template workflow offered by libraries such as Serilog. Choose based on the behavior required by the application, supported runtimes, and dependency maintenance.

Avoid treating either project age or a general activity claim as a security assessment. Check the exact installed release and applicable Apache Logging security advisories. Continuing to use a maintained dependency can be a reasonable engineering choice; no library is universally risk-free.

For gradual migration, new code can depend on Microsoft.Extensions.Logging.ILogger while an appropriate provider or adapter handles the output. Existing log4net code can remain during the transition. Preserve field names, severity mappings, exception details, and collector rules. If the two implementations write files, give each a distinct destination rather than making unrelated appenders share one rotating file.

Collect log4net records in Guance

Keep the current PatternLayout when it meets the requirement, or deliberately adopt a compatible JSON layout. In either case, emit the event time, severity, logger, message, exception, and required context to files or the console.

Configure logfiles, source, and service using DataKit file collection. Use Pipeline parsing to extract text fields or parse JSON. Normalize severity to status, map the timestamp to event time, and extract an actual trace identifier into the expected field.

In the log explorer, filter by service and inspect Error and Fatal examples. Confirm the entire exception is present before creating log detection rules. Where retention requirements differ, evaluate multiple indexes for debug and audit records, and verify the selected index and retention policy for each sample.

Common questions

Is log4net still safe to use?

Assess the installed version, supported runtime, configuration, and applicable advisories. A blanket statement that an existing system has no known risk is not a substitute for that check. Plan a migration when its operational or development benefits justify the work.

Why is nothing written?

Check that initialization runs, the configuration file is deployed at the expected path, and logger and appender thresholds permit the event. Also check destination permissions. Enable log4net.Util.LogLog.InternalDebugging = true early during diagnosis and inspect configuration messages. Logging failures may otherwise be easy to miss.

Can several threads or processes share a file?

log4net supports multithreaded use. Multiple processes are a separate problem: file locking models have trade-offs, and MinimalLock does not coordinate rolling files. Prefer a file per process or a collector-owned output path. This matters for IIS deployments with multiple worker processes. Apache FAQ on locking and rotation

Should an existing application move to Serilog?

Structured message templates, required sinks, and integration with ILogger can justify it. Stage the change and compare emitted records against existing queries before removing log4net. Changing libraries without preserving the operational schema can break alerts that already work.

Related reading

See the English NLog guide and Serilog guide. The .NET logging library comparison and .NET logging introduction are currently in Chinese.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting