How to provide a log4j.properties to an OSGI Bundle (Eclipse-Plugin)? - java

Project setup:
Logging-1.0.jar
contains a Logger.class which uses slf4j/log4j
depends on slf4j-api.jar, slf4j-log4j.jar, log4j.jar
LoggingOSGI-1.0.jar
wraps the logging project
contains an Activator and MANIFEST.MF
lib/ contains logging-1.0.jar, slf4j-api.jar, slf4j-log4j.jar, log4j.jar
jars from lib/ are added to classpath and packages from logging-1.0.jar are exported
SomeBundle-1.2.jar
contains an Activator and MANIFEST.MF
has a dependency on LoggingOSGI-1.0.jar
Accessing the Logger class from SomeBundle works, but the logging project can't find the log4j.properties (log4j:WARN No appenders could be found for logger).
Questions:
Where do i have to place the log4j.properties?
Any ideas what i could try? (already tried: different directories, Eclipse-Buddies, -Dlog4j.configuration as VM argument)
Would be an extension point, which tells the logging project the location of the log4j.properties, a good solution?

When I last tried this around six years ago, the solution turned to be to create a fragment bundle with the log4j.properties file, and then to attach that fragment (via the Fragment-Host manifest header) to the bundle that loads the logging library ("Logging-1.0.jar," in your case). It felt like a lot of project structure, build time, and deployment overhead for what seems like such a simple goal.
See section 3.14 of the OSGi Service Platform Core Specification for more detail on fragment bundles.
An alternate idea is to consider using the Configuration Admin Service to designate the path to a logging configuration file on disk, outside of your bundles. That would require augmenting your logging library to look up a configuration (or, better, listen for one) and then pass that configuration through to the logging implementation.
I would also be remiss to not point out the OSGi Log Service, specified in section 101 of the OSGi Service Platform Service Compendium.

To solve my problem i added this code to the Activator of the LoggingOSGI-1.0 which configures log4j. The file path is taken from a System property: -Dlog4j.configuration=path/to/log4j.properties.
Still interested in other approaches or opinions to this solution.
private static final String LOG4J_CONFIG_KEY = "log4j.configuration";
public void start(BundleContext bundleContext) throws Exception {
Activator.context = bundleContext;
if (System.getProperties().containsKey(LOG4J_CONFIG_KEY)) {
String file = System.getProperties().getProperty(LOG4J_CONFIG_KEY);
PropertyConfigurator.configure(file);
}
}

Related

Replace java platform system logger with slf4j in spring boot application

I've got a spring boot application build as multi-modular gradle project (old-style, not fancy jigsaw)
What I want to achieve - is replace java platform loggers (e.g., SSlLogger/ System.getLogger) with sl4j & logback that are used in my app and are managed by spring-boot-admin server at runtime. I need all my loggers to write to file instead of console - or else I won't see logs in Logz.io.
I do not control how my app is deployed, but I control the way fat jar is built (so, manuals with terminal commands 'java - ...' are not very helpful :( )
I started to follow https://www.baeldung.com/java-9-logging-api guide, but got stuck.
for simplicity, my structure is ->
build.gradle
/application-module
build.gradle (combines 3 other modules)
/src /...
/rest-module
build.gradle
/src /...
/service-module
build.gradle
/src /...
/persistency-module
build.gradle
/src /...
So, I want to add one more module
/log-module
/src -> with actual classes
module-info.java
Slf4jLogger implements System.Logger
Slf4jLoggerFinder extends System.LoggerFinder
and include it into my application-module
but when trying to build it all, I get 'error: module not found: org.slf4j', and app is not build.
So, what am I doing wrong? What additional plugins/config do I need? And will it even allow me to achieve my goal?
Okay, I managed to find the solution. It's a combination of
https://www.baeldung.com/java-9-logging-api
https://www.baeldung.com/java-spi
So, I don't even needed jigsaw modules - only the JDK's service provider mechanism
So, in fact you need 3 files [these are id's of pastebin's samples; but they are almost the same as in the java-9-logging-api article]
AkXY3zgu -> adapter class
YFUkZwat -> logger provider
CD6NNibj -> meta-inf file - and that's the trickiest part (with file name :) )
file name -> META_INF/services/java.lang.System$LoggerFinder
com.my-projects.commons.logs.Slf4jLoggerFinder
An now on regular app startup system logger will be replaced with slf4j-adapter.
But still, check how system logger is created -> for example, I mostly need SSLLogger, and there is some system-prop-based logic there...

Does depedent project in java take log4j config of parent project?

I am having project A and project B, A has jar dependency of project B. I have defined log4j.xml in project A but I am not able to see logs of sub-project(B.jar) in file appender as well as tomcat server console. Does project B will take log4j.xml form parent project A or not then which config does it use?
There is one log4j config for your entire JVM (unless you're working in a containerized environment using class loaders and.... that's not what's described).
Missing log messages implies that the configuration from log4j either (a) isn't what you think it is (i.e. a different log4j.xml is being used) or (b) doesn't have the right settings for the missing log lines.
Adding the following to the JVM at startup may help:
-Dlog4j.debug
It may also be possible to browse the log4j settings via MBeans in jconsole.
If you want all apps (WAR files) in a Tomcat instance to have the same logging configs, the simple solution is to arrange that all WAR files have a copy of the same config file.
If you want the apps to share a common logging framework (with a single configuration), then you should consider using Context Selectors, as described in the Log4j 2 documentation.
Using Context Selectors
There are a few patterns for achieving the desired state of logging separation using ContextSelectors:
Place the logging jars in the container's classpath and set the system property log4j2.contextSelector to org.apache.logging.log4j.core.selector.BasicContextSelector. This will create a single LoggerContext using a single configuration that will be shared across all applications.
Place the logging jars in the container's classpath and use the default ClassLoaderContextSelector. Follow the instructions to initialize Log4j 2 in a web application. Each application can be configured to share the same configuration used at the container or can be individually configured. If status logging is set to debug in the configuration there will be output from when logging is initialized in the container and then again in each web application.
Follow the instructions to initialize Log4j 2 in a web application and set the system property or servlet context parameter log4j2.contextSelector to org.apache.logging.log4j.core.selector.JndiContextSelector. This will cause the container to use JNDI to locate each web application's LoggerContext. Be sure to set the isLog4jContextSelectorNamed context parameter to true and also set the log4jContextName and log4jConfiguration context parameters.
The exact method for setting system properties depends on the container. For Tomcat, edit $CATALINA_HOME/conf/catalina.properties. Consult the documentation for other web containers.
I don't think there is a direct equivalent in Log4j 1.x.

JCE security provider in OSGi

We have a problem using a custom JCA implementation in our OSGi bundle. The JCA implementation that we are forced to use by our customer leads to a class loader memory leak. This prevents the deployment and usage of it in our bundle, because we quickly run into a perm gen space problem.
The proposed solution from the JCA provider is to put the JAR in the jre/lib/ext folder, but it is not loaded from there. This is due, as far as I know, to the OSGi (Eclipse equinox) class loader policy to have the bootstrap classloader as the parent of each bundle classloader, which excludes the extension class loader that loads from the jre/lib/ext folder. I.e. no bundle ever sees anything in the jre/lib/ext folder.
Is there a way to get Eclipse equinox to load a jar that is registered as a security provider, only once, such that all bundles or a specific bundle can see that provider? The fact that the JCA library is not unloaded via OSGi could be tolerated in this instance.
You should edit the system packages and add the packages from the JCA-custom.jar.
You can define which packages should be included in several ways.
You create a profile for equinox and define the packages. You can find examples for each jdk versions in the eclipse.osgi jar. For example, look for JavaSE-1.6.profile and try finding the entry org.osgi.framework.system.packages
You can define it as a system variable when you start your OSGi container. The system variable is the same: -Dorg.osgi.framework.system.packages=package names separated by comma

How do I configure log4j logging for a jar?

I have a project A with log4j.jar on its build path. I have a number of classes that have logging statements in the form of:
Logger _log = Logger.getLogger(A.<>.class);
...
_log.info("...");
I am exporting the project as a jar into another project B. Project B already has its own log4j jar and it's own .xml configuration file. I want to configure particular classes from A to log to Console Apender at varying "levels". How do I do this, please?
Well, basically, you shouldn't do that. Think of it this way: if that would be done that way, each library that is included in any application would host its own logging configuration, very potentially overriding any of those that are in the application, and each other, in non-specified order. You wouldn't want that. So do not.
[In case you really, really want to do it, you could have properties file in the jar that could be overriden by an xml file in the main application. See details here. But don't. :) ]
In general, you don't. In general, you want to have one log4j configuration file. But i suppose you could load your configuration explicitly from your code, as described in the log4j documentation
Pick a version of log4j.jar, probably the newer one. You can only use one version. You need to merge project A's and project B's logging configuration. You could do this in log4j classes at runtime, but don't. Just bite the bullet and merge them once in source control.

Controlling the classpath in a servlet

My servlet application includes a number of library .jars, some of which contain embedded log4j.xml or log4j.properties files. I'd like to ensure that log4j finds my log4j.xml first! I've tried searching for some specification of the priorities of the various classpath elements in a servlet (e.g. does WEB-INF/classes always precede WEB-INF/lib?), or some way to configure or tweak the servlet's classloader so that a given resource directory appears early in the classpath. So far, I've drawn a blank. Any suggestions on ensuring that a servlet .war file loads the correct log4j.xml via the classloader?
Tomcat 8.5
Ditto Tomcat 8.0.
See documentation: Class Loader HOW-TO.
Tomcat 8.0
The answer is simple, taken from the Tomcat documentation page, Class Loader HOW-TO. In particular notice the use of the /WEB-INF/ directory/folder.
Therefore, from the perspective of a web application, class or resource loading looks in the following repositories, in this order:
Bootstrap classes of your JVM
/WEB-INF/classes of your web application
/WEB-INF/lib/*.jar of your web application
System class loader classes (described above)
Common class loader classes (described above)
If the web application class loader is configured with <Loader delegate="true"/> then the order becomes:
Bootstrap classes of your JVM
System class loader classes (described above)
Common class loader classes (described above)
/WEB-INF/classes of your web application
/WEB-INF/lib/*.jar of your web application
Tomcat 6
Excerpted from Tomcat 6 page, Class Loader HOW-TO.
Therefore, from the perspective of a web application, class or resource loading looks in the following repositories, in this order:
Bootstrap classes of your JVM
System class loader classes (described above)
/WEB-INF/classes of your web application
/WEB-INF/lib/*.jar of your web application
$CATALINA_HOME/lib
$CATALINA_HOME/lib/*.jar
As far as I understand the resource selection from the classpath is non-deterministic (from the point of view of the app developer). Even if the same file is loaded consistently the behaviour could change:
1. When you upgrade the version of your current container.
2. If you switch containers.
The simplest solution will be to remove embedded log4j config files from library jars. It is almost never a good idea to embed log4j config's as it leads to the problem you are seeing here...
Are they third party jars or jars you developed?
We the Spring Log4jConfigListener in our web.xml file.
You can specify as a context parameter the location of the log4j config file, i.e. you could set it as /WEB-INF/log4j.xml
Would this be an option for you? If you're not using Spring I know that you can set the Log4j location programatically which might also work.
In my experience, WEB-INF/classes typically takes precedence over jars in WEB-INF/lib, however, that also depends on the servlet container you use (I could never figure out the behavior of JRun, for instance). It would help immensely if you could tell me which container you're using.
Also, are you certain that the offending log4j configuration is in a jar in WEB-INF/lib? Typically, when I've run into classpath problems in a servlet container situation, it's because of libraries that reside outside of the web app.
The servlet specs recommend that web app classloaders load their own classes before delegating to the container's classloader (SRV.9.7.2), but since this is counter to the Java spec, not all vendors do this by default (in fact Tomcat is the only container I've used that does this by default). With that said, it's always possible to configure your container's web app classloading behavior. If you tell me which container you're using, I may be able to help you (specifically, I have done this successfully before on WebLogic, WebSphere, Glassfish and JRun)).
If you're unable to control the classpath, since Tomcat is setting it for you, are you at least able to set a system property for log4j.configuration? I believe that location pointed to by that property can be set outside of the classpath.
If not, another approach, although an ugly one, would be to explicitly run one of the configurators yourself in your application code.
You need to have log4j.properties in your CLASSPATH. The best place is under WEB-INF/classes.
You also have to make sure that you use your version of log4j.jar. So, put it in WEB-INF/lib, just to make sure you are not using one from tomcat folders, since it may cause strange classloading issues.

Categories