(this post discusses a solution for Automatic Configuration – find the configuration file in the class path)
In the realm of plugin development, there’s a silent hero often overlooked: logging. While coding glamorous features and crafting sleek user interfaces steal the limelight, logging quietly plays a crucial role in ensuring the stability, security, and performance of applications. The significance, its various types, and best practices for implementing logging effectively has been discusses in many blogs and articles. In this occasion I will focus on how to enable logging for your eclipse plugin. I will be using Apache’s log4j as an example, but the ideas apply to any other framework.
You just finished implementing a complex piece of code and run some tests to make sure everything works as expected. A couple of test fail and in order to better understand why, it would be very helpful to look at your log file. Your log statements have been in-lined with your code so all that is missing is configuring the logger.
Most (if not all) logging frameworks allow you to use a configuration file (XML, JSON, YAML, properties) to set the properties of your loggers and appenders. The framework will uses that file during initialization to configure itself. The only requirement is that the file has a specific name (e.g., log4j2-test.properties
, log4j2.yaml
, etc. for log4j) and that the file is in the classpath. With these in mind, you create your configuration file save it in your classpath and run your tests. Sadly, your console is silent during test execution and the log file is nowhere to be found.
How many important log messages have been lost? All the hard work of developers picking when to log, what to log and writing helpful messages… lost. Why would I invest in adding proper logging to my application/plugin if it is going to get lost?
I tried…oh I TRIED!
After I figured out how to make things work in Java standalone applications, I was confident I could make it work on Eclipse. Stack Overflow is full of “where to put my xxx.properties file” and the answer is always verbatim from the logging framework documentation: “in your classpath!” (obviously). With different techniques on how to get a noncode artefact to end up in our classpath depending on your build system, folder structure and what not. And it works! It does. My standalone apps to proper logging.
I tried the same in my Eclipse plugins:
- configuration file in the root of my src folder
- configuration file in the root of my project, but add it to my build outputs
- configuration file in the root next to one of my classes
- configuration file next to my activator class
- copy configuration in the root of my eclipse installation
- copy configuration in my eclipse plugins folder
- unzip a one of the eclipse plugin jars, add the configuration file and zip it again (OK, this one I did not try, but I was tempted…)
No matter what I tried, I kept getting the dreadful message. In other words, the logging framework still could not see the file.
Understanding why the standard Framework configuration does not work
The issue is that your plugin and the logging framework plugin are using separate classpaths. Wait, what? Yes, you heard me right. Take a deep breath and read that sentence again. In a nutshell Eclipse uses OSGI (a framework for component-based systems) under the hood and as a result each plugin is started in its own classpath. Yes, plugins can talk to each other and you can have dependencies on other plugins, but that is handled by OSGI. When the log4j plugin starts and searches for configuration files it cant see the classpath of your plugin and hence, will never be able to see your configuration file.
The light at the end of the tunnel
Lately I have been developing grammars with xText, and they have some info on the matter on their website. The important bit the OSGI information:
In OSGi [i.e. Eclipse] you provide configuration by creating a fragment for org.apache.log4j. In this case you need to make sure that there is not any second fragment contributing a log4j.properties file.
Which of course I totally disregarded until I came across this Stack Overflow post, where SteveD suggests the same: “Put the log4j property file in an OSGi fragment and host it on the log4j bundle”. Where host it on the log4j bundle makes a bit more sense than creating a fragment for org.apache.log4j.
But WHY? (Spoiler alert, it works) I like to understand how/why things work so we need a bit of background.
Plugin Fragments
An OSGi fragment is a Java™ archive file with specific manifest headers that enable it to attach to a specified host bundle or specified host bundles to function. Fragments are treated as part of the host bundles.
The important bit is in bold (formatted by me). Basically, you can create a plugin fragment that will be appended to the host bundle and the OSGI framework will treat it as a single plugin. Thus, if you create a plugin fragment with the configuration file and attach it to the logging framework bundle (plugin), then the configuration file will be in the correct class path.
Creating a plugin fragment for the configuration file
1. Create a plugin fragment project:
Give it a name (probably relevant to your project) and leave the default options – unless you have other needs.
2. Select the correct host. In this case, I was using apache log4j:
3. Make your fragment a singleton:
4. Place the properties file in the src folder.
5. (Advanced) There might be another fragment contributing a configuration file. If that fragment is loaded first, then your fragment will have no effect (i.e. the other configuration file is loaded first). There are a couple of ways to fix this. One, make sure the other plugin is not part of your dependencies. For example, when running nested eclipse you can unselect it so it won’t be present in the nested environment. Another option is to change the Start Level of your fragment to make sure it is loaded first.