Is passive logging possible in .NET?

前端 未结 6 407
北海茫月
北海茫月 2021-02-02 03:19

I\'m frequently frustrated by the amount of logging I have to include in my code and it leads me to wonder if there\'s a better way of doing things.

I don\'t know if thi

6条回答
  •  有刺的猬
    2021-02-02 03:47

    I've written a logging library recently that uses the IDisposable interface to wrap regions with logging context. Basically, there's a LogSite disposable object that you use like this:

    using(var logger = new LogSite("methodName", new object[] { p1, p2, p3 })
    {
        // code that does stuff goes here
    }
    

    The LogSite object has a bunch of convenient overloads for the constructor such as MethodBase so you can just use MethodBase.GetCurrentMethod() and use reflection to get the actual name of the method and parameters (instead of a hard-coded strings).

    The way it works is this -- In the constructor, it writes to the log with all the trace information to indicate it entered the block. In the Dispose method, it writes an exit entry.

    While disposing, it also checks Marshal.GetExceptionCode() for a non-zero value to see if the code inside the using threw an exception or exited normally. It doesn't give you the exception, so that will have to be logged explicitly in the catch handler, but it does indicate "pass/fail" for that region. This allows your logging scope to be more specific than just the method, since you could have lots of these blocks in a single method, and know which one threw the exception exactly.

    Also, since there's a "logger" object now available, your catch handler just looks like:

    try { ... } 
    catch (Exception ex)
    { 
        logger.LogException(ex);
    }
    

    The logger already knows the method name, parameter information, and all that, and has internal methods for formating the exception information.

    Getting into the architecture below this high-level object, there's a concept of "LogDisposition" which handles the "pass/fail" we determined earlier, and there's a concept of "LogEntryType" which is a filter (implemented with Flags enum) that indicates what kind of log entry is being passed (Error, Trace, etc).

    The thing that actually does the logging is just a publisher/listener pattern. The publisher takes the passed in log entry, and much like a multi-cast delegate, keeps a registry of LogListener instances (should be setup at the start of program, or added in dynamically as needed), and passes the log entry to those instances.

    The LogListeners in turn, filter out what kind of log entries they care about.. So if you don't want the method entry and exit points for non-error conditions, they don't have to show up in the log. This can be controlled at run-time to allow the user to do things like turn on and off detailed logging at will. Since the publisher can write to a variety of loglisteners, you can hook up something that writes to a file, or writes to the database, or displays error notifications in the GUI... etc.

    It's a pretty good system, and requires a relatively small amount of coding to get relatively rich logging.

    I could give you a code sample if you wanted... You can contact me through my (nearly completely inactive) blog (see my account profile).

    Hope that helps.

提交回复
热议问题