Good Developer Habit #2: Be a Better Logger than Paul Bunyan
Like many things logging is an art, not a science. Many rules of thumb apply for effective logging, starting with use a logger (e.g. log4j, rlog, etc) rather than cerr or System.out.println.
Never emit a log statement w/o at least one argument!
Logging is expensive. It likely requires argument processing, disk writes, and storage. Make every log event really count by providing as much relevant context as possible for someone who views the log messages 5 minutes (or 5 days) later to understand what might have occurred. The more '''relevant''' context provided in the message the higher the probability someone can interpret and deduce the root cause.
Bad:
log.warn("Missing item");
Good:
log.warn("Missing item in order " + orderID + " for customer "
+ customerID);
It only took 2 seconds longer to type a much more useful error message. Don't be lazy! Its extremely frustrating when you are under pressure to debug a production system and realize someone forgot to log critical information that would help understand root cause.
Use appropriate logging levels
In production applications you should expect to drive log files to zero fatals and errors. You should also consider driving warnings towards 0 as having unnecessary entries in your logs makes surfacing real problems more difficult.
Think about the computation
Reminder: '''even if the logging level is disabled all arguments passed into the logger get evaluated'''. The following is a good example using log4j where CPU cost is undefined:
log.debug("Event " + event + " occurred for customer " + customerID);
While this looks innocuous, if event's toString() method actually does much computation this can eat up valuable CPU. For instance, event.toString() couldb e doing a database lookup to get event meta-data. A better approach is to add a conditional check:
if( log.isDebugEnabled() )
log.debug("Event " + event + " occurred for customer " + customerID);
For fatal/error/warning messages this is unnecessary since it is generally expected all log messages of those levels should be emitted into logs. However, for any more verbose levels (inform/debug/notify/verbose) you should wrap w/ a conditional check. '''If you are in a tight loop or critical core function, be extra careful!'''
Another rule of thumb: 'DON'T MODIFY STATE OR ADD BUSINESS LOGIC IN LOG STATEMENTS. This is considered a BIG NO NO:
log.warning("Request count=" + (++requestCount));
If someone changes the logging level there is a possibility that those statements never execute (depending on whether the logging library has conditional check macros/byte code insertion)! In fact, even though it works one day there are no guarantees that deploying a new version of your logging library won't change the behavior.
Java
Perhaps one day Java's log4j or commons logging will finally add byte code injection to insert these statements automatically. However until that day, follow the conditional check pattern.
And if using Java's log4j for logging exceptions, then leverage it's native ability for including Exceptions w/ configurable backtraces:
log.debug("Unexpected exception for accountID=" + accountID, e);