All your logs are belong to log4net
Heads Up!
This article is several years old now, and much has happened since then, so please keep that in mind while reading it.
History lesson
As you may or may not be aware since Umbraco 4.10 all internal logging by Umbraco is now done by the awesome log4net logging framework. The log4net framework is a port from the equally excellent log4j. I have used log4net in the past on non Umbraco projects and love it, so I was really excited when it was announced that Umbraco would make use of it.
Log4net in Umbraco
Log4net is configured using an XML config file. In Umbraco this file lives in the config directory and is called log4net.config. Out of the box you get one appender already setup for you. This is the AsynchronousLog4NetAppender, this writes all log statements to a log file that lives in App_Data\Logs\UmbracoTraceLog.txt.
Here is where it gets interesting; log4net is a highly customisable logging framework, you can make use of any number of standard out of the box appenders or you can write your own.
Other appenders
I am just going to cover one of the standard appenders - the SMTP appender and how I make use of it regularly in my code.
When I am writing code for macros or for Umbraco events like document publish or Examine GatheringNodeData I will for critical sections always wrap with a try catch block and in the exception block add a log4net statement.
public MyClass{
public void SomeCriticalMethod(){
try{
//some critical code here
}
catch(Exception ex){
LogHelper.Error(typeof(MyClass), "Error exceuting task”, ex);
}
}
}
In my log4net config file I add entry for the SMTP appender which looks something like
<appender name="SmtpAppender" type="log4net.Appender.SmtpAppender">
<to value="jo@site.com" />
<from value="me@site.com" />
<subject value="Info graphic error" />
<smtpHost value="localhost" />
<bufferSize value="512" />
<lossy value="true" />
<evaluator type="log4net.Core.LevelEvaluator">
<threshold value="ERROR"/>
</evaluator>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%newline%date [%thread] %-5level %logger [%property{NDC}] - %message%newline%newline%newline" />
</layout>
</appender>
Additionally I also setup a filter. If this was not done then anytime there is an error I will receive emails it can get a bit spamtastic! I am only interested in receiving emails when my critical section fails. The filter looks like
<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value=" MyClass" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
Now I will only receive emails when my method fails in MyClass class.
You could even set up an Error500 page in your web.config, this page will not be an umbraco page but a stand-alone aspx page (You will need to add it to the reserved urls settings in your web.config so that Umbraco request pipeline does not try to handle it)
<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
<error statusCode="500" redirect="/Error500.aspx" />
</customErrors>
and in the aspx page log any unhandled exceptions,
public partial class Error500 : System.Web.UI.Page
{
private static readonly ILog log = LogManager.GetLogger(typeof(Error500));
protected void Page_Load(object sender, EventArgs e)
{
LogError();
}
private void LogError()
{
Exception objErr = Server.GetLastError().GetBaseException();
log.Error("Error500 page",objErr);
}
}
so the user gets a friendly error message and you get an email telling you its hit the fan, no more nasty YSODs.
Custom appenders - LiveLogger
As well as standard out of the box appenders you can also easily create your own appender and in the LiveLogger project I made use of a SignalR appender for log4net.
using System;
using log4net.Appender;
using log4net.Core;
namespace LiveLogger
{
public class LogAppender : AppenderSkeleton
{
private FixFlags _fixFlags = FixFlags.All;
public Action<LogEntry> MessageLogged;
public static LogAppender Instance { get; private set; }
public LogAppender()
{
Instance = this;
}
virtual public FixFlags Fix
{
get { return _fixFlags; }
set { _fixFlags = value; }
}
override protected void Append(LoggingEvent loggingEvent)
{
// LoggingEvent may be used beyond the lifetime of the Append()
// so we must fix any volatile data in the event
loggingEvent.Fix = Fix;
var formattedEvent = RenderLoggingEvent(loggingEvent);
var handler = MessageLogged;
if (handler != null)
{
handler(new LogEntry(formattedEvent, loggingEvent));
}
}
}
public class LogEntry
{
public string FormattedEvent { get; private set; }
public LoggingEvent LoggingEvent { get; private set; }
public LogEntry(string formttedEvent, LoggingEvent loggingEvent)
{
FormattedEvent = formttedEvent;
LoggingEvent = loggingEvent;
}
}
}
When you install the package it will insert the appender configuration into your log4net config file,
<appender name="LiveLoggerAppender" type="LiveLogger.LogAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level - %message%newline" />
</layout>
</appender>
I find this to be very useful during development. So I will in appropriate places insert logging statements into my code and again make use of try catch blocks and also log exceptions.
Then if something I am working on is failing. I will open livelogger then re run the action and look at the log statements, it is also very useful on a live site and a lot easier than constantly pulling down the log file from the app_data directory.
This is only touching the surface of some of the log4net capabilities; You can really go to town with a wide mixture of appenders and filters to fine tune your logging to a number of different sinks, files, databases, SMTP servers and even custom sinks you write yourself.
Ismail Mayat
Ismail is on Twitter as @ismailmayat