As the ServiceMix/Karaf users know, the Pax Logging framework used in Karaf integrates nicely with Apache Log4J out of the box. In fact the Karaf logging configuration file located in etc/org.ops4j.pax.logging.cfg uses Log4J configuration syntax.
Log4J integration comes out of the box in Karad, the Pax Logging Service OSGi bundle includes the core Log4J classes, so that the default Log4J appenders are supported.
Now there are loads of additional appenders for Log4J available (a non-exhaustive list is here). One such appender is the Log4J Nagios appender, which pushes logging messages to Nagios (via an NSCA server).
I have never used Nagios before but had the task to get this Nagios appender working in Fuse ESB Enterprise 7.0.2. Fuse ESB is based on Apache ServiceMix, so the outlined solution applies to ServiceMix 4.x and Karaf as well.
As things were not dead-simple I take the time to document my solution so that it can hopefully save someone else's time.
To install and setup Nagios, I fired up a Ubuntu VM and installed the nagios and nsca packages using the Ubuntu package manager. With regards to configuring Nagios I followed this article and this document explaining the NSCA server setup. Although I had no previous knowledge on Nagios, I got it setup and running within an hour thanks to the referenced articles.
Turning back to ServiceMix/Karaf. The Nagios Log4J appender comes as a plain jar file (not OSGi enabled). A possible Nagios Log4j configuration is given at the end of this post.
The problem is that logging is performed by the Pax Logging Service in Karaf. So how do you tell the pax-logging-service system bundle that it should also load the Nagios Log4J appender from a different jar file deployed into Karaf?
There is probably other ways to resolve this but I found it easiest to use the OSGi fragment bundle concept.
In OSGi there is the concept of a fragment bundle. From the OSGi Wiki:
"A Bundle fragment, or simply a fragment, is a bundle whose contents are made available to another bundle (the fragment host). Importantly, fragments share the classloader of their parent bundle."
Its important to note that fragments use the classloader of their parent or host bundle.
By using a fragment bundle you can extend the classes that can be loaded by the host bundle, without having to modify the OSGi Import-Package list.
With respect to my use case this means: By making the Nagios Lo4J jar file a fragment bundle of the Pax Logging Service bundle, the Pax Logging Service bundle will be able to load the Nagios Log4J appender classes and send logging statements to the Nagios NSCA server.
The Nagios Log4J jar does not contain any OSGi metadata, so I had to manually add these. I extracted the jar file and modified META-INF/MANIFEST.MF to contain these headers
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.6.0_13-b03 (Sun Microsystems Inc.)
Bundle-Name: log4j-nagios
Bundle-SymbolicName: org.apache.log4j.nagios
Bundle-Version: 2.0.0
Bundle-ManifestVersion: 2
Fragment-Host: org.ops4j.pax.logging.pax-logging-service
Export-Package: org.apache.log4j.nagios;version="2.0.0"
Notice the Fragment-Host header, it sets the host to the pax-logging-service OSGi bundle.
Further there is no need to define an Import-Package list as all required Log4J classes will be made available by the host bundle.
I then rebuild the jar file and named it log4j-nagios-appender-2.0.0.osgi.jar.
If you don't want to run these steps manually yourself, you can download the OSGi enabled jar file using the above link.
Deploying this new jar is easy. The perhaps simplest form is to start with a fresh container (having no or an empty data/ folder).
Assuming etc/org.ops4j.pax.logging.cfg already configures for Nagios logging (see example config below) you can simply copy log4j-nagios-appender-2.0.0.osgi.jar to the ServiceMix deploy/ folder and startup ServiceMix.
It may raise the following exception on the first startup
java.lang.ClassNotFoundException: org.apache.log4j.nagios.NagiosAppender not found
by org.ops4j.pax.logging.pax-logging-service [3]
but you can ignore that. Because the pax-logging-service bundle was started before the fragment Nagios Log4J bundl, Pax Logging is not able to load the Nagios appender right at startup. However when the Nagios Log4J fragment bundle attaches to the pax-logging-service, the Nagios appender classes will get loaded and logging via that appender will start. Messages will get pushed to the NSCA server.
On subsequent restarts of Karaf the bundles are already wired together (i.e. the pax-logging-service knows there is a fragment bundle), so this exception will not be raised anymore.
Hope this helps.
Example org.ops4j.pax.logging.cfg configuration using Nagios appender:
################################################################################
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################
# Root logger
log4j.rootLogger=INFO, out, osgi:* , NAGIOS
log4j.throwableRenderer=org.apache.log4j.OsgiThrowableRenderer
# CONSOLE appender not used by default
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n
# File appender
log4j.appender.out=org.apache.log4j.RollingFileAppender
log4j.appender.out.layout=org.apache.log4j.PatternLayout
log4j.appender.out.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n
log4j.appender.out.file=${karaf.data}/log/karaf.log
log4j.appender.out.append=true
log4j.appender.out.maxFileSize=1MB
log4j.appender.out.maxBackupIndex=10
# Sift appender
log4j.appender.sift=org.apache.log4j.sift.MDCSiftingAppender
log4j.appender.sift.key=bundle.name
log4j.appender.sift.default=karaf
log4j.appender.sift.appender=org.apache.log4j.FileAppender
log4j.appender.sift.appender.layout=org.apache.log4j.PatternLayout
log4j.appender.sift.appender.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %m%n
log4j.appender.sift.appender.file=${karaf.data}/log/$\\{bundle.name\\}.log
log4j.appender.sift.appender.append=true
# Nagios Log4J configuration
# ------------------------------------------------------------
# set the appender for Nagios
log4j.appender.NAGIOS=org.apache.log4j.nagios.NagiosAppender
# Nagios configurations
log4j.appender.NAGIOS.Host=192.168.178.44
log4j.appender.NAGIOS.Port=5667
log4j.appender.NAGIOS.ServiceNameDefault=FuseESB
log4j.appender.NAGIOS.MDCCanonicalHostNameKey=nagios_canonical_hostname
# It may be required to set a Nagios config file if non-default
# data encryption algorithms are used.
log4j.appender.NAGIOS.ConfigFile=/opt/fuse/SMX/fuse-esb-7.0.2.fuse-097/send_nsca.cfg
# mapping warning levels.
log4j.appender.NAGIOS.Log4j_Level_INFO=NAGIOS_OK
log4j.appender.NAGIOS.Log4j_Level_WARN=NAGIOS_WARN
log4j.appender.NAGIOS.Log4j_Level_ERROR=NAGIOS_CRITICAL
log4j.appender.NAGIOS.Log4j_Level_FATAL=NAGIOS_CRITICAL
# set the layout for appender Nagios
log4j.appender.NAGIOS.layout=org.apache.log4j.PatternLayout
log4j.appender.NAGIOS.layout.conversionPattern=server: %X{nagios_canonical_hostname}: %m%n
5 comments:
Good one. was very useful.
My use case was similar to that of what you have explained here.
I have an OSGI bundle (main-bnd) which i own and it needs depends on couple of jars (lib1.jar, lib2.jar). I made those jars as framents (just dropped those jars in deploy folder) and had import statement in main-bnd. The import statements are resolved but when the main-bnd starts it throws error stating ClassNotFoundException for one of the classes in the fragments.
can u throw some light on the class loading in these case.
BR,
Rajesh
Hello Rajesh,
The following blog post may help you as well: http://freemanfang.blogspot.it/2012/03/how-to-use-jdbc-driver-in-osgi.html
It deals with a similar topic.
If your main bundle only depends on the classes provided in lib1.jar and lib2.jar, then you should get away with
- deploying lib1.jar and lib2.jar and ensuring they export the java packages that your main bundle depends on
- explicitly import the required packages from lib1.jar and lib2.jar in your main bundle's OSGi header information.
In such case there is typically no need for defining a fragment bundle.
Also, when you get such ClassNotFoundException, try a dev:dynamic-imports on the main bundle and refresh and restart that bundle. Chances are good that your dependencies got resolved.
I tried the way you suggested.
I explicitly imported the needed packages from lib1.jar and lib2.jar.
Here i dont have a control on to export specific packages in lib1.jar and lib2.jar. These jars are not in my control. I can only use them as lib.
With this ran into one more problem, or i can say i got one more hint to my earlier problem
The problem is that there is package say com.prod.test which i am importing in main-bnd, this packages is available in BOTH the jars, and they contain a different set of classes under them !!!!
actually there are a couple of packages which are of the same name in both the libs..
How can i get both classes from both the packages (from lib1.jar and lib2.jar) exported ?
Oops ... quite a big text !
Cheers
Rajesh
If both lib1.jar and lib2.jar export the same packages and the same version of these packages and if these jars are outside of your control, then you may want to consider combining the classes of both jar files into one big jar. That should resolve the problem as well.
Thanks Mielke, that solves the problem.
But only thing is that updating only one would not be possible now. Even though there is change in one jar we need to do a repack of both the jars in to one jar and that has to be deployed.
Cheers
Rajesh
Post a Comment