Loggable: a simple log4j meta-library - Part 2

In a recent post I described the goal of my Loggable meta-library. If you haven't read it yet, I suggest you to do it now.

What we are going to do now is analysing the second main component of the meta-library, the centralized controller Log.

How does it work

The main responsibility of the Log class is handling the logic behind the creation and returning of the Logger instance that the caller class should use. This logic, as we've already seen, is driven by the @Loggable annotation.

The Log class will have a single point of access: the public static method get(). Let's look at if, briefly:

	public static Logger get() {
		// Retrieve the StacKTraceElement of the caller method
		StackTraceElement caller = getCaller();
		// Retrieve the caller class
		Class<?> clazz = getClass(caller);
		// and its Loggable annotation instance
		Loggable annotation = getAnnotation(clazz);

		logger.debug("Returning a Logger for " + clazz.getSimpleName());

		// Retrieve the suitable logger for the caller class and returns it
		Logger suitableLogger = getLogger(annotation, clazz);
		return suitableLogger;
	}

This method does a precise series of things:

  1. Retrieves the stack trace element related to the caller method
  2. Retrieves the Class<?> instance which describes the caller class
  3. Reads the @Loggable annotation from the caller class
  4. Returns the suitable Logger instance, depending on the class and its @Loggable annotation definition

The whole code for this class will be available within the meta-library package, so I'll focus just on the main private method: getLogger().

Bonus hint: as you can see, in my development environment the Log class makes a direct access to a Logger instance. This is strictly necessary because of recursions problems: if the Log class would call itself for logging purposes, this will generate a never-ending recursion stack. Try it, and try to explain why: this will be your homework for today 🙂 .

The private getLogger() method

This method is the key for everything, I'd say. It controls the logic and the interpretation of the annotation parameters. It handles the Logger instance creation and retrieval and will be in our scope in the next paragraphs. The source code is the following:

	private static Logger getLogger(Loggable annotation, Class<?> clazz) {
		if (annotation == null)
			return LoggerContainer.getRootLogger();

		if (annotation.exclude())
			return LoggerContainer.getVoidLogger();

		// If a "loggerName" parameter has been passed to the annotation,
		// returns that specific logger instance
		if (!"".equals(annotation.loggerName()))
			return LoggerContainer.getInstance(annotation.loggerName(), annotation);

		// If no "class" parameter has been passed to the annotation, returns
		// the default logger for the caller class ...
		if (annotation.clazz() == Object.class)
			return LoggerContainer.getInstance(clazz, annotation);

		// ... otherwise use the "class" parameter to build the logger
		return LoggerContainer.getInstance(annotation.clazz(), annotation);
	}

What does it do? Just what we said in the previous "episode":

  1. If no @Loggable annotation has been set for the class, returns the root logger
  2. If the annotation requires the class to be excluded by logging, returns a void logger
  3. If the annotation defines a logger name, returns that logger (= Logger.getLogger(annotation.loggerName()) )
  4. If the clazz parameter of the annotation is still the default (Object.class), returns the default logger for the caller class (= Logger.getLogger(CallerClass.class) )
  5. Otherwise, returns the default logger with the class specified in the annotation (= Logger.getLogger(annotation.clazz()) )

How does this work? This method uses a third class, LoggerContainer. This is a really simple container: when a logger is required, if it has already been built and customized, the same object will be returned; otherwise, the new instance is built, customized, added to the local container and finally returned. This will save time and memory when we call the Log.get() method. (It is a somewhat similar implementation of the static final definition for the usual logger instance).

Conclusion

Given these three simple components, two classes and a single annotation, I tried to build a handy log4j meta-library. I am currently using it in a bunch of personal projects as it has drastically simplified the logging process throughout the whole applications. I found it very helpful and customizable.

I still need some feedback on this project. I will try to complete the setup of the package, and hope to release version 1.0 of the meta-library as soon as possible.

See you soon!

Update: the first official release is now available! Give it a try!

2 thoughts on “Loggable: a simple log4j meta-library - Part 2

  1. Terrific work! This is the type of information that should be shared around the web. Shame on the search engines for not positioning this post higher!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *