views:

340

answers:

2

I wish to include the call stack (e.g. the methods that called me) in a log4net message. Is there a standard way of doing this?

(I know this will be slow, but I only need to do it on some errors)

+4  A: 

Yes - you can get this stack information using the following patterns in a pattern layout:

%type %file %line %method %location %class

See the this documentation on the PatternLayout for more information.

Edit in response to Ian's comment below: I don't think log4net can be configured to write out the whole stack.

You can always fall back on writing it out for yourself, using something like new StackTrace().ToString(), but I'm guessing the reason you ask is you want this to be configurable in the logging configuration.

I'll have a deeper look, but my gut feeling is there is no way to configure this, and that you'd end up having to implement your own Layout class.

Edit++ OK - here is a custom pattern layout class that derives from PatternLayout but adds in a layout %stack.

This code is a bit rough - illustrative only - not production ready! (for example, you may not have security permission to access the stack you are trying to print)

public class CustomPatternLayout : PatternLayout
{
    public CustomPatternLayout()
    {
        this.AddConverter("stack", typeof(StackTraceConverter));
    }
}

public class StackTraceConverter : PatternLayoutConverter
{
    protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
    {
        var stack = new StackTrace();

        var frames = stack.GetFrames();
        for (var i = 0; i < frames.Length; i++ )
        {
            var frame = frames[i];

            // if the stack frame corresponds to still being inside the log4net assembly, skip it.
            if (frame.GetMethod().DeclaringType.Assembly != typeof(LogManager).Assembly)
            {
                writer.WriteLine("{0}.{1} line {2}",
                    frame.GetMethod().DeclaringType.FullName,
                    frame.GetMethod().Name, 
                    frame.GetFileLineNumber());
            }
        }
    }
}

You can then configure this with the following pattern configuration (note %stack at the end of the layout):

  <layout type="ScratchPad.CustomPatternLayout,ScratchPad">
    <conversionPattern value="%date %-5level %message%newline %type %file %line %method %location %class %stack" />
  </layout>
Rob Levine
when I raed that page, I took %location to only meen the fully qualified name of the CALLING method, not the full call stack
Ian Ringrose
I wish to include the callstack in some messages but not others by default. (E.g. a log to Ilog.Error() to include the callstack would be spot on.)
Ian Ringrose
You don't need to use a loop to write out the call stack. Take a look at the documentation on StackFrame.ToString: http://msdn.microsoft.com/en-us/library/system.diagnostics.stackframe.tostring.aspx
CodeMonkeyKing
@CodeMonkeyKing - not sure I follow your suggestion. StackFrame.ToString() only outputs the current frame, not the the whole trace up to the current frame. In any case, as mentioned in the code comment, I still need to loop through all frames in the trace in order to "push past" those frames which are internal to log4net. Have I misunderstood your suggestion?
Rob Levine
+2  A: 

If you are catching exceptions, then just log Exception.ToString(), as that will contain the full stack trace, and will be available as the normal log message.

Si