set an absolute path for a "log4j.properties" file - java

I am using apache commons + log4j for my web app.
normally log4j needs a configuration file inside the classpath; but I need to delegate the logging configuration to an external file (I need to deploy a .war in an environment, but the log configurations (max size, position, etc) it's up to a second team.
I have a commons-logging.properties in my classpath
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
# log4j.configuration=/absolute/path/where/external/logs/are/log4j.properties
unfortunately, the commented line doesn't work.
Is there a way to set up log4j with an external configuration file?

You can set it as a system property log4j.configuration property .. for example in J2SE app
java -Dlog4j.configuration=file:/path/to/log4j.properties myApp
Note, that property value must be a URL.
For more read section 'Default Initialization Procedure' in Log4j manual.
It's also possible letting a ServletContextListener set the System properties:
import java.util.Enumeration;
import javax.servlet.*;
public class SystemPropertiesHelper implements
javax.servlet.ServletContextListener {
private ServletContext context = null;
public void contextInitialized(ServletContextEvent event) {
context = event.getServletContext();
Enumeration<String> params = context.getInitParameterNames();
while (params.hasMoreElements()) {
String param = (String) params.nextElement();
String value =
context.getInitParameter(param);
if (param.startsWith("customPrefix.")) {
System.setProperty(param, value);
}
}
}
public void contextDestroyed(ServletContextEvent event) {
}
}
And then put this into your web.xml (should be possible for context.xml too)
<context-param>
<param-name>customPrefix.property</param-name>
<param-value>value</param-value>
<param-type>java.lang.String</param-type>
</context-param>
<listener>
<listener-class>servletUtils.SystemPropertiesHelper</listener-class>
</listener>
I got this from this listener code from answer .
I hope this could help you!

You can use a jvm parameter indicating the configuration file path:
-Dlog4j.configuration=absolute path
example with an absolute path:
java -Dlog4j.configuration="file:/dir1/log4j.properties"

Set the system property log4j.configuration=/abslute/or/relative/path_to_file_name
commons logging only needs to know what logging implementation it's using, the logging implementation it self is configured in whatever way it is always configured in.
This is documented in the log4j manual.

If you are using the Spring Framework, you can use the org.springframework.web.util.Log4jConfigListener.
You only need to specify the parameter log4jConfigLocation with the file location (using the URL for File). e.g.:
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>file:/absolute/path/where/external/logs/are/log4j.properties</param-value>
</context-param>
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>1000</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
You can specify the log4jRefreshInterval, the interval between config file refresh checks, in milliseconds.
See more in the javadoc of org.springframework.web.util.Log4jWebConfigurer.

Related

No log output on contextDestroyed using ServletContextListener & SLF4J

I am trying to write a message to the logger that a (Vaadin) servlet has stopped, this using SLF4J and Log4j2.
For this I am using a ServletContextListener which logs a message when the application has started. However I have been unable to get any output when logging inside the contextDestroyed method... Here is my implementation:
#WebListener
public class VaadinLogger implements ServletContextListener {
private static final Logger logger = LoggerFactory.getLogger(VaadinLogger.class);
#Override
public void contextInitialized(ServletContextEvent contextEvent) {
// Remove appenders from JUL loggers
SLF4JBridgeHandler.removeHandlersForRootLogger();
// Install bridge
SLF4JBridgeHandler.install();
// Get servlet context
ServletContext context = contextEvent.getServletContext();
// Retrieve name
String name = context.getServletContextName();
// Log servlet init information
logger.info("Start \"{}\"", name);
}
#Override
public void contextDestroyed(ServletContextEvent contextEvent) {
// Get servlet context
ServletContext context = contextEvent.getServletContext();
// Retrieve name
String name = context.getServletContextName();
// Log servlet destroy information
logger.info("End \"{}\"{}", name, System.lineSeparator()));
// Uninstall bridge
SLF4JBridgeHandler.uninstall();
}
}
At this point, I'm guessing this is probably because at the point contextDestroyed is called, logging is no longer possible because they have already been destroyed by the garbage collector.
So now my question is, is it possible to either log that the servlet has stopped before the context is destroyed, or make the contextlistener execute before log4j2 loggers are destroyed?
Thanks in advance!
Since log4j 2.14.1, you can disable the auto-shutdown and add a listener to stop the logger.
<context-param>
<!-- auto-shutdown stops log4j when the web fragment unloads, but that
is too early because it is before the listeners shut down. To
compensate, use a Log4jShutdownOnContextDestroyedListener and
register it before any other listeners which means it will shut
down *after* all other listeners. -->
<param-name>isLog4jAutoShutdownDisabled</param-name>
<param-value>true</param-value>
</context-param>
<listener>
<!-- ensure logging stops after other listeners by registering
the shutdown listener first -->
<listener-class>
org.apache.logging.log4j.web.Log4jShutdownOnContextDestroyedListener
</listener-class>
</listener>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
First, do you have the log4j 2.x api, core and web jars in your classpath? The log4j-web-2.x.jar also registers a context listener used to shut down the logging subsystem when the web app is unloaded.
If your listener runs after that you won't be able to log anything anymore.
You can check what's happening by setting <Configuration status="trace" ... in your log4j2 configuration file.
log4j2 (log4j-web-xx.jar) comes with a web-fragment. This fragment contains a ServletContextListener. The order of listeners are depend of the initialization (see Servlet Specification). Default: your application first then others.
Your can change the order to specify an <absolut-ordering> in your web.xml:
<web-app>
<absolute-ordering>
<name>log4j</name>
<others/>
</absolute-ordering>
See also: servlet-30-web-fragmentxml

class path resource [classpath*:xxxxx.properties] cannot be opened because it does not exist

I am trying to implement sort of conditional import bean context files within current spring application. To make this work, I use similar solution in SO to extend the ApplicationContextInitializer<ConfigurableApplicationContext>
package com.mydepartment.app.util;
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment env = applicationContext.getEnvironment();
// load from web.xml context-param
String propertyFileClassPath = env.getProperty("propertyFileClassPath");
try {
MutablePropertySources sources = env.getPropertySources();
sources.addFirst(new ResourcePropertySource(new ClassPathResource(propertyFileClassPath)));
} catch (IOException ioException) {
LOG.info(ioException.getMessage());
LOG.error( "Loading resource failed..", ioException);
}
}
}
And this is want I add into web.xml to make current application to be able to load the properties file which contains the properties which defines the condition
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.mydepartment.app.util.MyApplicationContextInitializer</param-value>
</context-param>
<context-param>
<param-name>propertyFileClassPath</param-name>
<param-value>classpath*:application-env.properties</param-value>
</context-param>
.....
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
the application-env.properties resides in
%war_file_name%\WEB-INF\classes\application-env.properties
but above configuration/coding returns following error in both WebLogic and Websphere, which makes me really miserable for the fix:
2015-10-15 13:23:12,625 -- INFO -- com.mydepartment.app.util.MyApplicationContextInitializer -- class path resource [classpath*:application-env.properties] cannot be opened because it does not exist
2015-10-15 13:23:12,626 -- ERROR -- com.mydepartment.app.util.MyApplicationContextInitializer -- Loading resource failed..
java.io.FileNotFoundException: class path resource [classpath*:application-env.properties] cannot be opened because it does not exist
I tried several pattern to establish the classpath but all failed, such as:
classpath*:application-env.properties
file:/WEB-INF/classes/application-env.properties
/WEB-INF/classes/application-env.properties
../classes/application-env.properties
Potentially I want the application to load the init properties from properties file inside the application, instead of system properties or server properties. But I don't see the necessity to add the properties file into the server's classpath.
I don't see any reason that the application server blocks access to this internal properties file.
When we use ClassPathResource, we directly specify the resource path. Try the following:
ClassPathResource cr = new ClassPathResource("application-env.properties");

Java, Spring Tests, how do I configure logging normally defined in web.xml?

I have the following defined for my test:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration("/App/webapp")
#ContextConfiguration({
"classpath:/config/AConfig.xml",
"classpath:/config/BConfig.xml",
"classpath:/config/CConfig.xml",
"classpath:/config/DConfig.xml"
})
public class MyTests{
...
}
In web.xml I use the same configuration plus additional configuration, for instance to configure filters and listeners. How can I enable these when doing tests?
It would be great if Spring just used the same web.xml but I guess that might not be an option.
I have this defined in web.xml:
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:/config/log4j.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
Is there a way to configure logging from a a contextConfig.xml ( bean definition ) instead?
http://codebyexample.info/2012/03/31/how-log4j-and-junit-became-friends/
that may be helpful, in particular the part where the author does:
Logger.getRootLogger().addAppender(someAppender)
You should be able to access Logger.getRootLogger() and configure it there.
If you want to use the properties file, you can do:
PropertyConfigurator.configure("/config/log4j.properties");
How to configure log4j.properties for SpringJUnit4ClassRunner?
or manually configure it like:
Logger rootLogger = Logger.getRootLogger();
rootLogger.setLevel(Level.INFO);
rootLogger.addAppender(new ConsoleAppender(
new PatternLayout("%-6r [%p] %c - %m%n")));
These configurations should be done once before all classes are run, though I don't think they harm too much if you do them in a #Before method.

Precedence of properties: system vs. deployment descriptor vs. properties file

If I have system property which I pass my container (e.g. Tomcat) like the following:
-Dmy.property=myValueOne
and a property with the same key defined in my web.xml:
<context-param>
<param-name>my.property</param-name>
<param-value>myValueTwo</param-value>
</context-param>
... and a property with the same key defined in one of my config*.properties files:
my.property=myValueThree
Which value will this property have?
myValueOne, myValueTwo or myValueThree?
If you have several properties with the same key, is there a hierarchy which defines which kind of property overwrites which other kind of property?
Simply said all three will be available and you can use SpEL to obtain the value of each.
#{systemProperties['my.property']} // myValueOne
#{servletContextInitParams['my.property'] // myValueTwo
The properties depend on how you are loading them (a #PropertySource or `
However you probably want to know what happens if you use a placeholder and you have the situation you have.
<property name="myProperty" value="${my.property}" />
Assuming you use the default (and are on Spring 3.1 or up) the following resources are consulted in order, in a web based environment.
Property Files
ServletConfig Init Params (
ServletContext Init Params
Jndi
System Properties (specified by -D)
Environment Properties
So in your case the ${my.property} will resolve to myValueThree. That is if you preserve the defaults. If you specify local-override="true" the property files are consulted last and the value would be myValueTwo.
Links
StandardServletEnvironment javadoc
StandardEnvironment javadoc
PropertySourcesPlaceholderConfigurer javadoc

How to set the Profile using application.properties in Spring?

I would like to set the Profile using application.properties file with the entry:
mode=master
How to set spring.profiles.active in my context.xml file? init-param works only in a web.xml context.
<init-param>
<param-name>spring.profiles.active</param-name>
<param-value>"${mode}"</param-value>
</init-param>
There are a few ways to change active profiles, none of which take directly from a properties file.
You can use the <init-param> as you are doing in your question.
You can provide a system parameter at application startup
-Dspring.profiles.active="master"
You can get the ConfigurableEnvironment from your ApplicationContext and setActiveProfiles(String...) programmatically with context.getEnvironment().setActiveProfiles("container");
You can use an ApplicationListener to listen to context initialization. Explanations on how to do that here. You can use a ContextStartedEvent
ContextStartedEvent event = ...; // from method argument
ConfigurableEnvironment env = (ConfigurableEnvironment) event.getApplicationContext().getEnvironment();
env.setActiveProfiles("master");
You can get the value "master" from a properties file as you see fit.
You can use either a environment variable, system variable (-D option for the JVM or application) or put it in JNDI (java:comp/env/. You cannot however put it in a properties file, as it is needed before the that specific properties file is read.
There is more information in the #Profile javadocs.
Another solution is to create your own ApplicationContextInitializer implementation which reads a certain file and activates the given profile.
You also can achieve this indirectly via System.setProperty:
// spring.profiles file: profile1,profile2
String anotherProfiles = Files.readString(Path.of("spring.profiles")); // or any other file
// Even some logic can be applied here to anotherProfiles
System.setProperty("spring.profiles.include", "dev," + anotherProfiles)
This sample can be rewritten a bit to read your application.properties file and take specified profiles for Spring.

Categories