Log4net Pattern Layout Customization and Syntax

Published:

Think your logs are fine? Most teams treat log formatting as an afterthought and lose hours hunting missing context.
PatternLayout is log4net’s formatter that turns LoggingEvent fields into the lines you see in files, consoles, or aggregators.
This post walks the pattern syntax, key tokens, XML examples, and how to add custom converters so you can tune logs for debugging, monitoring, and fast root cause analysis.
No code changes or rebuilds required when you tweak the XML layout.

Understanding log4net Pattern Layout Configuration Fundamentals

1SLhHKonS_aITdgSUrkv0w

PatternLayout is log4net’s primary formatting engine. It translates raw LoggingEvent data into readable text lines by scanning a conversionPattern string for percent tokens like %date, %logger, and %message, then swapping each token with the corresponding field from the current LoggingEvent. You can reuse PatternLayout across any Appender without changing code or recompiling.

Conversion patterns get parsed once at configuration time. When log4net initializes, the PatternParser reads your pattern string left to right, identifies tokens, and builds a chain of PatternConverter objects. At runtime, each converter receives the LoggingEvent and returns its piece of the final message. The layout concatenates those pieces into the line that gets written to disk, console, or database.

Token-to-field mapping is direct. LoggingEvent exposes properties like TimeStamp, Level, LoggerName, RenderedMessage, and ExceptionObject. Each built-in token queries one of those fields, formats it if needed (date string, alignment, truncation), and returns the result. Custom tokens require you to subclass PatternConverter, override the Convert method, and wire the new converter into PatternLayout so the parser recognizes your percent-whatever syntax. Older log4net versions (1.2-beta8) required the FinalizeConverter method at line 237 to be virtual before you could override parsing behavior. Newer releases seal PatternParser entirely and rely on a lighter custom-PatternLayout approach.

Common log4net Pattern Layout Tokens and Their Behavior

WTQVqHHDSSuiHPzZfAQ6pQ

Understanding individual tokens matters. Each one controls a different slice of event context. PatternLayout’s power comes from chaining tokens: %date in front, %logger next, %level and %message after that, and %exception at the end. Every token you add shapes the final line, and missing a critical one (like %exception) can hide stack traces during troubleshooting.

Tokens resolve by calling methods on LoggingEvent or pulling from context dictionaries. %date reads TimeStamp and formats it according to your date specifier. %thread queries ThreadName. %logger pulls LoggerName and can truncate it with precision rules. %message calls RenderedMessage, which returns the text you passed to ILog.Info or ILog.Error. %property pulls from GlobalContext, ThreadContext, or LogicalThreadContext using the key in braces. If the key doesn’t exist, the token renders as an empty string.

Common token list:

  • %date – formats the event timestamp using standard .NET date codes or ISO 8601
  • %logger – outputs the fully qualified logger name (namespace.class)
  • %level – shows INFO, WARN, ERROR, or DEBUG
  • %message – the actual log string your code passed
  • %exception – dumps the full stack trace if one exists, nothing if there’s none
  • %thread – outputs the thread name or ID
  • %property{log4net:HostName} – retrieves the machine name via a built-in context property
  • %newline – inserts a platform-appropriate line break

XML Examples for log4net Pattern Layout in Real Projects

Rr4XeDJrTiCSq46X9MjUFw

PatternLayout is declared inside an appender’s tag. You set type=”log4net.Layout.PatternLayout”, then add a child specifying the token sequence. No recompilation needed when you change appenders or adjust the pattern. log4net reloads configuration at runtime, so editing the XML and saving triggers a new parser build without bouncing the application. Every appender type accepts PatternLayout, which is why you can write one pattern and copy it across FileAppender, RollingFileAppender, and ConsoleAppender configurations.

Appender interaction is straightforward. After log4net formats the line using your pattern, it hands the result to the appender’s write method. FileAppender writes to disk. ConsoleAppender writes to stdout or stderr. RollingFileAppender checks size or date triggers, rotates the log if needed, then writes. The layout doesn’t care where the text ends up.

FileAppender with simple conversionPattern:

<appender name="FileAppender" type="log4net.Appender.FileAppender">
  <file value="app.log" />
  <appendToFile value="true" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
  </layout>
</appender>

RollingFileAppender using timestamp specifiers:

<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
  <file value="logs/app.log" />
  <appendToFile value="true" />
  <rollingStyle value="Date" />
  <datePattern value="yyyyMMdd" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date{yyyy-MM-dd HH:mm:ss,fff} [%thread] %-5level %logger{1} - %message%newline%exception" />
  </layout>
</appender>

Configuration editing tips:

  • Use %date{ISO8601} for sortable timestamps in log aggregation tools
  • Add %-5level to left-align the level column for easier visual scanning
  • Include %exception at the end so stack traces don’t break alignment
  • Test pattern changes in a dev environment by watching the output file update in real time

Custom Token Development for log4net Pattern Layout

SJfdfRxqSryqyDANCmT0OQ

You need custom tokens when built-in converters don’t cover your data. Common scenarios include logging a machine identifier for log routing, pulling environment variables, reading custom thread context keys, or modifying message text before it’s written. You implement a PatternConverter subclass, override Convert(LoggingEvent), and return the string you want inserted wherever the token appears in the pattern. Creating your own Custom PatternLayout walks through the override mechanics and PatternParser limitations in older log4net releases.

Older log4net versions (1.2-beta8) prevented subclassing PatternParser because FinalizeConverter wasn’t virtual at line 237. If you needed custom token logic, you had to modify log4net source to make that method overridable, then derive a custom parser. Newer releases seal PatternParser entirely, so the recommended approach is to subclass PatternLayout instead, register your converter in the layout’s constructor, and skip parser inheritance. Both paths get you to the same place. Your percent-token gets recognized and your converter code runs. But the PatternLayout route avoids modifying log4net source.

Implementing the Converter Class

Your converter inherits from log4net.helpers.PatternConverter. You must implement two constructors: a default parameterless constructor calling base(), and a constructor accepting FormattingInfo that also calls base(formattingInfo). The second constructor handles alignment and width modifiers if someone writes %‑10yourtoken. Override the protected method Convert(LoggingEvent loggingEvent), pull whatever data you need from the event or environment, and return a string. Don’t add newlines or other formatting inside the converter. PatternLayout handles all spacing and line breaks via the pattern itself.

Example code shape:

public class MachinePatternConvertor : log4net.helpers.PatternConverter
{
    public MachinePatternConvertor() : base() {}
    public MachinePatternConvertor(FormattingInfo formattingInfo) : base(formattingInfo) {}

    override protected string Convert(LoggingEvent loggingEvent)
    {
        return Environment.MachineName;
    }
}

Required implementation steps:

  • Subclass log4net.helpers.PatternConverter
  • Implement both constructors (parameterless and FormattingInfo)
  • Override Convert(LoggingEvent loggingEvent) and return your custom string
  • Access LoggingEvent properties or environment variables as needed
  • Avoid adding formatting characters (newlines, tabs) in the returned string

Registering Custom log4net Pattern Layout Converters in Configuration

GWSRoAC5ROqFu3462g6nYg

Once your converter class is compiled, you register it in the log4net XML config so PatternLayout recognizes the new token. Add a element under the tag, assign a name attribute (the token identifier without the percent sign), and set type to your converter’s fully qualified type name. Creating your own Pattern Layout Converter for Log4net demonstrates this registration step with a machine-name example. Once registered, every %machine in the conversionPattern is replaced by the converter’s output at runtime.

Context properties offer an alternative to writing a full converter if your custom data is already in GlobalContext, ThreadContext, or LogicalThreadContext. Call GlobalContext.Properties[“key”] = value before logging, then use %property{key} in your pattern. GlobalContext is application-wide and static. ThreadContext is per-thread and cleared when the thread exits. LogicalThreadContext flows across async calls. If your data changes per-request or per-operation, ThreadContext is usually the right choice. Static configuration like environment, hostname, app version? GlobalContext works fine.

Custom converter registration example:

<layout type="log4net.Layout.PatternLayout">
  <converter>
    <name value="machine" />
    <type value="MyNamespace.MachinePatternConvertor, MyAssembly" />
  </converter>
  <conversionPattern value="%date [%machine] %-5level %logger - %message%newline" />
</layout>
Context Type Scope Use Case
GlobalContext Application-wide, static Environment name, machine name, app version
ThreadContext Per-thread, cleared on exit Request ID, user ID, operation context
LogicalThreadContext Flows across async calls Correlation ID in async workflows

Enhanced Formatting Features in log4net Pattern Layout

YE3TZaoSTHiTCd50Wj8loA

Pattern modifiers control alignment, padding, and truncation. A token like %-5level left-aligns the level string in a five-character field. “INFO ” gets a trailing space, “ERROR” gets none. %10logger right-aligns the logger name in ten characters, padding with leading spaces. Precision truncates from the right: %logger{1} keeps only the rightmost segment after the last dot, so Namespace.Class becomes Class. Combining precision with width lets you enforce consistent column widths in console or file output.

Padding and truncation apply to any token. %date{HH:mm:ss} formats time without date, %-20message forces message text into twenty characters. If the rendered string exceeds the width, log4net truncates from the right unless you prefix the width with a dot: %.20message truncates but preserves the last twenty characters instead. Use this to keep the end of a long message visible when the start is less important.

Literal text is inserted by typing it between tokens. Brackets, pipes, and separators show up exactly as written. Escaping works for percent signs: %% renders a single percent. You can build columnar layouts by mixing width modifiers and literals. “%date{HH:mm:ss} | %-5level | %logger{1} | %message” produces pipe-delimited columns that align cleanly in a fixed-width viewer.

Modifier examples:

  • %-5level – left-align level in five characters (“INFO “, “ERROR”)
  • %10logger – right-align logger name in ten characters, pad with leading spaces
  • %logger{1} – truncate logger to the last segment after the final dot (Namespace.Class → Class)
  • %.20message – truncate message to last twenty characters if it exceeds the width

Structured Logging and JSON Output Using log4net Pattern Layout

yUaIz7aVSvGWGjtKnW7f7w

Structured logging typically means writing JSON or key-value pairs instead of free text. PatternLayout can approximate this by wrapping tokens in JSON syntax. Write a conversionPattern like “{ \”timestamp\”: \”%date{ISO8601}\”, \”level\”: \”%level\”, \”logger\”: \”%logger\”, \”message\”: \”%message\” }” and each log line becomes a valid JSON object. This approach works for simple cases but doesn’t escape special characters in message text, so a quote or newline in the message will break JSON parsing downstream.

Custom converters can handle escaping. Override Convert to call JsonConvert.SerializeObject on loggingEvent.RenderedMessage before returning, then wrap the result in your JSON structure. Or use a dedicated structured-logging layout like log4net.Ext.Json if you need full JSON support. PatternLayout’s JSON-style output is quick to set up and works well when message text is controlled and doesn’t contain quotes or backslashes.

JSON-style pattern example:

<conversionPattern value="{ &quot;time&quot;: &quot;%date{ISO8601}&quot;, &quot;level&quot;: &quot;%level&quot;, &quot;logger&quot;: &quot;%logger&quot;, &quot;message&quot;: &quot;%message&quot; }%newline" />

Troubleshooting log4net Pattern Layout Issues

mjKwP0FqRKCYJ2gMO6uIvQ

Most pattern configuration errors result from typos in token names or incorrect XML escaping. If a token doesn’t render, check spelling. %mesage instead of %message produces an empty string or a literal “%mesage” depending on log4net version. If quotes inside the conversionPattern break XML parsing, use " for double quotes and ' for single quotes. Log4net won’t throw an error for unrecognized tokens. It just skips them, so validate your pattern by logging a test message and comparing output to what you expect.

PatternParser is sealed in newer log4net releases, which trips up developers following older tutorials. Log4Net custom LayoutPattern explains that you can’t inherit from PatternParser anymore. Subclass PatternLayout instead and register your converter in the constructor. Trying to override FinalizeConverter in a modern version will fail at compile time because the class is sealed. If you’re stuck on log4net 1.2-beta8, you need to modify PatternParser line 237 to make FinalizeConverter virtual, but upgrading to a later release is a better long-term fix.

Avoid problematic formatting inside converter output. A custom converter that returns “value\n” will insert an extra line break every time the token appears, breaking alignment and doubling line counts. Let PatternLayout handle all spacing via the conversionPattern string. Your converter should return raw text with no newlines, tabs, or trailing spaces. If you need conditional formatting (show exception only if present), use %exception by itself. It renders nothing when LoggingEvent.ExceptionObject is null.

Common fixes:

  • Token doesn’t render – verify spelling and check that the converter is registered if it’s custom
  • XML parse error – escape quotes inside conversionPattern using " and '
  • PatternParser is sealed – subclass PatternLayout instead and register converters in the constructor
  • Extra line breaks in output – remove \n from custom converter output; use %newline in the pattern
  • Stack traces missing – add %exception at the end of the conversionPattern
  • Converter returns empty string – confirm LoggingEvent properties or context keys exist at log time

Final Words

in the action: this post explained what PatternLayout does, how conversionPattern works, and how tokens map to LoggingEvent fields.

We walked token behavior and examples, showed XML for File and RollingFile appenders, covered creating and registering custom converters, and touched on modifiers, JSON-like output, and common troubleshooting gotchas.

Use these tips to shape readable, reliable logs fast. Try small changes to a conversionPattern in your appender; log4net pattern layout lets you iterate without recompiles, so you’ll ship clearer logging with less fuss.

FAQ

Q: What is log4net PatternLayout and what is it used for?

A: The log4net PatternLayout formats log output by mapping tokens to LoggingEvent fields, letting you control message layout for any appender (File, RollingFile, Console, ASP.NET Trace) without recompiling.

Q: How does the conversionPattern work in PatternLayout?

A: The conversionPattern specifies a pattern string where percent tokens (like %date, %thread, %message) are parsed and each token renderer writes corresponding LoggingEvent fields into the final log line.

Q: How do PatternLayout tokens map to LoggingEvent fields?

A: PatternLayout tokens map directly to LoggingEvent properties: renderers pull values from the event (timestamp, logger, thread, message, exception, properties) and format them per conversion modifiers.

Q: Which built-in tokens should I know (examples)?

A: Built-in tokens include %date (timestamp), %thread (thread id), %logger (logger name), %message (log text), %exception (stack trace), %property{key} (context), %-5level (level with alignment), %n (newline).

Q: How do I configure PatternLayout in XML for FileAppender or RollingFileAppender?

A: PatternLayout is configured in XML by declaring with a conversionPattern inside; place that layout in your FileAppender or RollingFileAppender config—no recompilation required.

Q: How do I create a custom PatternConverter token like %machine?

A: Creating a custom token means subclassing log4net.helpers.PatternConverter, implementing constructors, and overriding Convert(LoggingEvent) to write the value (e.g., machine name), then register the token in config.

Q: How do I register custom converters and pass values via ThreadContext or GlobalContext?

A: Custom converters are registered with entries in the config; ThreadContext, GlobalContext, and LogicalThreadContext properties are accessed via %property{name} to pass application values into the layout.

Q: How do formatting modifiers like alignment, padding, and precision work in conversion patterns?

A: Formatting modifiers control alignment and width (e.g., %-5level for left pad), precision truncates fields (logger name precision), and literal text or escaping can be combined to shape output.

Q: Can PatternLayout produce JSON or structured logging output?

A: PatternLayout can approximate structured JSON by combining literal JSON syntax with tokens or using custom converters to emit properly escaped key-value fields for structured output.

Q: What common PatternLayout configuration errors should I watch for and how do I troubleshoot them?

A: Common errors include malformed conversionPattern, unregistered custom converters, forgetting to escape XML, and relying on sealed PatternParser; fix by validating patterns, registering converters, escaping special characters, and using custom layouts when needed.

Q: When should I create a custom PatternLayout or PatternParser subclass instead of just a PatternConverter?

A: Creating a custom PatternLayout or PatternParser is needed when PatternParser is sealed or you require complex parsing behavior beyond single-token converters; otherwise implement PatternConverter for most custom tokens.

curtisharmon
Curtis has spent over two decades guiding hunters and anglers through the backcountry of Montana and Wyoming. His expertise in elk hunting and fly fishing has made him a sought-after voice in the outdoor community. Curtis combines traditional woodsmanship with modern techniques to help readers succeed in the field.

Related articles

Recent articles