Loading log4j in ContextListener - java

I'm trying to move from log4j 1.x to 2.x and I'm having some trouble. I was using slf4j/log4j1.2.x but am trying to get off the slf4j stuff and just use log4j.
I have a web application that uses two shared jars that have their own log4j configuration files. Previously I was able to load them using DOMConfigurator:
DOMConfigurator.configure(Loader.getResource(sContext.getInitParameter(CoreServicesListener.INIT_SYSLOGGING_FILE)));
Not sure how to do this anymore in log4j2. I've checked several resources on the web and nothing seems to be getting me to the right path. I'm hoping someone can help.

The easiest solution is to replace your custom ServletContextListener with the log4j-web module (cf. documentation).
The log4j-web module uses the servlet context parameter "log4jConfiguration", which accepts string interpolation. If for any reason you can not change the old parameter name, you can set the value of "log4jConfiguration" to ${web:initParam.old_param_name} (cf. Web Lookup).
Remark: Log4j 1.x and 2.x configuration formats are incompatible. You need to convert the configuration files before migrating.

Related

log4j2 Web Lookup not working for IBM WAS9 JVM Custom Property

We have Spring java-based web applications which use log4j2.xml for logging messages to files, etc.
We now need to update our log4j2.xml configs in order to be able to do a ${a.b.c} web lookup inside them so that we can use a JVM custom property value as part of the log file's name which the loggers log messages to. "a.b.c" is a JVM custom property name in IBM WAS9.0. However, when we deploy the apps, the log4j2 configurations fail to recognize any web lookup related stuff. The file created to log messages to is simply created with the name ${a.b.c} and no messages are actually logged in them. I have log4j-api, log4j-core, log4j-web in pom.xml.
I have read various docs online related to log4j2 web lookups when running in 3.0 servlets but I still can't see what the problem might be in our configurations. And I don't know what to look for in the log4j's trace logs in order to see what it is that we are missing.
Our Stack:
Spring 4.3.x
Solaris
Java 8
IBM WAS9.0
log4j-2.17.0 (log4j-api, log4j-core, log4j-web all in classpath)
I get this issue resolved by referencing the IBM WAS JVM custom property by ${sys:condir} in the log4j2.xml file.

External web-application configuration in Tomcat

There's a web application and a number of environments in which it works. In each environment it has different settings like DB connection and SOAP ends-points that in their turn are defined in properties-files and accessed in the following way:
config.load(AppProp.class.getClassLoader().getResourceAsStream(
PROPERTIES_FILE_PATH + PROPERTIES_FILE_NAME));
Thus the WAR-files are different for every environment.
What we need is to build a unified WAR-file that doesn't contain any configuration and works in any environment (for now, Tomcat instance) getting its configuration from outside its WAR-file.
The answer Java Web Application Configuration Patterns, to my mind, gives the full set of common approaches but with just few examples. The most attractive way is configuring JNDI lookup mechanism. As I can guess it allows to separately configure web-applications by their context paths. But couldn't find a simple (step-by-step) instructions in both the Internet and the Tomcat's docs. Unfortunately cannot spend much time on studying this complicated stuff in order to just meet so seemingly simple and natural demand :(
Would appreciate your links at the relevant descriptions or any alternative suggestion on the problem.
If its a case of simply deploying your WAR on different environment (executed by different OS user), then you can put all your config files in the user's home folder and load them as:
config.load(new FileInputStream(System.getProperty("user.home") + PROPERTIES_FILE_NAME));
This gives you the isolation and security and makes your WAR completely portable. Ideally though, you should still provide built-in default configuration if that makes sense in your case.
The approach we've taken is based on our existing deployment method, namely to put the WAR files in the filesystem next to the Tomcat, and deploy a context.xml pointing to the WAR file to Tomcat.
The context descriptor allows for providing init parameters which is easily accessible in a servlet. We've also done some work on making this work with CDI (for Glassfish and TomEE dependency injection).
If you only have a single WAR file deployed to this Tomcat instance, you can also add init parameters to the default global context XML. These will be global and you can then deploy the WAR file directly. This is very useful during development.

How to configure Spring to load application.properties from outside of jar?

I'd like to have a directory structure as so:
/Directory
- Application.jar
- application.properties
So that I can change properties without having to repackage and redeploy (and instead just restarting the jar). How can I accomplish this with spring annotations or configuration classes?
I'm not asking about making external resources available with my web application, I'm also looking to change the location from where spring loads the application.properties file.
You're mentioning jar, so you're using Spring Boot?
If so, external application.properties in the same directory (structure just like you described) will override application.properties packaged inside the jar file.
Then, if you have something like key=value in your application.properties, you can inject it in your code with #Value("${key}") String key.
Try it, it will just work :)
You may want to explore PropertyPlaceholderExplorer class in Spring. This class provides the facility to access the properties file external to your jar/war bundle. There is a short nice tutorial on this as well here.
If you start using Spring Boot (at some stage you for sure will). you get powerful configuration externalization features.
With Spring Boot your applications.properties are automatically loaded into Spring Boot context and you can use ${...} placeholders.
You can use even more modern feature #ConfigurationProperties to map you configuration to POJO. This POJO can even be validated by Java EE validation annotations (e.g. #NotNull)

How do I configure log4j per deployed application in Glassfish 3?

I'm trying to use log4j to handle the logs for a web-service which is running under Glassfish 3. Most of the guides I've seen using log4j with Glassfish want me to mess around with Glassfish global settings, which I want to avoid as there will be more than one application deployed per instance of Glassfish.
Is there any way for me to have Glassfish execute a piece of code when my web service is deployed which will allow me to call DOMConfigurator and set up log4j using my XML file?
Thanks for any help!
EDIT: The answer is to place the log4j.xml file in WEB-INF/classes. In our case, it looks as if log4j remains un-configured, but logging does still actually work as expected.
Yes. All you need to do is deploy the log4j configuration with the component you're deploying; log4j will use the locally-scoped configuration as long as it's not being referenced in a parent classloader.
Glassfish' global settings won't factor in at all in that case.

Logging Java web applications?

I am planning to implement logging into a web application that I am currently working on but I am struggling with some of the details. What is the best way to go about logging a Java web application?
Specifically;
Where does the configuration file go in .war package file?
Where do people log to, relative or absolute path flat file, or a database?
Does Log4J logging go directly into the application server log file automatically or is that something you have to set up? In this case I am using Tomcat, but I often use Jrun.
Any other gotchas I should be aware of for web application logging?
Currently I am using Log4J but I imagine that the best practices would apply universally to all logging implementations.
EDIT:
One addition to the questions up top.
Where do you initilize the log
configuration?
In a traditional app, I do this at the entry point;
DOMConfigurator.configureAndWatch("log4j.xml");
What would the web application equivalent be?
I would recommend you to use SLF4J. This is simple logging facade which supports most of popular logging systems (Log4j, commons-logging, Java Logging API and Logback).
Using it, you will able to replace your underline logging system to any other, by simple CLASSPATH update.
The other benefit of SLF4J are parameterized calls, which reduces ugly logging code.
Actually, they recommends to use SLF4J with Logback.
Logback is a successor of Log4J. And it was designed by the same author.
Where does the configuration file go in .war package file?
Root of the classpath.
Where do people log to, relative or absolute path flat file, or a database?
Depends on the need. It's always better to use relative paths. Databases are good if you implement another application which will fetch logs from it and send them using email/sms
Does Log4J logging go directly into the application server log file automatically or is that something you have to set up? In this case I am using Tomcat, but I often use Jrun.
If you use Console appender, yes, it's going to be logged in your servlet container log file.
Any other gotchas I should be aware of for web application logging?
If you are logging from different threads use logback, it's thread-safe and it exposes parameterized log messages.
Logging to a DB adds another failure point. We had a situation where the prod servers logged to a DB and someone ran an expensive query on that DB that slowed it so much that the prod servers got very, very slow. Logging to a file can cause issues if you run out of space but it seems less likely to slow down the whole app.
I place my configuration on the default package: src/
and log to files using the ${catalina.home} system property:
log4j.appender.???.file=${catalina.home}/logs/system.log
It might be a good idea to place the config file somewhere where an admin can modify it without rebuilding your web app (e.g., so they can turn on detailed logging without waking you up in the middle of the night).
Unfortunately, there's no "official" way for locating externalized resources from a web app (correct me if I'm wrong). The most common way of doing it I've seen is to look through the directories in the classpath.
The excellent paper How to Do Application Logging Right has a bunch of gotchas.
I believe that your other questions have been answered by other people on this page.
I also recommend that you use SLF4J.
One last thing: having speakable representations of objects can save some time.
I recommend to call log API (log4j) via slf4j. Even if you use log4j, web container or depending modules may use different log API such as Java.util.logging or Jakarta commons logging. Slf4j provides bridge modules that redirect them to slf4j API. As a result, all log messages are written by log4j in that case.
put the log4j in the container (server) and create proper appenders per application
relative to server path, but that depends on your needs
we use appenders which log to different files, depends on your needs, e.g. one file for hibernate info/statistics, one for application only, etc.
don't log to much, it slows the application down
Personally I put the log4j.properties in the WEB-INF directory and use an init servlet with the following code :
public class InitServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
private static final String LOG4J_FILE = "WEB-INF/log4j.properties";
public InitServlet() {
super();
}
#Override
public void init() throws ServletException {
super.init();
PropertyConfigurator.configure(getServletContext().getRealPath(LOG4J_FILE));
LogFactory.getLog(InitServlet.class).info("LOG 4J configured");
}
}
Where does the configuration file go in .war package file?
At the root of the classpath but... Don't put the configuration file in the war package. You don't want to repackage and redeploy the application if you change the logging configuration, do you ? A better practice would be to put the configuration file somewhere in the classpath outside the war.
Where do people log to, relative or absolute path flat file, or a database?
I usually log to the file system on a separate partition (log files can grow very fast and should never block the application or the operating system if they become too big). I use most of time an absolute path based on the following model: /var/projects/<PROJECT_NAME>/<PRODUCT>/<CLUSTER_NAME>/logs/<INSTANCE_NAME>.log where <PROJECT_NAME> is the project name, <PRODUCT> can be Apache, Tomcat, Weblogic,..., <CLUSTER_NAME> the name of the cluster and <INSTANCE_NAME> the name of the instance inside the cluster. Logging to the file system is faster than in a database. The drawback is that logs aren't centralized if you are using several instances and physical machines. But merging can easily be done with a script.
Does Log4J logging go directly into the application server log file automatically or is that something you have to set up? In this case I am using Tomcat, but I often use Jrun.
Application server logs are application server logs, not application logs. Don't write to them but set up a logger tool (e.g. log4j) and write to application logs (understand dedicated).
Any other gotchas I should be aware of for web application logging?
If you are using log4j, don't forget to use the isDebugEnabled() before to log:
if(logger.isDebugEnabled()) {
logger.debug("Bla Bla Bla");
}
Where does the configuration file go in .war package file?
Usually, I do not place any logging configuration into the application, rather leaving that to the appserver admins to configure logging server-wide. In the rare cases I want the log4j configuration deployed with a webapp, WEB-INF is the usual path.
Where do people log to, relative or absolute path flat file, or a database?
Again, depends on appserver settings. One common log file for a appserver and rotating on a daily basis is the usual setup. If there are any app-specific needs, the admin may configure a separate logfile for an app (distinguished by package / class names).
Does Log4J logging go directly into the application server log file automatically or is that something you have to set up? In this case I am using Tomcat, but I often use Jrun.
See above. For tomcat used for development purposes, I'd just look for its logging (log4j) configuration and add app-specific specific there.
Any other gotchas I should be aware of for web application logging?
Performance. Limit the ,log level to a minimum (i.e. WARN or ERROR) once you go live. Use
if (log.isDebugEnabled()) { log.debug("..."); } and alike constructs in your code.
Note that if you just need a bit of logging, the servlet standard specifies that you can get the ServletContext and use the log methods there. That is the generic servlet equivalent of System.out.println.

Categories