|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 4 of 5

There is another reason that JDBCAppender's design is questionable. JDBC has its own template engine prepared statements. By using PatternLayout, however, the template engine is bypassed. This is unfortunate because JDBC precompiles prepared statements, leading to significant
performance improvements. Unfortunately, there is no easy fix for this. The obvious approach would be to control what kind
of layout can be used in JDBCAppender by overriding the setter as follows.
public void setLayout(Layout layout) {
if (layout instanceOf PatternLayout) {
super.setLayout(layout);
}
else {
throw new IllegalArgumentException("Layout is not valid");
}
}
Unfortunately, this approach also has problems. The method in Listing 4 throws a runtime exception, and applications calling
this method may not be prepared to catch it. In other words, the setLayout(Layout layout) method cannot guarantee that no runtime exception will be thrown; it therefore weakens the guarantees (postconditions) made
by the method it overrides. If we look at it in terms of preconditions, setLayout requires that the layout is an instance of PatternLayout, and has therefore stronger preconditions than the method it overrides. Either way, we've violated a core object-oriented design principle, which is
the Liskov substitution principle used to safeguard inheritance.
The fact that there is no easy solution to fix the design of JDBCAppender indicates that there is a deeper problem at work. In this case, the level of abstraction chosen when designing the core abstract
types (in particular Layout) needs fine-tuning. The core method defined by Layout is format(LoggingEvent event). This method returns a string. However, when logging to a relational database a tuple of values (a row), and not a string
needs to be generated.
One possible solution would be to use a more sophisticated data structure as a return type for format. However, this would imply additional overhead in situations where you might actually want to generate a string. Additional intermediate objects would have to be created and then garbage-collected, compromising the performance of the logging framework. Using a more sophisticated return type would also make Log4j more difficult to understand. Simplicity is a very desirable design goal.
Another possible solution would be to use "layered abstraction" by using two abstract types, Appender and CustomizableAppender which extends Appender. Only CustomizableAppender would then define the method setLayout(Layout layout). JDBCAppender would only implement Appender, while other appender implementations such as ConsoleAppender would implement CustomizableAppender. The drawback of this approach is the increased complexity (e.g., how Log4j configuration files are processed), and the fact
that developers must make an informed decision about which level of abstraction to use early.