ops4j / org.ops4j.pax.logging

The OSGi Logging framework implementation. Supports SLF4J,LOG4J,JCL etc.
https://ops4j1.jira.com/wiki/spaces/paxlogging/overview
Apache License 2.0
46 stars 79 forks source link

[Log4j2] Programmatic configuration failed [PAXLOGGING-237] #307

Closed ops4j-issues closed 7 years ago

ops4j-issues commented 7 years ago

Alexey Markevich created PAXLOGGING-237

Trying to check appender from code cause [1] in console and [2] in logs

  1. ERROR No log4j2 configuration file found. Using default configuration: logging only errors to the console. Set system property 'org.apache.logging.log4j.simplelog.StatusLogger.level' to TRACE to show Log4j2 internal initialization logging.

  2. config.getAppenders() =

    {DefaultConsole-4=DefaultConsole-4}


Affects: 1.10.0 Attachments:

Votes: 0, Watches: 4

ops4j-issues commented 7 years ago

Alexey Markevich commented

Initial Karaf issue: https://issues.apache.org/jira/browse/KARAF-5101

ops4j-issues commented 7 years ago

NiclasH commented

Observations from the peanut gallery...

  1. If you want to use a Log4J version 2 backend, then you should not use the pax-logging-service, which is the Log4J version 1 backend.

  2. For people looking at solving this; My guess is that Log4J2 is getting triggered "too early", for instance by the Log4J class in Activator.

ops4j-issues commented 7 years ago

Achim Nierbeck commented

For a log4j2 backend we do have the pax logging log4j2 bundle

ops4j-issues commented 7 years ago

NiclasH commented

Yes, he understood that, but both -servce and -log4j2 bundles are referenced in the pom.xml of the testcase.

ops4j-issues commented 7 years ago

Alexey Markevich commented

pax-logging-service can be safely removed; I took pax-logging-it as input and just add pax-logging-log4j2 dep

ops4j-issues commented 7 years ago

Grzegorz Grzybek commented

Alexey Markevich I'm looking at your example and I see you're using convenience method:

org.apache.logging.log4j.core.LoggerContext#getContext(boolean)

It Avoids the type cast for:

(org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(currentContext);

The problem is that org.apache.logging.log4j.LogManager#getContext(boolean) method is declared to return org.apache.logging.log4j.spi.LoggerContext interface and indeed, org.apache.logging.log4j.core.LoggerContext implements this SPI interfaces.

But in pax-logging-log4j2, org.ops4j.pax.logging.log4jv2.Log4jv2LoggerContext is used as org.apache.logging.log4j.spi.LoggerContext implementation and the hierarchy is:

org.apache.logging.log4j.spi.LoggerContext
+-- org.apache.logging.log4j.core.LoggerContext
|   +-- org.apache.logging.log4j.core.async.AsyncLoggerContext
+-- org.apache.logging.log4j.simple.SimpleLoggerContext
+-- org.ops4j.pax.logging.log4jv2.Log4jv2LoggerContext

so, org.ops4j.pax.logging.log4jv2.Log4jv2LoggerContext is not an instance of org.apache.logging.log4j.core.LoggerContext and you're getting ClassCastException...

ops4j-issues commented 7 years ago

Grzegorz Grzybek commented

There's no obvious way to access org.apache.logging.log4j.core.config.Configuration as you want to do with the call to:

final org.apache.logging.log4j.core.config.Configuration config = LoggerContext.getContext(false).getConfiguration();

org.apache.logging.log4j.core.config.Configuration object is stored inside org.ops4j.pax.logging.log4j2.internal.PaxLoggingServiceImpl#m_log4jContext field, which is private and doesn't have getter.

ops4j-issues commented 7 years ago

Grzegorz Grzybek commented

Another portion of OSGi magic.

In PAX-EXAM, when org.ops4j.pax.exam.ExamSystem is created, you have several options configured using org.ops4j.pax.exam.ConfigurationManager. By default you can have exam.properties file in root of your CLASSPATH or you can point to such file using pax.exam.configuration System property.

In this file you can configure pax.exam.system property with values:

  1. org.ops4j.pax.exam.Constants#EXAM_SYSTEM_DEFAULT ("default")
  2. org.ops4j.pax.exam.Constants#EXAM_SYSTEM_JAVAEE ("javaee")
  3. any other value

In case of 3), you'll get system configured using org.ops4j.pax.exam.spi.PaxExamRuntime#defaultTestSystemOptions() with several predefined options (among others - pax-logging-api bundle!)

I changed slightly your example:

// added implicitly by pax-exam, if pax.exam.system=test // these resources are provided inside org.ops4j.pax.exam:pax-exam-link-mvn jar url("link:classpath:META-INF/links/org.ops4j.base.link").startLevel(START_LEVEL_SYSTEM_BUNDLES), url("link:classpath:META-INF/links/org.ops4j.pax.swissbox.core.link").startLevel(START_LEVEL_SYSTEM_BUNDLES), url("link:classpath:META-INF/links/org.ops4j.pax.swissbox.extender.link").startLevel(START_LEVEL_SYSTEM_BUNDLES), url("link:classpath:META-INF/links/org.ops4j.pax.swissbox.framework.link").startLevel(START_LEVEL_SYSTEM_BUNDLES), url("link:classpath:META-INF/links/org.ops4j.pax.swissbox.lifecycle.link").startLevel(START_LEVEL_SYSTEM_BUNDLES), url("link:classpath:META-INF/links/org.ops4j.pax.swissbox.tracker.link").startLevel(START_LEVEL_SYSTEM_BUNDLES), url("link:classpath:META-INF/links/org.ops4j.pax.exam.link").startLevel(START_LEVEL_SYSTEM_BUNDLES), url("link:classpath:META-INF/links/org.ops4j.pax.exam.inject.link").startLevel(START_LEVEL_SYSTEM_BUNDLES), url("link:classpath:META-INF/links/org.ops4j.pax.extender.service.link").startLevel(START_LEVEL_SYSTEM_BUNDLES),

// configadmin should start before org.ops4j.pax.logging.pax-logging-log4j2 linkBundle("org.apache.felix.configadmin").startLevel(START_LEVEL_SYSTEM_BUNDLES),

// added implicitly by pax-exam, if pax.exam.system=test // - url("link:classpath:META-INF/links/org.ops4j.pax.logging.api.link").startLevel( START_LEVEL_SYSTEM_BUNDLES), // - url("link:classpath:META-INF/links/org.osgi.compendium.link").startLevel( START_LEVEL_SYSTEM_BUNDLES), // but we will use versions aligned to pax-web: url("link:classpath:META-INF/links/org.apache.geronimo.specs.atinject.link").startLevel(START_LEVEL_SYSTEM_BUNDLES),

CoreOptions.junitBundles(), mavenBundle().artifactId("pax-logging-api").groupId("org.ops4j.pax.logging").version("1.10.1-SNAPSHOT"), mavenBundle().artifactId("pax-logging-log4j2").groupId("org.ops4j.pax.logging").version("1.10.1-SNAPSHOT")


* these `url("link:classpath:META-INF/links/...` will be resolved using resources embedded inside `pax-exam-link-mvn` bundle
* this `linkBundle("org.apache.felix.configadmin")` comes from explicit resource inside `src/test/resources/org.apache.felix.configadmin.link`, which contains (configadmin is needed for pax-logging-log4j2): 

mvn:org.apache.felix/org.apache.felix.configadmin/1.8.14


* in order to be able to use property placeholders, you can't have just `log4j2.property.pattern` and use {{log4j2.appender.console.layout.pattern = $ {log4j2.pattern} 
}}, because prefix `log4j2.` is stripped and `org.apache.logging.log4j.core.config.properties.PropertiesConfigurationBuilder#build` detects generic properties to be prefixed by `property.`, so you have to have:

Common pattern layout for appenders

log4j2.property.pattern = %d{ISO8601} | %-5p | %-16t | %-32c{1} | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n … log4j2.appender.console.layout.pattern = ${pattern}



This way you'll have working example
ops4j-issues commented 7 years ago

Grzegorz Grzybek commented

Ah - and shorter explanation - if you use default pax-exam configuration, which explicitly adds pax-logging-api bundle, with mavenBundle().artifactId("pax-logging-api").groupId("org.ops4j.pax.logging").version("1.10.0"), you'll have two pax-logging-api bundles and your test class will configure the latter one, while loggers will come/be configured using the first one - that's why you saw failed test.