简体   繁体   中英

How to log with a tree like structure

I want to log with log4net (and I am doing this right now) to a file, but with log4net, there are just simple log outputs with a message each line.

Now I want to log to the same log file, but in a tree like structure. For example I've got the following method calls and each method logs its method name (there should be an indentation):

firstMethod().secondMethod().thirdMethod();

Should print this log for example:

2016-07-26 15:44:56,042    > firstMethod
2016-07-26 15:44:56,043         > secondMethod
2016-07-26 15:44:56,044              > thirdMethod
2016-07-26 15:44:56,045              < thirdMethod
2016-07-26 15:44:56,046         < secondMethod
2016-07-26 15:44:56,047    < firstMethod

The < and > signs are printed within the method at the start and at the end of the method.

It is not very efficient but you can look at using the call stack to get the hierarchy of methods and then pad/indent each log entry based on the depth of the call in the call stack.

 StackTrace stackTrace = new StackTrace();          
 StackFrame[] stackFrames = stackTrace.GetFrames();

Alternatives could include using PostSharp or (possibly) writing a custom appender for log4net . Another thought is to forget about indenting and use the full namespace and method name to track where your log entry is coming from. This way you will still get a tree like view.

var methodInfo = System.Reflection.MethodBase.GetCurrentMethod();
var fullName = methodInfo.DeclaringType.FullName + "." + methodInfo.Name;

All that said, it seems like a very a-typical requirement. Do you really need to do this?

How about adding another method for logging with parameter for enter/exit, and do the padding there :

int curLevel = 0;
public void MyLogInfo( bool enter, [System.Runtime.CompilerServices.CallerMemberName] string callingMethod = "", )
{
    string prefix;
    if (enter)
    {
      prefix = "> ";
      curLevel++;
    }
    else
    {
      prefix = "< ";
      curLevel--;
    }
    logger.info(prefix.PadLeft(curLevel+prefix.Length) + callingMethod);
}

You just call MyLogInfo(true) or MyLogInfo(false) - .NET will set the callingMethod name if you do not specify it yourself.

You would have to ensure that you do always call the method on entry/exit of each method you log - maybe check you don't decrement curLevel below zero.

You can use NDC to achieve similar behavior. But it is deprecated. Now, you can use Contexts and Contexts Stacks. Check out this documentation:https://logging.apache.org/log4net/release/manual/contexts.html

You can define multiple stacks (instead of indentation). One stack for a set of behaviors you want to debug. Let's say you want to log creating a Sales Order and its Items. In this example, this stack is defined in ThreadContext. However you can define it in a GlobalContext , LogicalThreadContext or LoggingEvent.

using(log4net.ThreadContext.Stacks["SalesOrderLogic"].Push("SalesOrder"))
{
    log.Info("Created the sales order");

    using(log4net.ThreadContext.Stacks["SalesOrderLogic"].Push("Items"))
    {
        log.Info("Created the sales order items");
    }
}

And then you should add it to your configuration:

<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%logger - %property{SalesOrderStatus} - [%level]- %message%newline" />
  </layout>
</appender>

DISCLAIMER: I did not test this code.

Note 1: The old NDC will appear in one thread. So if you pushed a message in one thread, it won't appear in another. And it has only one stack unlike the new Context Stack method.

Note 2: Don't forget to checkout Contexts. It is very useful.

Note 3: Also you might find it useful if you research Ninject and log4net working together

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM