Hi,

Maybe I add some technical explanation which might clear things up a little.

Pax-Logging essentially consists of two parts, a frontend that implements 
different logging APIs (slf4j, OSGi logging service, log4j2, apache commons 
logging…) and a backend, which is doing the actual logging and which is 
configured with the karaf logging configuration. There are two backends,  one 
using log4j2 (the default) and one using logback, which means that one of these 
log frameworks is actually there in the backend, but the classes are usually 
hidden (and the code that is accessing them will depend on the backend).

There are actually three different kinds of code that may interact with loggers:

  1.  Code that is using one of the logging APIs to write logs.
  2.  Code that is extending the logger e.g. by a custom extender to write 
specific log formats that are not supported out of the box
  3.  Code that does both and may also configure loggers but also extends 
loggers and writes logs

Code of the first type is simple, It should work out of the box.

Code of the second type should go into a fragment of the pax-logging backend. 
The fragment is an extension of the actual backend bundle. It has full access 
to all backend classes (e.g. log4j2) classes in the fragment are also 
accessible from the backend (this is important if you have your own extender 
and want to configure it in the log configuration of the Karaf container).

Code of the 3rd type is problematic, and you might have to refactor it. The 
fragment that is exporting the internal backend classes as Grzegorz mentioned 
might help you in some cases, but this means that your classes can access the 
log4j classes, but log4j cannot access your classes (which means that if your 
code contains an appender, the appender would theoretically work, but log4j 
cannot instantiate it.

If I were you, I would remove the log4j specific code of the second and third 
type for now (of course you should keep the code that is writing logs in the 
first place). This means that the logs you are writing may not have the format 
or the log levels you are intending but this should not impact the general 
functionality.
Once the code is running you can consider log configuration and custom 
appenders and add them to a fragment (and the container log configuration).

Best regards
Stephan

From: Grzegorz Grzybek <gr.grzy...@gmail.com>
Sent: Thursday, 14 March 2024 07:07
To: user@karaf.apache.org
Subject: Re: Karaf log4j conflict

Hello

So, for this fragment, do I simply need to make a fragment containing the extra 
log4j information, or do I need to extract the code that is using those log4j 
pieces and put that into a fragment. That seems to be a much taller order. 
Compared to bolting the necessary log4j.core classes onto the pax bundle so 
that those classes are then available to everyone from the pax bundles 
classloader.

First - here's a sample bundle fragment (attached to pax-logging-log4j2) that 
adds custom appender: 
https://github.com/ops4j/org.ops4j.pax.logging/tree/main/pax-logging-samples/fragment-log4j2

For technical details you have two options (writing without actually checking, 
but I did it many times):

  *   you create a bundle fragment (with 
<Fragment-Host>org.ops4j.pax.logging.pax-logging-log4j2</Fragment-Host>) that 
simply uses all private package from Log4j2, which are included (but not 
exported) from pax-logging-log4j2. You can implement anything you want, 
including appenders, layouts, ...
  *   you create a bundle fragment (again, with 
<Fragment-Host>org.ops4j.pax.logging.pax-logging-log4j2</Fragment-Host>) which 
DOESN'T include any code, but in it's MANIFEST.MF you export the packages you 
need (like "org.apache.logging.log4j.core") and then your 
"caterwaul-connect-core" should be resolved against pax-logging-log4j2 with the 
attached fragment (because effectively, now pax-logging-log4j2 exports what you 
need). AND YOU DON'T install any Log4j2 bundles at all (just pax-logging-log4j2)
The 2nd approach look (maybe) more clever and I used it to make Jetty 9 bundles 
work with servlet-api 4 bundle (because a fragment added additional exports 
faking that servlet api 4 exports version 3 of the packages).

I hope this helps.

regards
Grzegorz Grzybek

śr., 13 mar 2024 o 19:00 Will Hartung 
<willhart...@gmail.com<mailto:willhart...@gmail.com>> napisał(a):


On Wed, Mar 13, 2024 at 10:35 AM Jean-Baptiste Onofré 
<j...@nanthrax.net<mailto:j...@nanthrax.net>> wrote:
Hi Will

Did you take a look on
https://github.com/apache/karaf/tree/main/examples/karaf-log-appender-example
?

Generally speaking, the org.apache.logging* packages should be
imported in your bundle. The fragment would extend an existing bundle
classloader with your classes.

Maybe if you share a test case bundle, I can show you how to do this.

I'm trying to port an existing code base, originally organized as some Maven 
modules. I'm treating the individual jars independently,  and making them 
bundles individually. One of the bundles has a Log4j appender class of its own, 
this is in contrast to the example you posted which seems to be a specific pax 
appender.

There's also some other code. It looks like this:

    @Override
    public IELogLevel toIELogLevel(Object rawLogEvent) {
        String logEventClsName = rawLogEvent.getClass().getName();
        switch(logEventClsName) {
            case "org.apache.log4j.spi.LoggingEvent":
                LoggingEvent loggingEvent = (LoggingEvent)rawLogEvent;
                return IELogLevel.valueOf(loggingEvent.getLevel().toString());
            case "org.apache.logging.log4j.core.LogEvent":
            case "org.apache.logging.log4j.core.impl.Log4jLogEvent":
                LogEvent logEvent = (LogEvent)rawLogEvent;
                return 
IELogLevel.valueOf(logEvent.getLevel().getStandardLevel().toString());
            default:
                System.out.printf("Unrecognized log event class flavor '%s'. 
Returning INFO%n", logEventClsName);
                return IELogLevel.INFO;
        }
    }

This is not my code, as in I'm not intimately familiar with it at this level, 
I'm trying to just get it all bundled up to run in an OSGI container. I 
honestly don't know what calls this at the moment. Then come back to make some 
passes with declarative services and things like that. But out the gate, just 
trying to get the thing firing on a few cylinders.

So, I'm not ready to try and break code out if I don't have to, if I can get by 
with classpath/bundlepath shenanigans to get the baseline running.

Can a fragment solve this? It seems to me a fragment extends the classloader of 
another bundle. It inserts itself into another bundles internal environment. In 
that, should it be trying to export the log4j classes from the pax bundle 
(which i don't even know if it has these classes internally, I don't know how 
pax logging works)?

Just not sure what the role a fragment might fill or how that would work at 
this juncture.

Thanks again.

Regards,

Will Hartung



Reply via email to