Wiki
Clone wikiOkapi / OkapiLogging
Introduction
For a long time the logging in okapi used the java.util.logging
. But as okapi started being used in all kind of other products, both server and desktop, it became more and more necessary to allow for more flexibility, so that we can accommodate the logging in the hosting environment.
Also, having more flexibility in specifying what to log and what not was also something we would have liked.
So we looked at log4j
(for flexibility), and Apache commons logging (for “swapping” the logger), but in the end we decided to go with slf4j
. How we got to that decision is another story, and it is not the something for this document :-)
What we wanted
- The flexibility to plug in whatever logging system one would want, and that is achieved by
slf4j
. - To be able to enable/disable logging per component at build time, to reduce the noise. And we got that by binding
slf4j
tolog4j
for building. - We wanted to allow the developers using the libraries to bind
slf4j
to something else, whatever they want. - To cause minimal/no disruption to the developers using the libraries, or the users of the okapi applications, so we decided to bind
slf4j
to the JDK logger for the end result. But we usedlog4j
at build time. As you can imagine, this complicates things a bit.
Java class loading and log binding
I will not explain here Java the complete mechanism used by Java to find classes. The relevant part for us is the behavior of specifying a classpath in a jar manifest (with Class-Path
), versus a CLASSPATH
environment variable or a the -classpath
command line:
- it does not accept wildcards, so one cannot specify “everything in this folder”
- if specified, it overrides both the command line, and the
CLASSPATH
environment variable
And traditionally the okapi libraries list all the libraries needed in a manifest.
On the other side, slf4j
binds to whatever logging system it finds in the classpath.
So we needed a way to allow one to change the binding without modifying the original okapi jar files.
We did that by introducing logbind.jar
in the lib/logger
folder. All okapi jars that need logging list it as a dependency, and that jar is the one the manifest, listing the logging components to be used for binding.
Okapi users
Customizing default (JDK) logging
By default the logging of the official okapi release uses the JDK loggers. It is controlled by the logging.properties
file in the lib
subfolder of the JRE.
You can specify an alternate one using -Djava.util.logging.config.file=log.properties
in your java command line.
In the config file you can change logging level, handlers, logging pattern, etc.
Using another logging system (log4j)
Warning: tikal
and rainbow
“listen” to the logging in order to present it in a more user-friendly manner. For this we had to work around the slf4j
limitations and talk directly with the underlying logger. This means we had to write logger dependent code. And we only did it for the JDK logger and log4j
only. So even if slf4j
allows for other bindings, okapi is limited to these two (for now).
How to change it?
In the lib/logger
folder you will find logbind.jar
.
It’s manifest (META-INF/MANIFEST.MF
) has a Class-Path
, and by default it contains binding to the current path and the JDK logger:
Class-Path: . slf4j-jdk14-1.7.1.jar
If you want to bind to something else, change it to contain all the libraries used for binding. That means the slf4j
binding library, and the libraries of the logger proper (see http://www.slf4j.org/manual.html)
So this is what you would need in order to use log4j
:
Class-Path: . slf4j-log4j12-1.7.1.jar log4j-1.2.17.jar
You can add the log4j.properties
file in the logger folder, or in logbind.jar
(in the root).
If you want color output, you can download the color-loggers library from http://github.com/mihnita/java-color-loggers/downloads and add it to the classpath:
Class-Path: . slf4j-log4j12-1.7.1.jar log4j-1.2.17.jar color-loggers-1.0.2.jar
And change the appender:
log4j.appender.A1=com.colorlog.log4j.AnsiColorConsoleAppender
And (of course) make sure that the jar files mentioned in the manifest exist in the lib/logger
folder.
Developers building the okapi libraries
If you want to build okapi from sources, the logging at build time uses log4j
.
I would assume the most common case is that you want to keep logging with log4j
, but want to change the logging level. And you might want to do that globally, or for a component.
You can fine tune the logging level for various components by changing the root logging level and the level for various namespaces.
Example:
log4j.rootLogger=ERROR, A1 log4j.logger. net.sf.okapi.filters.openoffice=TRACE log4j.logger.net.sf.okapi.filters.openoffice.ui=TRACE
This will hide all log entries for all components except for the errors, but will enable logging all the way to trace level for everything related to the openoffice
filter.
Customizing logging for command line builds
Most of these are variations on the same question: how to force java to use the log4j
configuration file I want?
There are several ways to do this, and which one do you choose might depend on your style. I use option 1, on Windows.
- You only see logging output during the unit tests steps of the build. Since we do this with maven, you can specify an alternate
log4j
configuration file inMAVEN_OPTS
:
Windows example:
set MAVEN_OPTS=%MAVEN_OPTS% "-DargLine=-Dlog4j.configuration=file:C:/MyLogger/okapi/log4j.properties"
Mac/Linux example (not tested):
MAVEN_OPTS=$MAVEN_OPTS% "-DargLine=-Dlog4j.configuration=file:/MyLogger/okapi/log4j.properties"
Note that this is not an argument to pass to the JVM running maven, but an argument that maven should pass to the JVM that runs the tests, hence -DargLine=...
instead of just –Dlog4j.configuration=...
This will affect all modules.
- Another option is to change default
log4j.properties
The binding is done using the okapi\deployment\logbind-log4j
module, which is (almost) manifest-only. The manifest lists slf4j-log4j12-1.7.1.jar
and log4j-1.2.17.jar
in Class-Path
and also contains the default log4j.properties
file.
So you can change the file in okapi/deployment/logbind-log4j/src/main/resources
folder and build. But you will have to make you don’t submit it by mistake :-)
This will affect all modules.
- Yet another option is to change the configuration file in the local maven repository (
repository/net/sf/okapi/logbind/build-log4j/{okapiVersion}
). You can find it inside the jar, or take it out.
This will affect all modules, and you will lose your changes if logbind-log4j
gets rebuilt.
- And the last option listed is to add a config file to the module you are debugging. It should be in the
target/test-classes
ortarget/classes
folder, and should be namedlog4j.properties
.
In general I find that finding the log4j
configuration file can be pretty tricky. At times I am using a file activity monitoring application that can list where does log4j
try to open a log4j.properties
file (the tool on Windows is procmon
, from Windows Sysinternals suite).
You might even have a global (system wide) log4j.properties
by packing it in a jar and put it in an extension folder, like %SystemRoot%\Sun\Java\lib\ext
on Windows or /usr/java/packages/lib/ext
on Linux (see http://docs.oracle.com/javase/tutorial/ext/basics/install.html)
Customizing logging for Eclipse builds
I would assume that you already have you run configurations working in Eclipse.
All you have to do is add a folder containing log4j.properties
to the classpath.
Step 1: Create a “Classpath Variable”
For more flexibility you can also use a “Classpath Variable” instead of a folder. This would cut some folder navigation (if you have a lot of run configurations) and will allow you to update things in one place.
From Eclipse main menu select “Window” -> “Preferences”.
In the “Preferences” window navigate to “Java” -> “Build Path” -> “Classpath Variables”
Click the “New” button, give the variable a name (let’s say OKAPI_BUILD_LOG
), click “Folder” and navigate to the folder that contains your custom log4j.properties
, and click “OK”
Step 2: Add the classpath variable to your run configuration classpath
In the Eclipse main menu select “Run” -> “Run Configurations...”, then select “Classpath” tab. Click “User Entries” so that the buttons on the right get enabled, and click “Advanced...”.
Select the “Add Classpath Variable” radio button and click “OK”, select your variable (OKAPI_BUILD_LOG
in our example) and click “OK” again.
The trick is to make sure that your log4j.properties is found before the one in build-log4j-{version}.jar
(in the local maven repository). So select the newly added entry (OKAPI_BUILD_LOG
) in the “User Entries” and click the “Up” button until it gets before the “Maven Dependencies” entry.
This will affect everything for that run configuration.
Bonus step: color logging
1. Install the “ANSI in Eclipse Console” plugin from http://www.mihai-nita.net/eclipse/
Because I prefer a dark environment, so from the “Preferences” dialog I go to “Run/Debug” – “Console” and change the “Background color” to black and “Standard Out text color” to light gray. Then also change the “Background color” for “Ansi Console” to black.
2. Download the color-loggers.jar
from https://github.com/mihnita/java-color-loggers/downloads and save it somewhere (I keep it in the same folder as the log4j.properties
)
3. Create a “User Library”
From Eclipse main menu select “Window” -> “Preferences”.
In the “Preferences” window navigate to “Java” -> “Build Path” -> “User Libraries”
Click the “New” button, give the library a name (let’s say ColorLog4J
) and click “OK”
Select it, click “Add JARs...”, navigate to contains color-loggers.jar
, select it, and click “OK”
Click “OK” again to close the “Preferences” dialog.
4. Add the color logger to Classpath
In the Eclipse “Run Configurations...” dialog, “Classpath” tab, “User Entries” section (same as before) click “Advanced...”, select the “Add Library” radio button, and click “OK”
In the “Add Library” dialog select “User Library”, check the checkbox of ColorLog4J
and click “Finish”
5. Update the log4j
to use the color logger
Change the log4j
appender to use com.colorlog.log4j.AnsiColorConsoleAppender
:
log4j.appender.A1=com.colorlog.log4j.AnsiColorConsoleAppender
Updated