I am using log4j for logging activities in my application.I want to take the log path from data base.
now I need to configure my log4j properties dynamically.
can we do it on the fly we change the log4h logging path..
Please suggest.
Thanks
You should create a class what loads at the startup and configurate the log4j.
Here is a code what I used in a JavaEE project, what loads the configuration file from an outer directory:
public class InitListener implements ServletContextListener {
public InitListener() {
}
public void contextInitialized(ServletContextEvent sce) {
try {
File file = null;
file = new File(System.getProperty("catalina.base") + "/conf/query-log4j.xml");
DOMConfigurator.configure(file.toURL());
System.out.println("Log4J successfully configured!");
} catch(Exception e) {
System.out.println("There was an error when initialize the Log4J config!");
e.printStackTrace();
throw new RuntimeException(e);
}
}
public void contextDestroyed(ServletContextEvent sce) {
}
}
If you were using MentaLog, all you have to do was that:
yourLogger.setFilename("newfilenamehere.log");
Your log would be automatically re-opened with the new name. In my personal opinion, programmatic configuration is the way to go over XML and/or annotations. It provides unmatched flexibility and easy of use.
Create seperate properties file to hold Specific Enviroment related settings example:
**uatLog4j.properties**
#######################
UAT Settings
#######################
{Add your Settings here}
And another for sy production enviroment.
**productionLog4j.properties**
########################
PRODUCTION settings
########################
{Add your Settings here}
And then using say the IP Address or the Server Name to determine the deployed platform, pass the path from the database to the required enviroment properties file as required.
Alternatively you can use LogManager to retreive Logger instances or to operate on the current LoggerRepository. See Javadoc and a RepositorySelecter example.
NOTE:
You can achieve the same using XML.
Related
I'm using org.apache.commons:commons-configuration2:2.5 to read config parameters from a config file. In my application.properties, I want to specify default values for environment variables in case one of these has not been set, e.g. like so (similar as with Spring):
idp.username=${env:IDP_USERNAME:defaultUsername}
However, while setting the env variable works fine, specifying a default value like this is not working. Unfortunately, I can't find anything related to this in the commons-configuration2 documentation. Is this possible without explicitly checking for a missing value in the Java code? Does Spring Boot use a certain lib for this that I could also use? I don't want to bloat up my simple app with the Spring framework only to have this kind of configuration.
Simplified code example I'm currently using (here, I obviously could check each property I retrieve and replace it with a default, but I'd like to avoid this and handle it directly in the config file):
private void loadConfigFile() {
logger.info("Attempting to load `application.properties`...");
Configurations configBuilder = new Configurations();
try {
Configuration props = configBuilder.properties(new File("application.properties"));
// application config
this.conf = new Config();
this.conf.setAPIEndpoint(props.getString("api.endpoint"));
this.conf.setMaxConnections(props.getInt("api.maxConnections"));
...
logger.info("Config successfully loaded!");
} catch (Exception e) {
logger.error("Could not load `application.properties`: {}", e.getMessage());
System.exit(1);
}
}
Any suggestions?
Commons Configuration provides a way to do custom interpolation. This answer shows an example. Your Lookup implementation would need to handle parsing everything past the prefix and then doing the lookup and providing the default value.
Helo masters, I have to create a JNDI Datasource dynamically, I tried to do it with a listener called SetupApplicationListener. Here is the beginning of WEB-LIB/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee">
<display-name>pri-web</display-name>
<!-- Listeners -->
<listener>
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
</listener>
<listener>
<listener-class>myapp.SetupApplicationListener</listener-class>
</listener>
The code of the listener:
public class SetupApplicationListener implements ServletContextListener {
public static Log LOG = null;
public void contextInitialized(ServletContextEvent ctx){
try {
createOracleDataSource();
.....
}
}
private void createOracleDataSource() throws SQLException, NamingException {
OracleDataSource ds = new OracleDataSource();
ds.setDriverType(...);
ds.setServerName(...);
ds.setPortNumber(...);
ds.setDatabaseName(...);
ds.setUser(...);
ds.setPassword(...);
new InitialContext().bind("java:comp/env/jdbc/myDS", ds);
}
.....
}
And there is the error:
[ERROR] 29/01/2013 09:44:50,517 (SetupApplicationListener.java:86) -> Error
javax.naming.NamingException: Context is read only
at org.apache.naming.NamingContext.checkWritable(NamingContext.java:903)
at org.apache.naming.NamingContext.bind(NamingContext.java:831)
at org.apache.naming.NamingContext.bind(NamingContext.java:171)
at org.apache.naming.NamingContext.bind(NamingContext.java:187)
at org.apache.naming.SelectorContext.bind(SelectorContext.java:186)
at javax.naming.InitialContext.bind(InitialContext.java:359)
at myapp.SetupApplicationListener.createOracleDataSource(SetupApplicationListener.java:102)
Can I set the read-only properties of the Context to "true"? Thanks! :)
Tomcat 6.0
Oracle 11g
jdk1.5
EDIT: Don't need to be dynamically, i have to define a jndi datasource internally I can't modify the server files because it is a shared server. It must be jndi because other modules use it in that way, thanks.
If you need to create a datasource dynamically is there really any need for a JNDI lookup? JNDI is designed to make the connection external to the application, while in your scenario its tightly coupled to the application due to a legitimate requirement. Why not just use a JDBC connection?
You need to create a ServletContextListener and there you can make the InitialContext writable - it's not the way it should be done, but if you really need it, this is one way you can do it.
This also works with Java Melody!
protected void makeJNDIContextWritable(ServletContextEvent sce) {
try {
Class<?> contextAccessControllerClass = sce.getClass().getClassLoader().loadClass("org.apache.naming.ContextAccessController");
Field readOnlyContextsField = contextAccessControllerClass.getDeclaredField("readOnlyContexts");
readOnlyContextsField.setAccessible(true);
Hashtable readOnlyContexts = (Hashtable) readOnlyContextsField.get(null);
String context = null;
for (Object key : readOnlyContexts.keySet()) {
String keyString = key + "";
if (keyString.endsWith(sce.getServletContext().getContextPath())) {
context = keyString;
}
}
readOnlyContexts.remove(context);
} catch (Exception ex) {
ex.printStackTrace();
}
}
I haven't got this problem before since I usually defined JNDI in application server(tomcat, weblogic and etc). Just like what Kevin said, this is exactly what JNDI was designed for; separating datasource config from your source code and retrieving JNDI resources through lookup and inject;
Back to your question, I think tomcat has every strict rules on modifying JNDI at runtime. In another word, you cannot re-bind or remove jndi from Context. If you go through the tomcat specification you will probably see some thing about jndi lookup but no re-bind.
From section EE.5.3.4 of the EE 6 platform specification (JSR 316):
The container must ensure that the application component instances
have only read access to their naming context. The container must
throw the javax.naming.OperationNotSupportedException from all the
methods of the javax.naming.Context interface that modify the
environment naming context and its subcontexts.
Note that "their naming context" in this section is referring to java:comp.
I solved this problem when found that I was closing environmentContext object
For example:
Context context=new InitialContext();
Context environmentContext=(Context) context.lookup("java:comp/env");
And my code was:
environmentContext.close();
After removing close function from environmentContext problem was solded for me;
I also had this problem, but being new to Tomee, I didn't know that there is a simple solution. When I deployed my web app to the webapps folder, the app worked fine, but when I deployed it to a service folder, I got the same abort. The problem was that the folder name did not match the war name (minus the .war). Once I fixed that, the app worked fine. Make sure the war name, folder name and service name are identical. This problem produces several different errors, including Context is read only and Error merging Java EE JNDI entries.
I solved this issue by setting useNaming="false" in my context.xml.
From the documentation:
useNaming : Set to true (the default) to have Catalina enable a JNDI InitialContext for this web application that is compatible with Java2 Enterprise Edition (J2EE) platform conventions.
How: To disable Tomcat JARScanner?
Why: To stop Tomcat scan every .jar in my LIB folder.
According to documentation it says that it is possible to disable it within context.xml. But it seems to not be working. (May be I am missing something)
I made an exhaustive search in forums and could not find the solution.
This is in context.xml (not working yet):
<JarScanner scanClassPath="false" scanAllFiles="false" scanAllDirectories="false"></JarScanner>
Thanks in advance.
You should add the JarScanner element as a child of the root Context element in the context.xml file.
I have this kind of META-INF/context.xml file in the war file for disabling the JarScanner:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<JarScanner scanClassPath="false" scanAllFiles="false" scanAllDirectories="false"/>
</Context>
you can disable the JarScanner globally for user-definted patterns by opeining the file at
%TOMCAT_HOME%/conf/catalina.properties
and add a filename pattern to tomcat.util.scan.StandardJarScanFilter.jarsToSkip list.
For example, if you want to disable jar scanning completely you could add:
tomcat.util.scan.StandardJarScanFilter.jarsToSkip=\
*.jar,\
NOTE: this may of course lead to issues if you're employing JSTL, as templates won't be found by the scanner
in your java app add this :
#Bean
public TomcatServletWebServerFactory tomcatServletFactory() {
return new TomcatServletWebServerFactory() {
#Override
protected void postProcessContext(final Context context) {
((StandardJarScanner) context.getJarScanner()).setScanManifest(false);
}
};
}
This is what I did for Spring Boot.
Basically, append a new ignored jar file to the existing list of jars to ignore. This way, you don't totally disable the scanner, affecting who knows what else.
#Configuration
public class Config {
#Bean
public ServletWebServerFactory servletContainer() {
return new TomcatServletWebServerFactory() {
#Override
protected void postProcessContext(Context context) {
// db2 puts a ref to pdq.jar in the manifest, and tomcat then tries to find it, but it doesn't exist.
// The jar isn't needed, so we just disable looking for it. You could also remove it from the manifest,
// but that prob needs to be done after the build process.
JarScanFilter jarScanFilter = context.getJarScanner().getJarScanFilter();
if (jarScanFilter instanceof StandardJarScanFilter) {
StandardJarScanFilter filter = (StandardJarScanFilter) jarScanFilter;
String oldTldSkip = filter.getTldSkip();
String newTldSkip = oldTldSkip == null || oldTldSkip.trim().isEmpty() ? "pdq.jar" : oldTldSkip + ",pdq.jar";
filter.setTldSkip(newTldSkip);
} else {
logger.warn("Unable to disable the tomcat jar scanner for pdq.jar. You may see a FileNotFound exception complaining of not finding a db2 pdq.jar file. You can probably ignore the error. Ref: https://stackoverflow.com/questions/11656596/how-to-disable-tomcat-jarscanner");
}
}
};
}
}
I'm writing a Java servlet that needs to read some site-specific
configuration data; I would like it to be easily accessible/modifiable
by the sysadmins at deployment time. There is no sensible default,
so the data has to be provided by the site admin.
It consists of a few string key/value pairs (think Properties).
It would only be read once (at initialization time).
I'm aware of this SO question
and the ServletContext.getInitParameter() mechanism, but as far as
my understanding goes, they require the data to be bundled in the
servlet package (either as a properties file, or specified in the
web.xml), which makes it inconvenient to upgrade the servlet code.
Is there any "standard" interface for a servlet to get this kind of
key/value configuration data? It would be ok if the programming
interface is the same everywhere, but the actual way of setting the
configuration data depends on the actual servlet container being used.
I'm looking preferably at portable solutions, but I'd be content with
something that only works in Tomcat and Jetty.
The recommended way to configure an application server for a web application is per JNDI.
Every application server (including Jetty and Tomcat) allows you to configure JNDI parameters.
For Jetty you can add the following to your jetty.xml to add the JNDI parameter param.file:
<!-- JNDI java:comp/env -->
<New id="param.file" class="org.mortbay.jetty.plus.naming.EnvEntry">
<Arg>param.file</Arg>
<Arg type="java.lang.String"><SystemProperty name="jetty.home" default="."/>etc/config.properties</Arg>
<Arg type="boolean">true</Arg>
</New>
Then in your servlet you can read the JNDI parameter:
import javax.naming.InitialContext;
import javax.naming.NamingException;
...
public Object readJndi(String paramName) {
Object jndiValue = null;
try {
final InitialContext ic = new InitialContext();
jndiValue = ic.lookup("java:comp/env/" + paramName);
} catch (NamingException e) {
// handle exception
}
return jndiValue;
}
public String getConfigPath() {
return (String) readJndi("param.file");
}
The way to set JNDI values differs for other application servers but the code to read the configuration is always the same.
The Servlet init parameters are the right (and standardized) way of defining properties which can be configured by the administrator. Many of the application servers provide a GUI backend where the parameters can be configured.
For an example for Tomcat, see Defining Tomcat servlet context parameters
Configure the external location of the properties - either via a jvm argument (when starting the servlet container), or in the web.xml
in the external location use config.properties and read it with java.util.Properties
You may take Preferences or hack with user.home, user.dir, etc. But for a few key/value keep things simple.
Write a small Singleton to wrap around Properties and load them from a fix & absolute location
public class LocalConfig extends Properties {
public static LocalConfig $ = new LocalConfig();
private LocalConfig() throws IOException {
load(new File("/etc/myconfig.properties"));
}
}
The problem is, whenever you change the log4j.properties/log4j.xml, you need to restart the tomcat [ or say any other server ]. Is there any workaround of reloading the log4j configuration?
From http://logging.apache.org/log4j/1.2/faq.html#3.6
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.
Said that, I've successfully used PropertyConfigurator#configureAndWatch method in a Java EE environment (Sun One Web Server, not Tomcat).
As of log4j 2.x you can reload the config periodically, in this example every 30 seconds:
<configuration monitorInterval="30">
Please take a look here for more information on log4j 2.x configuration:
You can write a little initializer code with the following short steps:
listen for the "BEFORE_START_EVENT",
when the event happens (once per Tomcat restart), start log4j using the configureAndWatch method
also don't forget to install a shutdown hook to cleanup the watcher thread
See this blog post for details - reload log4j configuration in tomcat
They also moved it to github.
Update:If you are using lg4j2.xml, the configuration is the only thing you will need for log4j to be managed at runtime
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO" monitorInterval="30">
<Loggers>
-------
</Loggers>
</Configuration>
Monitor interval 30 loads the log4j changes every 30 seconds.
Below solution is if you are on older version of log4j.
Yes you can change the log4j level at run time without the need to restart the server provided you are using spring.
public class OptionalLog4jConfigurer extends Log4jConfigurer implements
InitializingBean {
public static final Long DEFAULT_REFRESH = 30000L;
private static final Log LOG = LogFactory
.getLog(OptionalLog4jConfigurer.class);
private String configLocation;
private Long refreshInterval;
public OptionalLog4jConfigurer(final String configLocation,
final Long refreshInterval) {
this.configLocation = configLocation;
if (refreshInterval == null) {
this.refreshInterval = DEFAULT_REFRESH;
}
else {
this.refreshInterval = refreshInterval;
}
}
public void afterPropertiesSet() throws Exception {
if (!StringUtils.isEmpty(this.configLocation)) {
LOG.info("Log4J configuration is being customized.");
this.initLoggingInternal();
}
else {
LOG
.info("Using default Log4J configuration. No customization requested");
}
}
public String getConfigLocation() {
return this.configLocation;
}
public Long getRefreshInterval() {
return this.refreshInterval;
}
}
Then do these changes to applicationContext.
<bean id="optionalLog4jInitialization" class="com.skg.jetm.OptionalLog4jConfigurer">
<constructor-arg index="0" type="java.lang.String" value="${log4j.configuration}" />
<constructor-arg index="1" type="java.lang.Long" value="100" />
</bean>
Full code and explanation can be found here
Changing log4j Level dynamically
You can create a strut action or a servlet which reload the properties file. So after editing the log4j.properties file, you will need to call the servlet to reload it.
For example:
public class Log4JServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
protected static Logger log = Logger.getLogger(Log4JTestServlet.class);
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("Reload Log4J prop file");
String path = "C:\\GlassFishESBv22\\glassfish\\domains\\domain1\\config\\log4j.properties";
PropertyConfigurator.configure(path);
/*
log.debug("debug message");
log.info("info message");
log.warn("warn message");
log.error("error message");
log.fatal("fatal message");
*/
}
}
Another way is to configure Spring Framework's Log4jConfigListener in web.xml
The Guido Garcia answer is quite on target.
Log4j 1 offers a way of reloading log4j configuration in a non JEE thread safe maner.
So if you are in a JEE continer, you can solve your problem trivially by:
(A) Create your #Singleton ejb timer to periodically scan your log4j.properties file
(b) Look at the implementaiton of the log4j log watch given by log4j.
What it does when it is time to relaoad a file is quite simply and conveniently, the following:
new PropertyConfigurator().doConfigure(filename,LogManager.getLoggerRepository());
Just do the same, if the time stamp on you configuration file changes.
That is it.
Another Method is to configure a file watcher using Java File WatcherService as explained below link and reload Log4J configuration on any file Modifications.
https://dzone.com/articles/how-watch-file-system-changes
Reloading can be done using DOMConfigurator's APIs
https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/DOMConfigurator.html