I have been told to add “logging” to my code using log4net, the problem is no one can travel in time and see what real world problems the logging will need to be used to solve.<
In general with logging I add logging in the following order:
Obviously I rarely get to the last one, the first one is trivial to do if you roll your own wrapper for log4net and use the disposing pattern, and possibly a little reflection magic.
The 2nd one is done generally during acceptance/integration and regression testing as major logic flow and problem areas are identified. Adding logging at this stage is also fairly minimal as you know in general where you need to add it as you are debugging and testing.
The 3rd is generally (for me anyways) only done in sections of code that experience regressions, or are particularly important.
I have implemented a basic wrapper object for log4net which provides me direct logging capabilities as well as a context object which can be used with IDisposable to wrap "enter/exit" logic in a nice convient package.
One thing to bear in mind is that whilst your configuration will handle logging at different levels, you may be causing heavy overhead in your log calls. For example:
// some kind of loop
// do some operations
Logger.LogDebug(myObject.GetXmlRepresentation());
// end loop
This will obviously only log the object if you have a logger listening to DEBUG logs, however the call to build the XML object will run regardless of your logging level and could cause some considerable slowdowns.
The correct solution would be:
// some kind of loop
// do some operations
if (Logger.IsDebug)
{
Logger.LogDebug(myObject.GetXmlRepresentation());
}
// end loop
I found this article very helpful: http://blog.codinghorror.com/the-problem-with-logging/
In particular I think a minimalist approach is indeed the way to go. In the past I've tried to log too much but this bloats the code
Also the thinking that the more log entries the better is wrong because it bloats the logs themselves. I now look at loggings main benefit as providing a "foothold" or overview of what is going on. If more detail is needed for particular areas then so be it but the default position should be less is better
There's probably a better way to do this for WCF; for WinForms, you might consider looking at PostSharp. This would allow you to log method calls without cluttering your code. Behind the scenes you'd still be using the excellent log4net.
Caveat: I haven't used it myself; I've seen a very impressive presentation on it at a code camp.
My favorite source of information for this kind of question is Release It - a book from the Pragmatic guys. Highly highly recommended.
Their basic point in regards to your question is that logging should be geared toward what is needed at the operational level. Operations guys are most concerned with exceptional things where the site may be going down (i.e. connection pool is full, connection to a server is down, etc.) Make sure the messages are self-explanatory and exceedingly clear as to what the problem is, and if applicable what the fix is. Write the messages for human consumption.
I see little point in function entry/exit style logs. Stack traces for top-level caught exceptions are useful, logging around areas where system crash can happen (i.e. full connection pool) is useful, as is logging around areas where the system crashed before.
One of the great things about log4net is that you can log events to different categories. The defaults are Debug, Info, Warning and Error. I like these to mean
Debug - very verbose, contains lots of debugging information. For example, SQL queries.
Info - useful information that's good to know.
Warning - nothing fatal but an operator should be aware of the problem.
Error - the application is now unstable, the log contains diagnostic information such as the exception message and stack trace.
Use these in code, so e.g.
_log.Info("Updating object.");
would write an INFO level message to any listener that was interested.
Then you can hook up listeners in configuration to do things with the log messages. Here's one that I'm using:
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline" />
</layout>
</appender>
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<file value="c:\temp\servicelog.txt" />
<appendToFile value="true" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline%exception" />
</layout>
</appender>
<root>
<level value="ERROR" />
<appender-ref ref="ConsoleAppender" />
</root>
<logger name="Warehouse.Core">
<level value="INFO" />
<appender-ref ref="FileAppender" />
</logger>
</log4net>
This says: all ERROR messages to the console, all INFO messages from the logger Warehouse.Core to the given file.
Because this wiring of categories to listeners is done in configuration, you can alter the logging after deployment. There's virtually no performance penalty to logging if nothing's listening.
Regarding the costs versus benefits of logging there definitely is a sweet spot between too much logging (huge logs that nobody will use) and not enough (a single line that says "fail").
My policy is to log at INFO what could fail: external dependencies (application startup, service calls, SQL connections), and at DEBUG the more complex bits of meaty code (diagnostic messages in business logic, individual SQL calls, some method invocations).
Unusual situations where (for example) defaults are taken that aren't usually would go to WARN, and exceptions go to ERROR or FATAL.
Also: bear in mind that WCF has a most excellent service trace viewer that allows you to "drill down" to individual packets and how they're processed by both ends of the stack. That, too, is available by configuration without code changes. Because of this I'll generally just do very abbreviated logging of WCF service calls and responses.