Context

Unfortunately, there is no easy way to create a fully qualified XML file. The problem is that a rolling file appender writes its entries sequentially. As we all know, an XML file has to start with opening a root element and end with closing the root element.

Of course, we can always provide some kind of workaround.

Log4netXmlLayout

As I mentioned before, we’re not going to write a custom appender. We are going to use the RollingFileAppender with a custom layout pattern. Because the RollingFileAppender writes its entries sequentially, we only need to format the entry so it represents XML.

Luckily, we don’t have to write it all from scratch. Log4net provides a set of layouts, including the XmlLayoutBase.

public class Log4netXmlLayout : XmlLayoutBase
{
	protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
    {
		writer.WriteStartElement("LogEntry");

		writer.WriteStartElement("Level");
		writer.WriteString(loggingEvent.Level.DisplayName);
		writer.WriteEndElement();

        writer.WriteStartElement("Message");
        writer.WriteString(loggingEvent.RenderedMessage);
        writer.WriteEndElement();

        writer.WriteStartElement("Details");
        if (loggingEvent.ExceptionObject != null)
			writer.WriteString(loggingEvent.ExceptionObject.ToString());
		writer.WriteEndElement();

        writer.WriteStartElement("StackTrace");
        if (loggingEvent.ExceptionObject != null)
			writer.WriteString(string.IsNullOrEmpty(loggingEvent.ExceptionObject.StackTrace) ? string.Empty : loggingEvent.ExceptionObject.StackTrace);
        writer.WriteEndElement();

        writer.WriteStartElement("TimeStamp");
        writer.WriteString(loggingEvent.TimeStamp.ToString("dd/MM/yyyy HH:mm:ss"));
        writer.WriteEndElement();

        writer.WriteEndElement();
	}
}

The log4net configuration will look like:

<configuration>
	<log4net debug="false">
		<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
			<file value="Logs\log" />
			<staticLogFileName value="false"/>
			<appendToFile value="true" />
			<rollingStyle value="Date" />
			<datePattern value=" yyyy-MM-dd&quot;.xml&quot;"/>
			<layout type="Be.MikeBevers.Logging.Log4netXmlLayout">
			</layout>
		</appender>
		<root>
			<level value="INFO" />
			<appender-ref ref="RollingLogFileAppender" />
		</root>
	</log4net>
</configuration>

 

Displaying the log

Our log file will not contain a root element. You’ll notice Internet Explorer will give an error like: “The XML page cannot be displayed”.

You can always create some kind of “logviewer” or “logparser” that adds the root element. Something like:

public XmlDocument ParseLogFile(string logFilePath)
{
	string xmlData = "<LogEntries>" + File.ReadAllText(logFilePath) + "</LogEntries>";
	MemoryStream ms = new MemoryStream(new UTF8Encoding().GetBytes(xmlData));
	XmlDocument xmlDocument = new XmlDocument();
	xmlDocument.Load(ms);
	ms.Close();
	return xmlDocument;
}

 

Sources and Example

Download the logging framework sources with example.