I have a java webapp that has to be deployed on either Win or Linux machines. I now want to add log4j for logging and I'd like to use a relative path for the log file as I don't want to change the file path on every deployment. The container will most likely be Tomcat but not necessarily.
What's the best way of doing this?
Tomcat sets a catalina.home system property. You can use this in your log4j properties file. Something like this:
log4j.rootCategory=DEBUG,errorfile
log4j.appender.errorfile.File=${catalina.home}/logs/LogFilename.log
On Debian (including Ubuntu), ${catalina.home} will not work because that points at /usr/share/tomcat6 which has no link to /var/log/tomcat6. Here just use ${catalina.base}.
If your using another container, try to find a similar system property, or define your own. Setting the system property will vary by platform, and container. But for Tomcat on Linux/Unix I would create a setenv.sh in the CATALINA_HOME/bin directory. It would contain:
export JAVA_OPTS="-Dcustom.logging.root=/var/log/webapps"
Then your log4j.properties would be:
log4j.rootCategory=DEBUG,errorfile
log4j.appender.errorfile.File=${custom.logging.root}/LogFilename.log
I've finally done it in this way.
Added a ServletContextListener that does the following:
public void contextInitialized(ServletContextEvent event) {
ServletContext context = event.getServletContext();
System.setProperty("rootPath", context.getRealPath("/"));
}
Then in the log4j.properties file:
log4j.appender.file.File=${rootPath}WEB-INF/logs/MyLog.log
By doing it in this way Log4j will write into the right folder as long as you don't use it before the "rootPath" system property has been set. This means that you cannot use it from the ServletContextListener itself but you should be able to use it from anywhere else in the app.
It should work on every web container and OS as it's not dependent on a container specific system property and it's not affected by OS specific path issues.
Tested with Tomcat and Orion web containers and on Windows and Linux and it works fine so far.
What do you think?
If you use Spring you can:
1) create a log4j configuration file, e.g. "/WEB-INF/classes/log4j-myapp.properties"
DO NOT name it "log4j.properties"
Example:
log4j.rootLogger=ERROR, stdout, rollingFile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.File=${myWebapp-instance-root}/WEB-INF/logs/application.log
log4j.appender.rollingFile.MaxFileSize=512KB
log4j.appender.rollingFile.MaxBackupIndex=10
log4j.appender.rollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.rollingFile.Encoding=UTF-8
We'll define "myWebapp-instance-root" later on point (3)
2) Specify config location in web.xml:
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j-myapp.properties</param-value>
</context-param>
3) Specify a unique variable name for your webapp's root, e.g. "myWebapp-instance-root"
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>myWebapp-instance-root</param-value>
</context-param>
4) Add a Log4jConfigListener:
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
If you choose a different name, remember to change it in log4j-myapp.properties, too.
See my article (Italian only... but it should be understandable):
http://www.megadix.it/content/configurare-path-relativi-log4j-utilizzando-spring
UPDATE (2009/08/01)
I've translated my article to English:
http://www.megadix.it/node/136
Just a comment on Iker's solution.
ServletContext is a good solution for your problem. But I don't think it is good for maintains. Most of the time log files are required to be saved for long time.
Since ServletContext makes the file under the deployed file, it will be removed when server is redeployed. My suggest is to go with rootPath's parent folder instead of child one.
Doesn't log4j just use the application root directory if you don't specify a root directory in your FileAppender's path property? So you should just be able to use:
log4j.appender.file.File=logs/MyLog.log
It's been awhile since I've done Java web development, but this seems to be the most intuitive, and also doesn't collide with other unfortunately named logs writing to the ${catalina.home}/logs directory.
As a further comment on https://stackoverflow.com/a/218037/2279200 - this may break, if the web app implicitely starts other ServletContextListener's, which may get called earlier and which already try to use log4j - in this case, the log4j configuration will be read and parsed already before the property determining the log root directory is set => the log files will appear somewhere below the current directory (the current directory when starting tomcat).
I could only think of following solution to this problem:
- rename your log4j.properties (or logj4.xml) file to something which log4j will not automatically read.
- In your context filter, after setting the property, call the DOM/PropertyConfigurator helper class to ensure that your log4j-.{xml,properties} is read
- Reset the log4j configuration (IIRC there is a method to do that)
This is a bit brute force, but methinks it is the only way to make it watertight.
In case you're using Maven I have a great solution for you:
Edit your pom.xml file to include following lines:
<profiles>
<profile>
<id>linux</id>
<activation>
<os>
<family>unix</family>
</os>
</activation>
<properties>
<logDirectory>/var/log/tomcat6</logDirectory>
</properties>
</profile>
<profile>
<id>windows</id>
<activation>
<os>
<family>windows</family>
</os>
</activation>
<properties>
<logDirectory>${catalina.home}/logs</logDirectory>
</properties>
</profile>
</profiles>
Here you define logDirectory property specifically to OS family.
Use already defined logDirectory property in log4j.properties file:
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=${logDirectory}/mylog.log
log4j.appender.FILE.MaxFileSize=30MB
log4j.appender.FILE.MaxBackupIndex=10
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d{ISO8601} [%x] %-5p [%t] [%c{1}] %m%n
That's it!
P.S.: I'm sure this can be achieved using Ant but unfortunately I don't have enough experience with it.
My suggestion is the log file should always be logged above the root context of the webApp, so in case we redeploy the webApp, we don't want to override the existing log files.
My solution is similar to Iker Jimenez's solution, but instead of using System.setProperty(...) I use org.apache.log4j.PropertyConfigurator.configure(Properties). For that I also need log4j to be unable to find its configuration on its own and I load it manually (both points described in Wolfgang Liebich's answer).
This works for Jetty and Tomcat, standalone or run from IDE, requires zero configuration, allows to put each app's logs in their own folder, no matter how many apps inside the container (which is the problem with the System-based solution). This way one can also put the log4j config file anywhere inside the web app (e.g. in one project we had all config files inside WEB-INF/).
Details:
I have my properties in the log4j-no-autoload.properties file in the classpath (e.g. in my Maven project it's originally in src/main/resources, gets packaged into WEB-INF/classes),
It has the file appender configured as e.g.:
log4j.appender.MyAppFileAppender = org.apache.log4j.FileAppender
log4j.appender.MyAppFileAppender.file = ${webAppRoot}/WEB-INF/logs/my-app.log
...
And I have a context listener like this (gets much shorter with Java 7's "try-with-resource" syntax):
#WebListener
public class ContextListener implements ServletContextListener {
#Override
public void contextInitialized(final ServletContextEvent event) {
Properties props = new Properties();
InputStream strm =
ContextListener.class.getClassLoader()
.getResourceAsStream("log4j-no-autoload.properties");
try {
props.load(strm);
} catch (IOException propsLoadIOE) {
throw new Error("can't load logging config file", propsLoadIOE);
} finally {
try {
strm.close();
} catch (IOException configCloseIOE) {
throw new Error("error closing logging config file", configCloseIOE);
}
}
props.put("webAppRoot", event.getServletContext().getRealPath("/"));
PropertyConfigurator.configure(props);
// from now on, I can use LoggerFactory.getLogger(...)
}
...
}
You can specify relative path to the log file, using the work directory:
appender.file.fileName = ${sys:user.dir}/log/application.log
This is independent from the servlet container and does not require passing custom variable to the system environment.
Related
We are migrating our application from WAS 6.1 to Liberty. Our application uses third party jars that read property files byInputStream is = ClassLoader.getSystemResource("myproperty.properties").
In WAS 6.1, we set server classpath to the location of myproperty.properties. We tried the below approaches to set classpath in Liberty, but nothing works
Approach 1: Set the below in jvm.options (D:\ConfigFiles\DEV\ - path containing myproperty.properties)
-Djava.class.path=D:\\ConfigFiles\\DEV\\
Approach 2: Setting the classloader in server.xml,
<library id="config">
<folder dir="${server.config.dir}/config/" includes="*.properties" scanInterval="5s"/>
</library>
<enterpriseApplication id="applicationEAR" location="application.ear" name="application">
<classloader privateLibraryRef="config"/>
</enterpriseApplication>
Please let us know if there is any other ways to override/set classpath in Liberty profile?
Try setting this property in jvm.options (instead of -Djava.class.path=path/to/propertyDir):
-Xbootclasspath/a:path/to/propertyDir
This will append the path of the property directory (containing your resource file) to the JVM's bootstrap classpath. Because this is an append, it should also work in Java 9+ (some related options have been removed in Java 9).
I suspect that the reason -Djava.class.path=... doesn't work is that the JVM gets the classpath from the WLP server script - so the system property is essentially applied too late in the startup of the server JVM.
You might also be able to put the properties file(s) in your JVM's lib/ext directory, but I haven't tested that. The -Xbootclasspath/a:path approach works for me on Mac - I assume it will also work on Windows.
HTH, Andy
If your end goal is to load a properties file, a more straightforward way to do this would be using a bootstrap/env/system property or <jndiEntry> in server.xml to store the location of the properties file, and then load it. For example, using an environment variable:
(in server.xml file)
<server>
<featureManager>
<feature>jndi-1.0</feature>
...
</featureManager>
<jndiEntry jndiName="configDir" value="D:\\ConfigFiles\\Dev"/>
</server>
Then, you can load the resource in your application like this:
#Resource(lookup = "configDir")
String configDir;
InputStream is = new FileInputStream(configDir + "/myproperty.properties");
Or, if you will always put your config property files somewhere under ${server.config.dir}, then you can utilize the built-in SERVER_CONFIG_DIR environment variable in Liberty:
String configDir = System.getenv("SERVER_CONFIG_DIR"); // equivalent to ${server.config.dir} in server.xml
InputStream is = new FileInputStream(configDir + "/myproperty.properties");
On the topic of managing configuration, check out MicroProfile Config (e.g. <feature>microProfile-1.2</feature>) which you may find useful: Configuring microservices with Liberty
I just converted from log4j to log4j2, using an xml config file. Everything is working except that I can't seem to set the path of my log files using a properties file.
This is a Spring MVC app and I have a filedirs.properties file located in the src/main/resources folder along with the log4j2.xml, i18n message and other properties files. It has a simple entry: logs=G:/web/logs/.` I've looked through other posts and am just not getting how to configure log4j2 correctly. Here's what I've have:
<Configuration>
<Properties>
<Property name="filename">${bundle:net.myapp.filedirs:logs}standard.log</Property>
</Properties>
<File name="stdLog" fileName="${filename}" ignoreExceptions="false">
<PatternLayout pattern="%d{DEFAULT} %-5p: %c - %m%n"/>
</File>
...rest of config...
The error is this:
ERROR FileManager (${bundle:net.myapp.filedir:logs}standard.log) java.io.FileNotFoundException:
Substituting the actual path works, e.g.,
<Property name="filename">g:/web/logs/standard.log</Property>
I'm hung up on the correct domain portion of the bundle syntax - myapp.net is not the actual website, but it's similar enough. I've tried a couple dozen variations but in debug mode java.util.ResourceBundle never finds the filedirs properties file when it goes through the log4j2 initialization. The manual sample is com.domain.messages so I'm stumped why net.myapp.filedirs doesn't work.
As I indicated in my comment, it turns out that it does work but the manual may be incorrect. Instead of com.domain.messages as described here, Property Substitution, what worked was simply messages, e.g., <Property name="filename">${bundle:filedir:logs}standard.log</Property>. Adding status=debug to <Configuration> finally helped me figure this out.
I don't know if this makes any difference, but I'm testing through STS (localhost) on my PC - is it possible the documentation may be correct for a production website? No idea since I don't have a production deployment yet but it's something I'll check out when I get it up and running.
I want to configure log4j2 to lookup the logging path dynamically from web startup (tomcat).
From the docs (http://logging.apache.org/log4j/2.x/manual/lookups.html) there is a web: lookup with different parameter possibilities.
At first I'm trying the provided example:
<Appenders>
<File name="ApplicationLog" fileName="${web:rootDir}/app.log"/>
</Appenders>
Result: ERROR Unable to create file ${web:rootDir}/app.log java.io.IOException
I also tried the other buildin properties like servletContextName and contextPath with the same error message.
So I'm probably still missing something important. But what?
Add log4j-web JAR
Your usual log4j-core JAR file on its own is not enough.
There is an additional component called log4j-web.
For automatic replacement of the ${web:...} placeholders in your logging configuration, you need the additional log4j-web JAR file. If you don't have this log4j-web JAR file, then Log4j2 itself will still work, but it will not replace these placeholders with anything. So for example ${web:rootDir} will just end up as the literal text ${web:rootDir}.
Perhaps you are missing that dependency in your project?
Log4j-web on Maven Central
Is ${web:rootDir} a value? The placeholder is not replaced by its real value.
In the page you can red:
Information on how to use Lookups in configuration files can be found in the Property Substitution: http://logging.apache.org/log4j/2.x/manual/configuration.html#PropertySubstitution
According to the documentation for Google App Engine for Java:
The App Engine Java SDK includes a
template logging.properties file, in
the appengine-java-sdk/config/user/
directory. To use it, copy the file to
your WEB-INF/classes directory (or
elsewhere in the WAR), then the system
property java.util.logging.config.file
to
"WEB-INF/classes/logging.properties"
(or whichever path you choose,
relative to the application root). You
can set system properties in the
appengine-web.xml file, as follows:
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
...
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/classes/logging.properties" />
</system-properties>
</appengine-web-app>
The Google Plugin for Eclipse new
project wizard creates these logging
configuration files for you, and
copies them to WEB-INF/classes/
automatically. For java.util.logging,
you must set the system property to
use this file.
If your write to standard out or standard error, that will automatically get logged as INFO or WARNING.
So, why do you need to use a logging.properties file?
Does this give you some additional control over your logging?
If you want to use more specific logging info, like some DEBUG.
This way you can log more info during development, and you don't need to change your code when you put your code in production.
Personal example: When I code, I log a lot of info ( logging Level FINE, and FINEST). When I send my application to tester, they use DEBUG level. In production (to public) only INFO, WARNING and SEVERE are log.
In conclusion , this give you more control, and you don't have to change any line of code.
For more info about logging in java : here
I have an embedded tomcat app and whenever I start it up I see this error printed to the console twice:
Unable to configure the logging system. No log.properties was found.
It seems stupid, but I've done some googling and searched stackoverflow and can't seem to find someone experiencing this problem.
My main class Looks roughly like this:
public class AuthServerEntryPoint {
static {
org.apache.log4j.PropertyConfigurator.configure("conf/log4j.properties");
}
public static void main(String[] args) {
// ...
}
"conf/log4j.properties" contains a seemingly valid configuration:
log4j.appender.mainAppend=org.apache.log4j.ConsoleAppender
log4j.appender.mainAppend.layout=org.apache.log4j.PatternLayout
log4j.appender.mainAppend.layout.ConversionPattern=%d %p [%t] %c -- %m%n
log4j.appender.fileAppend=org.apache.log4j.FileAppender
log4j.appender.fileAppend.layout=org.apache.log4j.PatternLayout
log4j.appender.fileAppend.layout.ConversionPattern=%d %p [%t] %c - %m%n
log4j.appender.fileAppend.file=logs/myservice.log
log4j.rootLogger = info, fileAppend
log4j.logger.com.mycompany.myservice = debug, fileAppend
And the logging actually does work - i.e., logs are correctly written to myservice.log. So what gives?
Thanks!
-Carl
By embedded Tomcat app, do you mean that you are starting Tomcat from Java code and are using the class org.apache.catalina.startup.Embedded?
If yes, my guess is that Tomcat might not be able to find its logging configuration file that is set up in catalina.sh (or equivalent) when using the scripts:
LOGGING_CONFIG="-Djava.util.logging.config.file=$CATALINA_BASE/conf/logging.properties"
The odd part is that this file is called logging.properties, not log.properties, so I'm really not sure. I didn't check Tomcat sources though, maybe they are doing some kind of black magic in there.
Could you just try to rename $CATALINA_BASE/conf/logging.properties into log.properties (or not) and to put it on the classpath of your app (or to set -Djava.util.logging.config.file)?
By default, log4j will look for a logging properties file on the classpath. Put your webapp's logging properties config file into its WEB-INF/classes directory; e.g.
$CATALINA_HOME/webapps/<yourApp>/WEB-INF/classes/log4j.properties
This is the simple approach to getting a webapp to use Log4j is recommended by the referenced documentation. And it is the approach I use myself for webapp logging.
There are various other ways to configure Log4j logging on Tomcat as well. It is possible that your Tomcat has been (partly) configured to use one of them, but something has not been done quite right.
Configuring Tomcat' log4j logging via the system properties is an option that avoids figuring out where log4j is looking ... and what is going wrong. But you are then stuck with creating/using a custom launch script or having to remember to set environment variables before launching Tomcat.
References:
Configuring logging on Tomcat 5.5
Configuring logging on Tomcat 6
The "Default Initialization under Tomcat" section of the Log4j Manual