The "One Laptop Per Child" project has a great device ready to ship, but there's no Java on there. Let's think about working together to put Java on OLPC!
Eclipse Europa has Log4J available as a bundle in some packages; you can also download it from Orbit. But how do you configure it? We take a look at a simple way.
I'll assume that you know about
Log4J
already. If not, go over and read up about it. I'll wait.
Log4J works by obtaining
loggers
, which can either be globally shared or customised on a per-class basis, and then using the output from those to generate code to
appenders
. Thus, we need a way of configuring both of these. Typically, this is done by putting a
log4j.properties
on the classpath, but we don't have that luxury in an OSGi environment.
Let's say we want to do some logging in our bundle when it starts. Our code might look like:
The idea is that we'll get the logger associated with our particular class, so that we can enable/disable logging for our class individually. (Alternatively, we might choose to use the bundle's symbolic name here if we want all logging for the bundle to be the same.) We're printing out a few different flavours of message, just to show what you can do.
We can compile and run this in the form of an OSGi bundle; it's attached below. Here's how you'd get it up and running, assuming that both Log4JDemo.jar and the org.eclipse.osgi/org.apache.log4j bundles are in the same directory:
java -jar org.eclipse.osgi_3.3.0.v20070530.jar -console -noExit -clean
osgi> install file:./Log4JDemo.jar
Bundle id is 1
osgi> install file:./org.apache.log4j_1.2.13.v200706111418.jar
Bundle id is 2
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.0.v20070530
1 INSTALLED Log4JDemo_1.0.0
2 INSTALLED org.apache.log4j_1.2.13.v200706111418
osgi> start 1
log4j:WARN No appenders could be found for logger (log4jdemo.Activator).
log4j:WARN Please initialize the log4j system properly.
Oops. That's not good. We get an annoying message from Log4J letting us know that it doesn't know what to do, so plays safe and ignores logging requests. We'd quite like it to do something else. We need to give it a configuration file like this:
# Set root logger level to debug and its only appender to default.
log4j.rootLogger=debug, default
# default is set to be a ConsoleAppender.
log4j.appender.default=org.apache.log4j.ConsoleAppender
# default uses PatternLayout.
log4j.appender.default.layout=org.apache.log4j.PatternLayout
log4j.appender.default.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
Suffice to say, running with 'log4j.properties' in the current directory (or anywhere on the classpath) doesn't work, because OSGi manages a per-bundle classpath. What we need to do is provide log4j.properties for it to find.
One way of doing this would be to put a log4j.properties file into our Log4JDemo bundle. However, that's not very extendible, and we have to do all sorts of weird things to make that work. Instead, we can use OGSi
fragments
, which are like parasitic bundles. They infect the host bundle that they're attached to, but are only available when that bundle is running.
We can create a fragment by defining a special keyword in the fragment's manifest:
This says we need to attach to the org.apache.log4j bundle. At runtime, the log4j's bundle classpath is extended with the contents of our classpath (in this case, '.' by default) and so we can embed our log4j.properties file in this fragment. When Log4J asks for log4j.properties from the current bundle, it will fail to find it here and use our fragment's classpath, which will contain the log4j.properties entry. For convenience, I've attached Log4JProperties to this post as well.
Now we can run our app again, but this time, installing the Log4JProperties as well:
java -jar org.eclipse.osgi_3.3.0.v20070530.jar -console -noExit -clean
osgi> install file:./Log4JDemo.jar
Bundle id is 1
osgi> install file:./Log4JProperties.jar
Bundle id is 2
osgi> install file:./org.apache.log4j_1.2.13.v200706111418.jar
Bundle id is 3
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.0.v20070530
1 INSTALLED Log4JDemo_1.0.0
2 INSTALLED Log4JProperties_1.0.0
3 INSTALLED org.apache.log4j_1.2.13.v200706111418
osgi> start 1
0 [OSGi Console] INFO log4jdemo.Activator - Info starting
5 [OSGi Console] WARN log4jdemo.Activator - Warning starting
6 [OSGi Console] ERROR log4jdemo.Activator - Error starting
What happens if we want to change the level of the logging? We might want to change the default log level from 'debug' to 'error'. Well, all you need to do is edit the log4j.properties in the bundle and update it. Unfortunately, because it's a fragment, and the fragment is associated with the log4j bundle, you've got to refresh the packages for it to be re-bound, which will have the side effect of stopping/starting the Log4J classes:
osgi> update 2
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.0.v20070530
1 ACTIVE Log4JDemo_1.0.0
2 INSTALLED Log4JProperties_0.0.0
3 RESOLVED org.apache.log4j_1.2.13.v200706111418
osgi> refresh
10 [OSGi Console] INFO log4jdemo.Activator - Info stopping
15 [OSGi Console] WARN log4jdemo.Activator - Warning stopping
16 [OSGi Console] ERROR log4jdemo.Activator - Error stopping
26 [OSGi Console] ERROR log4jdemo.Activator - Error starting
osgi> ss
id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.0.v20070530
1 RESOLVED Log4JDemo_1.0.0
2 RESOLVED Log4JProperties_0.0.0
Master=3
3 RESOLVED org.apache.log4j_1.2.13.v200706111418
Fragments=2
Obviously the timestamps will be different in your cases (as may the bundle identifiers) but it introduces the concept of fragments and how you can use them to separate out the configuration of the logging from the actual code itself. And of course, you can use many other logging formats and appenders that are provided with the Log4J infrastructure that we're not covering here.
The only problem with this approach is that it's not very modular. Log4J's property configurator (or XML configurator) uses a single file to configure everything. Next time, we'll look at the OSGi configuration admin service in conjunction with the Log4J configurators to see how we can make it more flexible and dynamic, without having to start and stop our application's bundles each time we want to change the logging level.
My name turned up in
Category.java
from a good few years ago
(In fact, the
curious paleontologist
in me found out that the comment was in there back in
Log4J 1.0.4
, released in January 2001 Doesn't time fly? Putting it into perspective, that's about 10 months before Eclipse 1.0 hit the shelves.
it's an attempt to tie the various logging APIs together with an implementation of the OSGi LogService, so developers can code to whatever API they're comfortable with.
the latest version allows configuration via the ConfigAdmin service.
I think this is trying to sit on top of Log4J, and map OSGi Log files to it. What would be more interesting is to allow people to continue to use Log4J, but have it administered by CM. I'm not convinced that providing a Log4J emulator is really what's needed.
actually it still uses Log4J as the final backend (configured using ConfigAdmin) but has the benefit of providing alternative APIs for bundles that were written to use other logging schemes.
as a client of Pax-Logging you can use log4j, slf4j, jcl or even the LogService API and have your log output aggregated through the log4j backend.
Alex, I'm very interested in your followup on CM. I've looked at it briefly and I'm interested in the possibility of using it to configure an OSGi server from an Eclipse admin client. A good tutorial on CM would be very much appreciated.
Using Log4J in Eclipse Equinox/OSGi
At 7:50 PM on Jul 31, 2007, Alex Blewitt
wrote:
You can obtain org.apache.log4j_1.2.13.v200706111418 from the Orbit Downloads page if you don't already have it installed. You'll also need the org.eclipse.osgi_3.3.0. v20070530.jar , which is available in an Eclipse 3.3 install too. This may likely work on different versions; but those links will enable you to run with the versions I've tested.
I'll assume that you know about Log4J already. If not, go over and read up about it. I'll wait.
Log4J works by obtaining loggers , which can either be globally shared or customised on a per-class basis, and then using the output from those to generate code to appenders . Thus, we need a way of configuring both of these. Typically, this is done by putting a
log4j.propertieson the classpath, but we don't have that luxury in an OSGi environment.Let's say we want to do some logging in our bundle when it starts. Our code might look like:
public class Activator implements BundleActivator { public void start(BundleContext context) throws Exception { Logger logger = Logger.getLogger(Activator.class); logger.info("Info starting"); logger.warn("Warning starting"); logger.error("Error starting"); } // stop method not shown }The idea is that we'll get the logger associated with our particular class, so that we can enable/disable logging for our class individually. (Alternatively, we might choose to use the bundle's symbolic name here if we want all logging for the bundle to be the same.) We're printing out a few different flavours of message, just to show what you can do.
We can compile and run this in the form of an OSGi bundle; it's attached below. Here's how you'd get it up and running, assuming that both Log4JDemo.jar and the org.eclipse.osgi/org.apache.log4j bundles are in the same directory:
Oops. That's not good. We get an annoying message from Log4J letting us know that it doesn't know what to do, so plays safe and ignores logging requests. We'd quite like it to do something else. We need to give it a configuration file like this:
Suffice to say, running with 'log4j.properties' in the current directory (or anywhere on the classpath) doesn't work, because OSGi manages a per-bundle classpath. What we need to do is provide log4j.properties for it to find.
One way of doing this would be to put a log4j.properties file into our Log4JDemo bundle. However, that's not very extendible, and we have to do all sorts of weird things to make that work. Instead, we can use OGSi fragments , which are like parasitic bundles. They infect the host bundle that they're attached to, but are only available when that bundle is running.
We can create a fragment by defining a special keyword in the fragment's manifest:
This says we need to attach to the org.apache.log4j bundle. At runtime, the log4j's bundle classpath is extended with the contents of our classpath (in this case, '.' by default) and so we can embed our log4j.properties file in this fragment. When Log4J asks for log4j.properties from the current bundle, it will fail to find it here and use our fragment's classpath, which will contain the log4j.properties entry. For convenience, I've attached Log4JProperties to this post as well.
Now we can run our app again, but this time, installing the Log4JProperties as well:
What happens if we want to change the level of the logging? We might want to change the default log level from 'debug' to 'error'. Well, all you need to do is edit the log4j.properties in the bundle and update it. Unfortunately, because it's a fragment, and the fragment is associated with the log4j bundle, you've got to refresh the packages for it to be re-bound, which will have the side effect of stopping/starting the Log4J classes:
osgi> update 2 osgi> ss Framework is launched. id State Bundle 0 ACTIVE org.eclipse.osgi_3.3.0.v20070530 1 ACTIVE Log4JDemo_1.0.0 2 INSTALLED Log4JProperties_0.0.0 3 RESOLVED org.apache.log4j_1.2.13.v200706111418 osgi> refresh 10 [OSGi Console] INFO log4jdemo.Activator - Info stopping 15 [OSGi Console] WARN log4jdemo.Activator - Warning stopping 16 [OSGi Console] ERROR log4jdemo.Activator - Error stopping 26 [OSGi Console] ERROR log4jdemo.Activator - Error starting osgi> ss id State Bundle 0 ACTIVE org.eclipse.osgi_3.3.0.v20070530 1 RESOLVED Log4JDemo_1.0.0 2 RESOLVED Log4JProperties_0.0.0 Master=3 3 RESOLVED org.apache.log4j_1.2.13.v200706111418 Fragments=2Obviously the timestamps will be different in your cases (as may the bundle identifiers) but it introduces the concept of fragments and how you can use them to separate out the configuration of the logging from the actual code itself. And of course, you can use many other logging formats and appenders that are provided with the Log4J infrastructure that we're not covering here.
The only problem with this approach is that it's not very modular. Log4J's property configurator (or XML configurator) uses a single file to configure everything. Next time, we'll look at the OSGi configuration admin service in conjunction with the Log4J configurators to see how we can make it more flexible and dynamic, without having to start and stop our application's bundles each time we want to change the logging level.
Until next time,
Alex.
5 replies so far (
Post your own)
Re: Using Log4J in Eclipse Equinox/OSGi
Ah, a good old trip down memory lane ...My name turned up in Category.java from a good few years ago
(In fact, the curious paleontologist in me found out that the comment was in there back in Log4J 1.0.4 , released in January 2001
Re: Using Log4J in Eclipse Equinox/OSGi
FYI, you might find Pax-Logging interesting:http://wiki.ops4j.org/confluence/display/ops4j/Pax+Logging
it's an attempt to tie the various logging APIs together with an implementation of the OSGi LogService, so developers can code to whatever API they're comfortable with.
the latest version allows configuration via the ConfigAdmin service.
Re: Using Log4J in Eclipse Equinox/OSGi
I think this is trying to sit on top of Log4J, and map OSGi Log files to it. What would be more interesting is to allow people to continue to use Log4J, but have it administered by CM. I'm not convinced that providing a Log4J emulator is really what's needed.Alex.
Re: Using Log4J in Eclipse Equinox/OSGi
actually it still uses Log4J as the final backend (configured using ConfigAdmin) but has the benefit of providing alternative APIs for bundles that were written to use other logging schemes.as a client of Pax-Logging you can use log4j, slf4j, jcl or even the LogService API and have your log output aggregated through the log4j backend.
Re: Using Log4J in Eclipse Equinox/OSGi
Alex, I'm very interested in your followup on CM. I've looked at it briefly and I'm interested in the possibility of using it to configure an OSGi server from an Eclipse admin client. A good tutorial on CM would be very much appreciated.