- Organizational Standards and Conventions
- Putting the "Engineering" in Software Engineering
- Working with Logging and Tracing
- Project and Packaging File Structure
- Unit Testing Requirements
- Code Completion and Review Process Requirements
- Communicating the Vision the Wiki Way
- Conclusion
- Links to developerWorks Articles
- Reference
Working with Logging and Tracing
This type of logging offers several benefits, one of which is the ability to configure logging from within the admin console. Loggers are automatically displayed within the admin console Diagnostic Trace Service (see Figure 2.2) settings area. Using a package name approach can greatly increase the ability to turn on and off loggers at the right scope.
Figure 2.2 Admin console trace services
Turning on too much logging (see Figure 2.3) can cause an avalanche of information into the logs that will quickly overwhelm anyone trying to get usable information about a problem. The better approach is to selectively turn on logging for the potential problem area or to turn it on more generally at higher levels until you narrow down the problem area and can turn on full tracing to a deep analysis.
Figure 2.3 Setting log levels
You can change logging levels on the fly using the admin screen. Click on the package you want to examine to set the appropriate levels. This approach is invaluable when diagnosing a problem in a running environment where you suspect that a restart would mask the problem, or make it go away for a while.
Too Much Logging?
One issue with logging is that sometimes it can be too much—or can it? In the simple samples of code in this section, the logging actually takes more effort than the code doing actual business logic. But this probably won’t ever be the case in real life, unless all you are doing is displaying table data and nothing else. But the reality is being verbose with your code should be a good thing. If the code is well formatted and the internal documentation is verbose enough, then code becomes more readable and maintainable, both during development and at runtime.
Good logging standards should be one of the basic building blocks in the framework of your architecture. Imagine if you were building a house and instead of putting a stud up every 12 or 16 inches, the builder decided to only put them up every once in a while? Luckily, people build houses better than we build software, because otherwise your house would never pass inspection and never be allowed to go beyond the framing stage. Some developers are naturally more verbose than others, but there has to be some minimum standards, or building codes if you will, to ensure your application can withstand the high winds and rain that we have to plan for.
Why Not Use Aspects?
Aspect-oriented programming is a development approach that attempts to separate many of the routing and repeating parts of development into their own set of objects. AOP specifically focuses on topics of a cross-cutting concern, or those items that cut across the entire development effort. Logging is one of those cross-cutting concerns. It is something that we do everywhere and not specific to any one part of the code. Whenever the discussion starts to focus on aspects the discussion always turns to logging, mainly because it is the most obvious example of a cross-cutting concern with any application. While this is interesting I don’t believe I can get the same type of detail needed by relegating logging to a different set of layers. I believe that logging and tracing have to be built into the system to get the maximum detail.
Unless you have been at the back end of a project that is having trouble in production, understanding how important this standard really is may be difficult. Aspects can play another role in instrumentation of code that can be very powerful.
Exception and Error Handling
Exception handling is a topic that is probably even more controversial than some of the other topics discussed in this chapter. I don’t have any silver bullet solutions regarding this topic, but I can offer guidance for some decisions for this part of your project. Much of the debate and discussion around exception handling revolves around two specific areas—handling exceptions where they occur, or throwing them up through the call stack.
Checked Versus Unchecked Exceptions
More often than not I agree with the idea that unchecked or runtime exceptions should never be caught. If you are not sure of the difference between checked and unchecked exceptions then you need to open a programming book and dig in. The general idea is that unchecked exceptions are things you should be looking for in your code anyway, such as a null pointer or an array out of bounds. Checked exceptions are things you can’t really do anything about, such as an out-of-memory error
So with these basic definitions in mind, you should only throw checked exceptions within your code, and only those things that you can reasonably expect the calling code to handle—Of couse any errors that the calling method might expect from your code should be documented.
I do not like the idea of building large custom exception libraries within your application. This technique has always seemed counterintuitive to me when trying to reduce the bulk and complexity of an application. Each layer should only worry about the calling layer and what it might be able to do with a thrown exception. Letting upper layers determine whether the calling layer can reasonably handle an exception or will need to re-throw it is beyond the scope of the lower layer designers. From there is the calling layer’s responsibility to determine whether it can actually handle the exception or needs to throw it higher up the stack.
Think about what the calling method may be looking for. If a method passes a null parameter and gets back a null result then it should be expecting to handle that exception and do some internal value checking. If you are within a middle layer it might be your responsibility to notify the calling program that it has passed you a null when you know that will cause problems.
You have probably heard it before but it is worth saying again, because I see it time and again when I do code reviews for customers:
Never hide an exception!
Hiding exceptions is asking for trouble, and then not wanting to know about it. If you do not know what I mean by exception hiding take a look at the following code example:
} catch (SQLException SQLex) { //Do nothing here. }
In this case I caught the exception, a SQLException, and then did nothing, just kept running my program. Undoubtedly something is going to crash during this request and more than likely it will end with an ugly-looking error message to the end user requesting this data. Don’t catch java.lang.Exception except in the presentation layer of the application.
} catch (Exception ex) {
Many people will disagree and say not to catch general exceptions at all, but I think that in the presentation layer you have a responsibility to ensure that your end users receive generic error messages, rather than a stack trace on their screens, even if the prettier message is fairly useless.
The next point about handling exceptions, especially in the upper layers of your code, is to do something responsible with them.
}catch(SQLException SQLex) { if (logger.isLoggable(Level.SEVERE)) { logger.logp(Level.SEVERE, getClass().getName(), "doGet", "Servlet failed: " + SQLex.getMessage()); logger.logp(Level.SEVERE, getClass().getName(), "doGet", SQLex.getStackTrace().toString()); } //something bad happened, so display error jsp. nextJSP = "WEB-INF/com.cm.web.sample.log.jsp/Error.jsp"; }
Notice in the preceding code snippet the catch block changes the display JSP to provide an error message to the end user. More than likely when something bad happens we don’t get any returned data and the JSP would end up with an empty data set for display. An alternative approach is to add some handling code in the display JSP to handle an empty data set.
The possibility exists that the logging code itself may throw an exception. Additional wrapping of a try/catch may be necessary to avoid this problem.
}catch(SQLException SQLex) { try{ if (logger.isLoggable(Level.SEVERE)) { logger.logp(Level.SEVERE, getClass().getName(), "doGet", "Servlet failed: " + SQLex.getMessage()); logger.logp(Level.SEVERE, getClass().getName(), "doGet", SQLex.getStackTrace().toString()); } //something bad happened, so display error jsp. nextJSP = "WEB-INF/com.cm.web.sample.log.jsp/Error.jsp"; } catch(...) { }
This is especially true when you are trying to close resources such as a data connection. This may seem like overkill, but it has happened time and again. The approach you use depends upon your level of comfort, your timeline, and the skill of your development team.