We have a webapp that contains two log4j2.xml files:
one within WEB-INF/classes containing webapp-specific logging settings (1), and
one within the root directory of a dependency located in WEB-INF/lib containing default logging settings that we use also in other webapps (2)
My questions are:
How does tomcat read and evaluate these files?
In which order are they read?
Is it possible to overwrite settings of (2) by (1) and how would I do that?
Thank you.
Reading the documentation here you will find that:
Log4j allows the configuration file to be specified in web.xml using the log4jConfiguration context parameter. Log4j will search for configuration files by:
If a location is provided it will be searched for as a servlet context resource. For example, if log4jConfiguration contains "logging.xml" then Log4j will look for a file with that name in the root directory of the web application.
If no location is defined Log4j will search for a file that starts with "log4j2" in the WEB-INF directory. If more than one file is found, and if a file that starts with "log4j2-name" is present, where name is the name of the web application, then it will be used. Otherwise the first file will be used.
The "normal" search sequence using the classpath and file URLs will be used to locate the configuration file.
if I understand correctly, this answers all your questions.
Related
I am new to Springboot and I am having trouble with externalizing my properties files.
I have multiple ".properties" files that I have kept in a subdirectory "config/". I have removed context-placeholders from my project and have included the comma-separated properties files while executing the jar.
eg. java -jar myjar.jar --spring.config.location=file:////config/PROP1.properties, file:///config/PROP2.properties -debug
I have few questions
Why are the files in the config directory not being read even after explicitly mentioning where to look?
I have my own dependencies in the project that have same-named properties files packed in its jar. Is that creating any sort of problem when SpringBoot tries to read the files from the config folder while executing my project jar?
Update
Now I am keeping only a single properties file suppose ABC.properties outside the jar in the same directory . I am using the name "ABC" instead of "application". I am using the below command
java -Dserver.log.dir=/path/to/log/dir -jar myjar.jar --server.port=9090 --spring.config.name=ABC --prop1=val1
I have overriden a property in my external property file but I don't see the overriden value being used when I run the application. My new questions are
Is there anything wrong with the command-line?
I have the following line in xml bean configuration
<context:property-placeholder location="ABC.properties" />. Is this causing any sort of problems to detect and use the external properties?
If the above is true and I have to remove the line how will SpringBoot understand from where the property values are to be imported?
(Not related directly to the question) Is there a order that I need to follow while giving command line arguments?
Spring boot has explicit indicate how to write this external configuration. See doc ref here: https://docs.spring.io/spring-boot/docs/2.5.2/reference/htmlsingle/#features.external-config.files
Basically, you need to specify the location like this:
--spring.config.location=classpath:/somefolder/somefile.properties
--spring.config.location=file:./somefolder/somefile.properties
From your command line, it seems that you are missing one dash, and also using a wrong format of file schema.
Q1: Why are the files in the config directory not being read even after explicitly mentioning where to look?
A1: If your config folder is beside your jar file, your command should be like
java -jar myjar.jar --spring.config.location=file:./config/PROP1.properties
meanwhile, you can also use the full path to target your config file.
Q2: If I don't mention the properties files names explicitly as an argument then they won't be picked up even when they are in the config directory
A2: No. See this doc, Spring Boot will try to find config files from the four places:
A /config subdir of the current directory.
The current directory
A classpath /config package
The classpath root
If spring.config.location is not set, files in these folders named application.properties will be treated as valid config file. You can change the accepted file name by setting property spring.config.name.
Q3 About Config File Priority
A3 As described in the doc mentioned earlier, if same name properties appear in different config files, locations higher in the list override lower items.
Another tip, it will be better to remove config files inside the jar file, so you can get a full view of configuration just in one place (the externalized config folder);
I'm using the log4j2 library to manage the logging process.
I created a configuration file named log4j2.xml containing the Appenders and Loggers configurations. Then, I defined a Logger in each class
private static Logger my_logger = LogManager.getLogger(my_class);
I did not specify anywhere the name of the conf file, so I think that the library implicitly get and read it.
Now, I need to provide my application in the form of a jar file, so I need to make the config file available so that the user can modify and configure it.
In my case, I suggest to create a XXX folder at the level of the jar file, containing all the configuration files used by my app.
My question is how can I say to the app "get XXX/log4j2.xml" rather than the xml contained into the jar.
that config file must be located in the class path, if you want the app to read the configuration from any other location then you need to specify that using
PropertyConfigurator.configure("/myPath/log4j.properties");
Make any folder and put your property or xml file in that. In order to read the property file you can do something like this:
Properties objProperties = new Properties();
<your-class>.class.getClassLoader().getResource("folder/log4j.properties");
objProperties.load(isFile);
or, Also this:
InputStream ist = Thread.currentThread().getContextClassLoader().getResourceAsStream("folder/log4j.properties");
In case of java web application please use the link
I had a similar task a few weeks ago.
I solved it this way:
Store a template of your log4j2.xml inside your jar files resource folder
When running your application, check for a file named log4j2.xml in the jar files current directory
If there is one, use that to create your logger
If not, copy your template from within your jar to the jar files directory and then use that to create your logger.
Cheers
I am doing a maven application. If I place my log4j2.xml configuration file in the src/resources folder it works fine.
However, I need to store it outside the application. How can I call the location of the log4j2.xml file if it is stored, for example, on my desktop? Should I create a file in the src/resource folder that reads in my log file location and how can I do that?
The Log4j2 configuration file doesn't need to be in the classpath.
You can specify a relative or a full path with system property -Dlog4j.configurationFile=path/to/log4j2.xml.
See also the Log4j2 FAQ page.
In my web application I have to send email to set of predefined users like finance#xyz.example, so I wish to add that to a .properties file and access it when required. Is this a correct procedure, if so then where should I place this file? I am using Netbeans IDE which is having two separate folders for source and JSP files.
It's your choice. There are basically three ways in a Java web application archive (WAR):
1. Put it in classpath
So that you can load it by ClassLoader#getResourceAsStream() with a classpath-relative path:
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input = classLoader.getResourceAsStream("foo.properties");
// ...
Properties properties = new Properties();
properties.load(input);
Here foo.properties is supposed to be placed in one of the roots which are covered by the default classpath of a webapp, e.g. webapp's /WEB-INF/lib and /WEB-INF/classes, server's /lib, or JDK/JRE's /lib. If the propertiesfile is webapp-specific, best is to place it in /WEB-INF/classes. If you're developing a standard WAR project in an IDE, drop it in src folder (the project's source folder). If you're using a Maven project, drop it in /main/resources folder.
You can alternatively also put it somewhere outside the default classpath and add its path to the classpath of the appserver. In for example Tomcat you can configure it as shared.loader property of Tomcat/conf/catalina.properties.
If you have placed the foo.properties it in a Java package structure like com.example, then you need to load it as below
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input = classLoader.getResourceAsStream("com/example/foo.properties");
// ...
Note that this path of a context class loader should not start with a /. Only when you're using a "relative" class loader such as SomeClass.class.getClassLoader(), then you indeed need to start it with a /.
ClassLoader classLoader = getClass().getClassLoader();
InputStream input = classLoader.getResourceAsStream("/com/example/foo.properties");
// ...
However, the visibility of the properties file depends then on the class loader in question. It's only visible to the same class loader as the one which loaded the class. So, if the class is loaded by e.g. server common classloader instead of webapp classloader, and the properties file is inside webapp itself, then it's invisible. The context class loader is your safest bet so you can place the properties file "everywhere" in the classpath and/or you intend to be able to override a server-provided one from the webapp on.
2. Put it in webcontent
So that you can load it by ServletContext#getResourceAsStream() with a webcontent-relative path:
InputStream input = getServletContext().getResourceAsStream("/WEB-INF/foo.properties");
// ...
Note that I have demonstrated to place the file in /WEB-INF folder, otherwise it would have been public accessible by any webbrowser. Also note that the ServletContext is in any HttpServlet class just accessible by the inherited GenericServlet#getServletContext() and in Filter by FilterConfig#getServletContext(). In case you're not in a servlet class, it's usually just injectable via #Inject.
3. Put it in local disk file system
So that you can load it the usual java.io way with an absolute local disk file system path:
InputStream input = new FileInputStream("/absolute/path/to/foo.properties");
// ...
Note the importance of using an absolute path. Relative local disk file system paths are an absolute no-go in a Java EE web application. See also the first "See also" link below.
Which to choose?
Just weigh the advantages/disadvantages in your own opinion of maintainability.
If the properties files are "static" and never needs to change during runtime, then you could keep them in the WAR.
If you prefer being able to edit properties files from outside the web application without the need to rebuild and redeploy the WAR every time, then put it in the classpath outside the project (if necessary add the directory to the classpath).
If you prefer being able to edit properties files programmatically from inside the web application using Properties#store() method, put it outside the web application. As the Properties#store() requires a Writer, you can't go around using a disk file system path. That path can in turn be passed to the web application as a VM argument or system property. As a precaution, never use getRealPath(). All changes in deploy folder will get lost on a redeploy for the simple reason that the changes are not reflected back in original WAR file.
See also:
getResourceAsStream() vs FileInputStream
Adding a directory to tomcat classpath
Accessing properties file in a JSF application programmatically
Word of warning: if you put config files in your WEB-INF/classes folder, and your IDE, say Eclipse, does a clean/rebuild, it will nuke your conf files unless they were in the Java source directory. BalusC's great answer alludes to that in option 1 but I wanted to add emphasis.
I learned the hard way that if you "copy" a web project in Eclipse, it does a clean/rebuild from any source folders. In my case I had added a "linked source dir" from our POJO java library, it would compile to the WEB-INF/classes folder. Doing a clean/rebuild in that project (not the web app project) caused the same problem.
I thought about putting my confs in the POJO src folder, but these confs are all for 3rd party libs (like Quartz or URLRewrite) that are in the WEB-INF/lib folder, so that didn't make sense. I plan to test putting it in the web projects "src" folder when i get around to it, but that folder is currently empty and having conf files in it seems inelegant.
So I vote for putting conf files in WEB-INF/commonConfFolder/filename.properties, next to the classes folder, which is Balus option 2.
Ex: In web.xml file the tag
<context-param>
<param-name>chatpropertyfile</param-name>
<!-- Name of the chat properties file. It contains the name and description of rooms.-->
<param-value>chat.properties</param-value>
</context-param>
And chat.properties you can declare your properties like this
For Ex :
Jsp = Discussion about JSP can be made here.
Java = Talk about java and related technologies like J2EE.
ASP = Discuss about Active Server Pages related technologies like VBScript and JScript etc.
Web_Designing = Any discussion related to HTML, JavaScript, DHTML etc.
StartUp = Startup chat room. Chatter is added to this after he logs in.
It just needs to be in the classpath (aka make sure it ends up under /WEB-INF/classes in the .war as part of the build).
You can you with your source folder so whenever you build, those files are automatically copied to the classes directory.
Instead of using properties file, use XML file.
If the data is too small, you can even use web.xml for accessing the properties.
Please note that any of these approach will require app server restart for changes to be reflected.
Assume your code is looking for the file say app.properties. Copy this file to any dir and add this dir to classpath, by creating a setenv.sh in the bin dir of tomcat.
In your setenv.sh of tomcat( if this file is not existing, create one , tomcat will load this setenv.sh file.
#!/bin/sh
CLASSPATH="$CLASSPATH:/home/user/config_my_prod/"
You should not have your properties files in ./webapps//WEB-INF/classes/app.properties
Tomcat class loader will override the with the one from WEB-INF/classes/
A good read:
https://tomcat.apache.org/tomcat-8.0-doc/class-loader-howto.html
Where to put the logback.xml file in Tomcat when we want to have it configurable?
And how to make it accessible for the Java application(s) running inside?
You typically want to have logback.xml on the classpath. Per the Logback FAQ:
For web-applications, configuration files can be placed directly under WEB-INF/classes/.
You therefore need to put it in:
/webapps/your-app/WEB-INF/classes/
Logback has some conventions for where it looks for it. Those are documented here.
Logback tries to find a file called logback.groovy in the classpath.
If no such file is found, logback tries to find a file called logback-test.xml in the classpath.
If no such file is found, it checks for the file logback.xml in the classpath..
If neither file is found, logback configures itself automatically using the BasicConfigurator which will cause logging output to be
directed to the console.
But you can also tell it where to find the file.
You may specify the location of the default configuration file with a
system property named "logback.configurationFile". The value of this
property can be a URL, a resource on the class path or a path to a
file external to the application.
java -Dlogback.configurationFile=/path/to/config.xml chapters.configuration.MyApp1
Note that the file extension must be ".xml" or ".groovy". Other
extensions are ignored. Explicitly registering a status listener may
help debugging issues locating the configuration file.