Log(ger) Variable Declaration - java

In the majority of cases I see Log instances declared as follows:
public static final Log LOG = LogFactory.getLog(MyClass.class);
I assume this means that the log configuration is loaded when the MyClass is loaded and is therefore set in stone until the MyClass is either reloaded or the JVM restarted?
So, if this assumption is correct what is the best way to ensure changes to the log configuration are picked up as (or as soon after) they happen?

I assume that you are using commons-logging from the LogFactory class? As far as I know, none of the usual logging implementations (Log4J, java.util.logging) allow you to reload a configuration file in a running application (regardless of whether the actuall Loggers are declared as static variables). (EDIT: Peter's answer below proves that I was wrong about this in the case of Log4J)
However, they do allow for the dynamic changing of logging levels (e.g. via an MBean). These level-changes will be picked up by any Logger (including those declared as static variables). If you use java.util.logging you get the MBean for free in the JConsole.
Is it just the changing of levels you care about, or do you wish to provide completely different logging configurations (e.g. files, logger definitions) on the fly?

I guess this depends on the underlying implementation, as pointed out by oxbow_lanes. Generally, it might be difficult to reconfigure your logging subsystem if you are relying on config files that are available via the classpath. To get around this limitation, we do all our config programmatically, and do not rely on only static config files. But I don't know whether your implementation support programmatic reconfiguration.

No, the log configuration is loaded typically when the logging implementation classes are initialized. When your class is (re)loaded, all that happens is that the logging API is called to get a logger (which may or may not be present in any configuration) and stored as a class variable.
To reload your logging configuration, then you typically would have to get the logging implementation to reload.

Depends on the backend.
Logback has a very niftly feature where the reload can be triggered by JMX, i.e. in jvisualvm or jconsole.

Actually, if you are using "java.util.logging" logging directly, then you CAN reload the logger configurations on the fly. There are two methods on the LogManager class for doing this:
public void readConfiguration()
throws IOException, SecurityException
This reloads the default logging properties and reinitializes the logging configuration.
public void readConfiguration(InputStream ins)
throws IOException, SecurityException
This loads the logging properties from a stream (in Property file format) and reinitializes the logging configuration.
See the LogManager javadoc.

Log4j can reload your config file whenever it changes.
see the faq here
Is there a way to get log4j to
automatically reload a configuration
file if it changes?
Yes. Both the DOMConfigurator and the
PropertyConfigurator support automatic
reloading through the
configureAndWatch method. See the API
documentation for more details.
Because the configureAndWatch launches
a separate wathdog thread, and because
there is no way to stop this thread in
log4j 1.2, the configureAndWatch
method is unsafe for use in J2EE
envrironments where applications are
recycled.

Related

using Logger.getGlobal() with tomcat9 and org.apache.logging.log4j.jul.LogManager - log4j config is ignored

We have a tomcat9 with a legacy application using jul for logging running. To be able to steer the log format easier and have MDC and some other features we use org.apache.logging.log4j.jul.LogManager and a log4j config file like so:
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager
-Dlog4j.configurationFile="\path\to\file\log4j2.xml
this works very well for all loggers which are fetched via
Logger log = Logger.getLogger(LoggingTest.class.getName());
but in the legacy code there is also the GlobalLogger as well as Anonymous Loggers used e.g. like this (simplified but reproduces the problem):
Logger.getGlobal().log(Level.INFO, "bar with global");
Logger.getAnonymousLogger().log(Level.INFO, "bar with anonymous");
these are not logging via log4j but directly with Java Util ignoring the LogManager completely. The logger returned in these cases is not an instance of org.apache.logging.log4j.jul.CoreLogger but java.util.logging.Logger, although it's manager is still org.apache.logging.log4j.jul.LogManager.
Is this a known limitation and/or is there a workaround for that?
Looking at the source code it looks like Logger.getAnonymoustLogger() does a new Logger() directly instead of going through the LogManager. This makes it impossible for Log4j's replacement LogManager to properly support this as Log4j is expecting its LogManager replacement to be creating Loggers.
The GlobalLogger appears to also be setup with a new Logger() call during initialization. However, in your code above you show that you are calling getLogger() against the Global Logger which is a bit interesting since getLogger() is a static method and should behave just like Logger.getLogger() which ends up calling the LogManager to get a Logger, so in this case I am not sure why you would not also be getting a Log4j CoreLogger.

Vert.x: best way to log to file

What is the fastest async way to log to file in Vert.x?
The aim is to write logs from loggers from different classes (i.e. Class1, Class2 etc) to 1 file (something like 'console.log')
Vert.x uses the JDK bundled JUL logging framework to avoid shipping additional dependencies. However it allows to append a custom logger implementation.
Assuming that you want to stick to the default logging facility, customizing the log handler would then be as easy as droping a logging file and referencing it through the java.util.logging.config.file system property:
For example you can drop the logging configuration file under a config directory under the root path of your (fat) jar which may look as follows:
handlers = java.util.logging.MyFileHandler
config =
#...
You should then refrence that file in a system property as follows when starting your Vert.x application:
-Djava.util.logging.config.file=config/logging.properties
You can then access the Logger object in your classes as follows:
Logger logger = LoggerFactory.getLogger("some.package.MyClass");
Use that logger to log messages that will be handled by the configured handler(s):
logger.info("some informative message");
Note the use of a custom log handler in the properties file to emphasis the possibily of appending your own handler (which may extend the default FileHandler).
Check the Vert.x documentation for more informations on how to use explore the logging feature.
Most of loggers are async from the beginning , i.e. they are not write information immediately. Logs are stored into buffer, which is flushed by timeout or when it is full. So slf4j + log4 is good enough for most cases.

Logging same class to different file, if war is different

I need to split 1 batch application to 3 different. Code is almost the same, I have just modified ANT build script, and excluded or included some dependencies for different app. Than I have set different web.xml for each war. Each web.xml defines different spring application context with different beans for different behaviour.
All wars run on one tomcat server. Application used log4j, but now I refactored it to use slf4j instead. Thought I still need to use log4j under slf4j.
The problem I have is that each application log must appear in different log file,
even though class names are the same.
I can't write different log4j.properties file, because administrators placed it in tomcat/lib folder for all applications.
I have tried to place 3 files in tomcat/lib and change configuration file name for each application when initializing servlet, but it changed for all applications at same time.
Only solution I can think of now is to wrap log4j-over-slf4j, create 3 different slf4j log factories, that would append some prefix for each log name. For example, if I have this log:
private final Logger logger = LoggerFactory.getLogger(MainProcessor.class);
Each logging factory would genarate these logging names (with prefixes app1,app2 and app3) :
app1.com.test.MainProcessor
app2.com.test.MainProcessor
app3.com.test.MainProcessor
Is there any better way to deal with this problem ?
Try using a hook method, fire event, etc. so the logging doesn't happen in the class that is shared across the applications, but in some (top) class that is unique per application.
Variation is to statically access some logging class, use a singleton, etc. from the class where the logging should occur, but set context to that logging class on app initialization.

What is the right logging approach when using Spring Batch?

What is the right logging approach when using Spring Batch? Should I use log4j (or something similar) or Spring Batch provides some instruments that help me to instantiate a logger and use it? Maybe some sort of dependency injection of the logger?
I'd used log4j. and its the simple and nice approach.
I'm not sure the original poster's question was answered, so I'll try restating this a bit. In Spring Batch you may have multiple threads going, and you may want to have job-specific logging, so that all events for a particular job are logged into a single log file. You want a Logger whose scope is tied directly to the job you are processing. When the job finishes, the logger (and all references to the logger) go away.
So when you submit Job#1, all events are logged to "job_1.log"; when you submit Job#2, its events are logged to "job_2.log", etc.
In log4j, when you do "Logger.getLogger('mylogger')" you are telling the LogManager to get 'mylogger' out of the cache and give it to you. What you really want is a new instance of the logger, configured using the configuration of mylogger. In Spring this might be typically done with a prototype bean. Every time you ask the context for 'mylogger' you would get a new instance.
You should think about Slf4J (logging API) + Logback (logging implementation) as Log4j is intented to be succeeded by this duet.
More:
http://www.slf4j.org/
http://logback.qos.ch/

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