Effectively available log appenders? (SLF4J vs. Log4j2 vs. OSGi vs. OPS4J/pax-logging)

Hi guys,

I’m currently struggling to find out which log appenders are effectively supported by OH3.

Looking at the docs I find a reference to SLF4J which, from what I can tell, should be obsolete by now as it supports Log4j1 only. Yet OH3 seems to be using Log4j2 nowadays, as hinted at by the use of the log4j2.xml config file.

I figured, awesome, I can use the ZeroMQ appender. After I added the required JeroMQ JAR to the runtime and configured the log appender I noticed that there’s another abstraction layer involved which turned out to be incompatible. After investigating the involved codebases for some time, I think I could confirm that the effective OSGi implementation only covers the barebones of Log4j2. In fact, pax-logging not only doesn’t cover any most of the interesting appenders that give Log4j2 an edge over other implementations, it even has missed the ZeroMQ appender entirely so far. One could even argue that pax-logging not keeping up with with Log4j2’s 2018 refactorings is a serious regression for OH.

I wonder, am I correct with my analysis? Is there any way around that OSGi layer and use Log4j2 directly? Which log appenders are effectively available…?

Cheers

For reference: I’m not the only one apparently.

Update: I did some more digging…

Despite the upstream issue stating a fix version of 2.11.0 and a corresponding commit in 2018, the ZeroMQ appender is still part of Log4j2’s core since that very commit hasn’t yet made it into an actual release - duh! Apparently that’s going to be the case with version 3.0 as the associated epic hints at.

That means the “incompatibility” (a ClassCastException) I mentioned above is the real culprit here. In fact that seems to be an issue (a missing interface implementation, breaking compatibility) in pax-logging for which I’m going to file a bug report upstream.

And to answer my main question: these should be the currently still available appenders.

Nevertheless, I’d still like to know if there’s a way around pax-logging such that one can use Log4j2 directly…

Cheers

I am not up to date with appenders available in log4j2 but I can tell you that it is never ending story. Attaching a new appender under osgi requires a bit of effort.

If it happens that appender you would like is missing then there are two ways.

  1. create a fragment bundle which is attached to pax-log4j2 bundle (you need a Fragment-Host header) with all classes your appender needs. This should make configuration working as expected.
  2. create an osgi service which will catch events and do whenever is necessary. In config you can find OsgiVmAppender (or similar) which is not declared nor explicitly configured in config file. I also must note that I do not recall exact procedure here.

Since pax does re-packaging of log4j it might happen that some classes are missing. Overall adding third party appender will always involve repackaging or additional work on person who wants to use it.

Thats theoretical part, now practical one. Do you have a stack trace or sample config I could test locally?

Thanks for the details Łukasz.

Please note that my original issue wasn’t about a new third party appender but about Log4j2’s existing ZeroMQ appender. Pax-logging just got out of sync with upstream (back in 2014!) and I already filed a bug report with them.

In my opinion this is a prime example where “standardizing” abstraction layers like OSGi just do more harm than good. Log4j2 gained a lot of capabilities but the pax-logging abstraction cripples those for no good reason - as far as I can tell.

That said, I’d be interested to know if one can somehow forgo pax-logging within OH3. IOW, is it possible to use Log4j2 directly?

Cheers

Update: pax-logging couldn’t resolve the issue by itself and forwarded it further upstream to Apache’s Log4j2.

Since this could take a while I’m going to give the custom appender approach a try…

I don’t want to drift too far off-topic here, so just a quick follow-up question for you @splatch: I built and deployed said fragment bundle and

  • Karaf shows it with a status of Resolved
  • The fragment host (org.ops4j.pax.logging.pax-logging-log4j2) shows my fragment’s class among its classes (via bundle:classes)

Is there anything else I need to take care of in terms of integrating my fragment bundle? I’m still getting an error when I add my appender to log4j2.xml:

karaf[354]: org.ops4j.pax.logging.pax-logging-api [log4j2] ERROR : Appenders contains an invalid element or attribute "MyCustomAppender" Ignored FQCN: org.apache.logging.log4j.spi.AbstractLogger

I just want to make sure I got at least the integration part right. If you could confirm that I’d focus on the code itself for further troubleshooting…

Thanks!

@brevilo it is not an off-topic, it is really interesting area. Whether it works or not depends on what you packaged into your fragment. Fragment bundle can not override classes in “source” bundle. It can only add new ones and new resources (ie. translations).

The “resolved” state is highest you can get for fragment. it really means that it is “attached” to host bundle and, depending on lookup method, should be visible to “mother” module. From error message you shared problem seems to still be in completely different place.

Do you have sources of your project somewhere so I could test and help you fixing it?

Best,
Łukasz

Yep, it’s just about one additional class.

That’s what I figured. Having it listed among the fragment host’s classes should infer availability. Are there any other methods to validate the fragment bundle?

Why? As far as I can tell a custom appender is provided as a Log4j2 plugin under a defined name (here: “MyCustomAppender”). You then add it as a child of the <Appenders> element in etc/log4j2.xml, e.g.: <MyCustomAppender name="FOO"/>.

The error message just means (to me) that the new appender element wasn’t recognized which would be the case if the fragment isn’t yet integrated correctly (see above, caching maybe?) or the class’s code is still flawed.

Nope, but it’s more or less identical to one of the official example fragments (just ignore the BlockingQueue stuff). The Plugin annotation defines the element name, which would be “List” in that case.

Maybe I should try the example itself first, to see if that works at all :wink:

Yeah, it works!

Quick update: I think my fragment bundle lacked the serialized plugin listing file (Log4j2Plugins.dat) that should have been created automatically. Since that didn’t work for some reason I added the maven-compiler-plugin snippet listed in Log4j2’s plugin documentation to have the annotation processor generate it explicitly.

I changed a few other minor bits and pieces I need to check again. I’ll report back as soon as I have all the details.

Cheers :beers:

Two more details:

  1. The maven-bundle-plugin instructions also need an explicit Include-Resource to ensure the generated Log4j2Plugins.dat is actually included in the fragment bundle:
    <Include-Resource>${project.build.directory}/classes</Include-Resource>
  2. When you deploy a new fragment bundle update, you need to run the following (an OH restart isn’t sufficient!):
    1. bundle:update <symbolic-name>
    2. bundle:refresh

Hope this helps!