Logging Delegate

Logging is part of every application and we’re used to the lines of code which clutter our codebase with logger.info("something is happening here with {}" , var1); But is this great code? Or has this become the level of quality we’re familiar with? In the light of ”separation of concerns” (one and only one reason for change), ”same level of abstraction” (inspired by Clean Code of Uncle Bob) what would be the best practice to implement logging (without going to external solutions like AOP or alike)? Well, not really a question about the implementation of logging itself of course. SLF4J, LogBack, Log4j, Java Logging API and others are excellent implementations for logging info to a log file. The question here is about the best way to use a(ny) logger. Many developers will recognise the following lines (examples with org.slf4j.Logger):


private logger = LoggerFactory.getLogger(this.getClass());

or static final:


private static final logger = LoggerFactory.getLogger(MyClass.class);

and using the declared logger:


public String doSomething(final String input) {
  logger.debug("doSomething START met input [{}]", input);
  String result = businessService.executeLogic(input);
  logger.info("doSomething FINISH input [{}], result [{}]", input, result);
  return result;
}

The first issue with this code snippet is the difference in level of abstraction. Using the logger with the full info being logged isn’t the same level as the executeLogic method call of a businessService. The following snippet has more equal abstraction levels:


public String doSomething(final String input) {
  logDoSomethingStart(input);
  String result = businessService.executeLogic(input);
  logDoSomethingFinish(input, result);
  return result;
}
 
public void logDoSomethingStart(final String input) {
  logger.debug("doSomething START met input [{}]", input);
}
 
public void logDoSomethingFinish(final String input, final String result) {
  logger.info("doSomething FINISH input [{}], result [{}]", input, result);
}

Although it is getting better there’s still an issue about the separation of concerns. There is reason to change this class for the actual logic being executed, calling a businessService, but also for the logging info. To separate these concerns the logging methods could be extracted into a loggingDelegate as a second class beside the current class. Taking into account it is a best practice to program against interfaces this would double the amount of interfaces and concrete classes. Each BusinessService interface would have a BusinessServiceLoggingDelegate interface as would the implementations BusinessServiceImpl and BusinessServiceLoggingDelegateImpl. This is not really an improvement of the whole code base, is it?

Solution

Well, what about a separate class without having a separate .java file: an ”inner class”. The not often used inner class might provide a solution here. This will not totally separate the concern outside the .java file but separates it on class level. An instance of the logging delegate could be named logger for the feel of it. The LoggingDelegate class could extend a general abstract implementation with common features.

The result:


/**
 * Example business service implementation only as example for application of the {@link LoggingDelegate} pattern.
 */
public class BusinessServiceImpl {

    private LoggingDelegate logger;

    public BusinessServiceImpl() {
        super();
        logger = new LoggingDelegate();
    }

    public String doSomething(final String input) {
        logger.logDoSomethingStart(input);
        String result = otherBusinessService.executeLogic(input);
        logger.logDoSomethingFinish(input, result);
        return result;
    }

    private static class LoggingDelegate extends Slf4jLoggingDelegate {

        public void logDoSomethingStart(final String input) {
            logger.debug("doSomething START with [{}]", input);
        }

        public void logDoSomethingFinish(final String input, final String result) {
            logger.info("doSomething FINISH with [{}], result: [{}]", input, result);
        }

    }

}

On Github is the working code example of above extract: https://github.com/marcvanandel/logging-delegate

A few final thoughts:

  • Applying this into a real world application my Sonar report does not improve with this pattern in place. On the contrary it decreases. So what is the problem?
  • One rule is not mentioned and forgotten: Is it testable? Maybe we should change the private accessor to default? And have some tests in the test companion object of the BusinessServiceImpl?

 

Triangle of Software Development Principles

IN CONCEPT

When we develop our business functions we need to automate things. Automation means software. Software is cool. Software is called ‘soft’ because there’s no hardware, no hard things involved … BUT software is hard to develop. Here’s my Triangle of Software Development Principles.

Long time ago I learned the quote: There’s only one constant in software development: CHANGE. And it’s true. While we strive to build perfect software for perfect business functions with perfect teams … there’s always a reason for change. We gain new insight on our business function so we would like to have new features. So our business was not so perfect as we had in mind. Even before a new feature is released to our customers technology has evolved and our solution has ‘technical debt‘, a lag with the latest state of technology stack (which our competitor might use already). So our technology stack and software is not as perfect as we would like it to be. And even the team might strive for perfection we will always be learning and become more equipped than before to develop and optimise the way the team supports the business function.

So our software needs to be flexible, changeable, like clay. It should have a high level of Changeability.

On the other hand users might not be pleased with ever changing user interfaces. Other systems the business function needs to integrate with is not changing at the same pace and at the same time. There’s also some need of stability in our software. But in a way it does not frustrate the changeability. There’s a huge amount of books, blogs, website about good software design and principles and here are some examples:

  • Design patterns like
    • Open Closed Principle from Uncle Bob’s SOLID principles: Open for extension but closed for modification
    • Dependency Inversion Principle and the example of this in Inversion of Control / Dependency Inversion: Modules should depend on abstraction not on details (or ‘Programming to an interface’ in very short 😉
    • And DRY, Don’t Repeat Yourself, of course …
  • API First; thinking in APIs which are versioned and steady so decoupling is maximum and change is less likely to break things

All these principles are about how to design what should be changeable and what not. Therefore you need to think about the changeability of your software for things that need to change much. For the ‘harder’ parts you need to think about the Extendibility of your software. How to set it up that it can be stable but still open for extension and thereby still supporting change. This will generally be visible in abstraction, interfaces and such.

And while you have all this flexibility and stable parts going around and be changed the only way to prove the business function is still doing what it’s supposed to do is by testing. And the design should support the Testability. If it’s hard to test there’s a smell to it. You should rethink the design, the structure of the software and refactor it to improve. And support a higher level of changeability and extendibility which still supplies the business function in a stable way.

On the other hand tests will also make change harder. The way the tests are proving the business function are binding the changeability. So even the tests are eligible for these principles in itself.

So there’s a balance between the goal of changeablity against proving the product is still doing what it is supposed to do by the tests and to be able to still change easily we strive for extensibility which in turn downgrades the changeability. It’s a balance of a triangle, the Triangle of Software Development Principles.


Part of the Triangles Architecture