log4net object renders | Dru Sellers

:

In my last post on using the built in ‘DebuggerDisplay’ attribute to improve your debugging experience, a few commentors said that they used the ToString() method to control their logging output, and that way could get double duty for debugging and logging. That’s great, but after having worked in banking, I would often want one thing for my logs (especially control over what is logged), one thing for debugging (usually much more detail), and yet another thing for ‘ToString’ to make views easier.

In log4net, there is a concept of IObjectRender that you can setup that will control your log output for you and let you have your ‘ToString’ method back for your own devices.

Lets say we have a basic program that looks like this:

class Program
{
    static void Main(string[] args)
    {
        log4net.Config.BasicConfigurator.Configure();

        var log = LogManager.GetLogger("root");
        var loan = new Loan 
        { 
            AccountNumber = "ABC1234",
            Amount = 100.23
        };
        log.Debug(loan);

        Console.ReadLine();
    }
}

Lets say that the object in question looks like this:

public class Loan
{
    public string AccountNumber { get; set; }
    public double Amount { get; set; }

    public override string ToString()
    {
        return "Loan";
    }
}

So when we call that ‘log.Debug(loan);’ we are going to get

Not super helpful. Of course we can pimp up the ToString, but maybe I want to use that for some other use case. So instead I add an object render to the mix, which looks like:

public class LoanLogRenderer : IObjectRenderer
{
    public void RenderObject(RendererMap rendererMap, 
                             object obj, 
                             TextWriter writer)
    {
        var loan = obj as Loan;
        if (loan == null) return;

        var actNum = loan.AccountNumber;
        var prefixLength = actNum.Length - 4;
        var prefix = new String('*', prefixLength);
        var actNum = actNum.Substring(prefixLength, 4);

        writer.Write("Loan: {0}{1} - {2}",
            prefix, actNum, loan.Amount);
    }
}

then I tweak my program like so: (can also be done in the XML if you prefer)

class Program
{
    static void Main(string[] args)
    {
        log4net.Config.BasicConfigurator.Configure();
        //adding the renderer
        var logRepo = LogManager.GetRepository();
        logRepo.RendererMap.Put(typeof(Loan),
            new LoanLogRenderer());

        var log = LogManager.GetLogger("root");
        var loan = new Loan 
        { 
            AccountNumber = "ABC1234",
            Amount = 100.23
        };
        log.Debug(loan);

        Console.ReadLine();
    }
}

and now when I run the program I get:

Which gives me more information, and just the info I want in the logs. While this example is not awesome in the sense of “look at this cool way I leveraged object renderers”, I hope it has given you some new ideas on how you can improve your logging output, without having to give up your ‘ToString()’. :)

-d

PS: I don’t know how to do this in NLog, if you do please drop a comment.

Further Reading

log4net config docs (ctrl+f for renders)
Producing readable log4net output