Forum Controls
Spotlight Features

The Rich Engineering Heritage Behind Dependency Injection

Andrew McVeigh takes us on a tour of the rich heritage behind dependency injection, what it represents, and tells us why its here to stay.

Java, the OLPC, and community responsibility

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!
Replies: 5 - Pages: 1  
Threads: [ Previous | Next ]
  Click to reply to this thread Reply

Using Log4J in Eclipse Equinox/OSGi

At 7:50 PM on Jul 31, 2007, Alex Blewitt DeveloperZone Top 100 wrote:

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.

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.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:

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:

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:

Bundle-SymbolicName: Log4JProperties
Fragment-Host: org.apache.log4j


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.

Until next time,

Alex.
  Click to reply to this thread Reply
1. At 8:06 PM on Jul 31, 2007, Alex Blewitt DeveloperZone Top 100 wrote:

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 :-) Doesn't time fly? Putting it into perspective, that's about 10 months before Eclipse 1.0 hit the shelves.
  Click to reply to this thread Reply
2. At 11:10 AM on Aug 1, 2007, Stuart McCulloch Javalobby Newcomers wrote:

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.
  Click to reply to this thread Reply
3. At 1:13 PM on Aug 1, 2007, Alex Blewitt DeveloperZone Top 100 wrote:

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.
  Click to reply to this thread Reply
4. At 11:10 PM on Aug 1, 2007, Stuart McCulloch Javalobby Newcomers wrote:

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.
  Click to reply to this thread Reply
5. At 12:01 PM on Sep 22, 2007, Bryan Hunt Blooming Javalobby Member wrote:

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.

thread.rss_message