Strategies for logging to application logs from library code? - java

Where I work we use Log4j for web application logging. The log4j.jar is at the application level, not the container level. We're using a daily rolling file appender. Our log4j.properties files define appenders based on the app package name, so only classes in our application's package and below get logged to our app's log file.
In my application, I'm extending our framework with some supporting classes. These classes are not in the application's package, as they are not exclusive to my application and will eventually be made into a jar library for use with other applications. Because of this, my logging statements are not picked up by my application's appender, and are thus not logged to my application's log files.
I want to allow the classes in my jar to log to the log file of the application using the classes. However, if I create an appender in my application's log4j properties file based on my classnames, I suspect that when multiple applications are using my jar, because of the identical class names in the log4j.properties files, only one application log file will receive my jar's logging statements, and that it will receive ALL the logging statements from EVERY application using that jar. I think this is the case, since we're using a static Logger.getLogger() call to retrieve the logger.
I first want to know if my fears are valid, if this is really what would happen when multiple web applications in the same or different containers are using my jar simultaneously.
I'd also like to know if there are "boundaries" on which this behavior changes. For example, does this problem exist regardless of whether log4j.jar is a container-level jar or an app-level jar, or if each container is running in a separate JVM?
Lastly, if this IS the case, I'd like to know what strategies I should use to overcome the problem.
Thanks in advance!

If log4j.jar is only in the web-app then the logs will stay separate so each web-app should have its own log4j.jar and log4j.properties so that all logs stay separate.

The problem is that the preffered pattern for log4j is to use static *Configurator methods, which don't go very well with application containers.
This article solved this problem for me when i came across it ...

Related

How to have separate log4j config for library jar

We are developing client library which will be distributed as jar to other user application who will in turn create runnable application.
The problem is we want to setup log4j separately for this jar so that issues can be logged correctly. As this is library we can not assume user will always use log4j they might use some other logging framework or may not initialize log4j as we want. In short we want to configure log4j separately for our library jar. Please suggest way to do this.

Can a JAR shared by two web applications log to the same file in Tomcat?

Let's say we have two web applications and a Tomcat instance loading shared JARs (depedencies for both applications) from an external directory (via the means of shared.loader defined in catalina.properties). Therefore, these dependencies are not packaged into the WAR files.
Let's also say that:
Both web applications depend on a particular shared JAR file, which uses a logging framework (log4j2 at the moment but that's not required).
Both web applications use a logging framework of their own (we don't care whether they are identical or not, as long as things work as expected), and different logging configurations.
What we would like to achieve is for the shared JAR to reliably log to the same file, regardless of which web application its methods are called. To our understanding, both web applications have different logging contexts and having two such contexts log to the same file is either not possible or at least dangerous. If that's not true or doesn't have to be true, please elaborate.
The question: is it possible to achieve the above scenario with a single logging context? If so, could you please provide an example to make it working (the crucial bits will perfectly suffice), using lo4j2 or logback? Are there any catches?
Please note that we would like to avoid setting up a special servlet in one of the web applications for this (so the other web application would call it instead of logging directly to a file). Using (e.g.) syslog instead might be a solution perhaps but still, let's keep this question focused on the described scenario please.
After some research, trial and error, we managed to satisfy our requirements:
Each web application logs to its own files.
The shared JAR always logs to its own file too.
At least with Log4j2, the problem appears to revolve around class loaders. In our case, classes from the shared JAR file, and all of its dependencies, were always loaded using a class loader dedicated to (shared.loader). This means that Log4j2's JAR files need to be there beside the shared JAR file and if we tried to remove them, we would see ClassNotFoundExceptions.
Now, we can move to the web applications:
If a web application DOESN'T package Log4j2 for its own use, its loggers are also loaded using the class loader for shared.loader (as a fallback), and the web application's logging configuration overwrites the configuration applied previously (in our case, we had to call explicit initialization or reconfiguration, so that's why). It wouldn't work as expected.
If a web application DOES package Log4j2 for its own use, its loggers are loaded using the class loader for the web application (since dependencies packaged within WEB-INF take precedence), which is a completely different 'context' (class loader) than in approach #1, and the web application's logging configuration does NOT overwrite previously applied configurations (since the shared JAR and all web applications have their own 'context').
That is the behaviour we observed. During approach #1, each web application overwrote logging configuration for the applications initialized/started previously, because the Log4j2 'context' (class loader) was shared. During approach #2, the shared JAR's Log4j2 context was initialized by the web application that called its initialization (only one of them), and the configuration was not touched ever since. In our case, the configuration file was provided by the web application (it was not packaged within the shared JAR). Note that in practice, one of the web applications will always have to initialize the shared JAR, either implicitly or explicitly.
For absolute certainty, we listed open file descriptors to the shared JAR's log files and with approach #2, there was indeed only one. With approach #2, there can still be multiple descriptors open if some of the web applications' configuration files also reference the shared JAR's log files (configuration duplicity). That is precisely the sort of situation you'd normally want to avoid.
Pitfalls that we discovered:
Notice that the shared JAR's Log4j2 context was initialized by the web application that called its initialization (only one of them) remark is a bit tedious. Unless we copy or include the shared JAR's configuration within configuration of each web application (and for the above stated reasons, we want to avoid that), some of the logging messages may end up elsewhere or get lost during servlet container's start. In general, it may not be possible to guarantee the order in which web applications are started. I may be wrong but from what I saw in Tomcat logs, Tomcat starts the web applications (WAR files) in alphabetic order.
If the shared JAR and any of the web applications happen to share some more dependencies (beside the logging framework), these dependencies must, also, be placed within shared.loader. But, if the shared JAR's logging configuration redirects logging messages of any of those dependencies into its own logs, they can not end up in the web application's logs, unless that dependency is also packaged within the web application (same principle as approach #2 above). But in practice, separation of logging API and backing implementations makes this difficult. For example, you can read here on StackOverflow that selection of SLF4J bindings is rather "random" (JVM-dependent). If you put different bindings to shared.loader and into one of the web application's WEB-INF folder, you may not have certainly about which binding is going to get selected.
I certainly can not recommend the scenario we are trying to achieve but the described "solution" definitely works as expected (despite the pitfalls).

Strategies to change Logback appender level at production

I package my applications as jars and wars. After deployment in production, I like to see on the logs if everything is ok, and once it is, I would like to decrease the level of these logs at runtime, without deploying a new jar/war again.
What is the strategies available to do this with logback? The only I found was to put the logback configuration outside the jar/war, so I could change the level anytime without deploying again. The problem with this is that it will create new arguments to my application, I would like to avoid this, I want to keep it simple.
Is there a better way to accomplish the same?
From my experience, there is no cleaner way than providing an external logback.xml.
Other options include using JMX to adjust the logger level or implementing an endpoint that will change the logger configuration programmatically.

Getting the Log4j settings from a custom properties file

I am working on a huge application which uses its on property file to set the global variables. I want to use the same one for setting the properties of Log4j log file instead of log4j.properties file due to some problems. How do I do this? Is there any way to set the properties of Log4j at run time?
If you just want to rename your log4j.properties file you can do this using a System Variable in your JVM startup (refer link)
-Dlog4j.configuration=test.properties
The Log4j API also allows configuration changes to the Loggers from within an application. Theoretically you could read your own property file and call the appropriate API calls. I'm not sure this approach is recommended - you might end up spending a lot of time getting it working that you could better use developing your application functionality.
Note: I dont know the nature of the huge application, but it if its running in an application server such as JBoss they often have their own dynamic logging configuration

Combining Application and Server Level logging with Log4J?

Is it possible to work with two different levels of logging simultaneously with Log4J/Tomcat? Here is my situation: I have a few webapps operating on the same Tomcat Server. Some of these applications have their own log4j properties file (legacy).
Something I want to add is a new logger with a JDBCAppender that will work across all of these applications (They're meant to be used together so having them log to the db we've selected would be VERY useful for us.) I've written the properties file entries in order to do this the way I want (and tested in one of those local property files for syntax purposes).
Is it possible to drop this new logger/JDCBAppender in a server-level log4j.properties file, and then have the webapps gain access to it? For instance, if I define the logger as 'com.xxx.yyy', then in any webapp that has a class in a 'com.xxx.yyy' package grab said logger with a call like:
private static Logger logger = Logger.getLogger(MyClass.class);
assuming the full declaration is com.xxx.yyy.MyClass.
I've tried dropping the log4j.properties file in the $CATALINA_HOME/lib directory as well as placing the necessary jar files in the same directory (as directed in the comments below) but when I launch my server, it doesn't seem to pick that one up, although it picks up the one from my webapp. I know the properties file has to be on the classpath for log4j to pick it up, but can there be some sort of similar-class path style issue if there is more than one log4j.properties file?
Update: I've updated the description of what I've tried.
I've done some additional research and learned that if multiple log4j.properties files are on the classpath, the system will use the first it finds, just like with java class/library files.
So in the end, the situation I was describing is not feasible, as adding a Server-level properties file would cause all my individual web-apps' legacy property files to be ignored. It may still be possible to do something similar, but my question was intended to focus specifically on the log4j.properties file.

Categories