I am working on a web application that uses Log4j (1.2.16). To avoid having to edit a ton of files, I am trying to connect a custom implementation of LoggerFactory that protects against log falsification.
It looks like you can set the configuration setting log4j.loggerFactory(the project uses the property file method) to indicate the factory logger used. However, this does not work. Having studied the source code for Log4j, it seems that the property is never used, although it is read by the PropertyConfigurator class.
Learning more about the source of Log4j, this is the only way to achieve what I want, I need to create custom subclasses of the Log4j classes. Is this the only way?
Below is what I needed to do in my web application context listener to initialize Log4j, so my custom factory log will be used:
package com.example.myapp.log4j;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.*;
public class MyLog4jInitContextListener implements ServletContextListener
{
public void contextInitialized(
ServletContextEvent event
) {
this.context = event.getServletContext();
String file = context.getInitParameter("log4jConfiguration");
if (file != null) {
String prefix = context.getRealPath("/");
String pathname = prefix+file;
event.getServletContext().log("Initializing log4j with "+pathname);
org.apache.log4j.LogManager.setRepositorySelector(
new org.apache.log4j.spi.DefaultRepositorySelector(
new MyHierarchy(
new org.apache.log4j.spi.RootLogger(
(org.apache.log4j.Level)org.apache.log4j.Level.INFO))), this);
new MyPropertyConfigurator().doConfigure(
pathname, org.apache.log4j.LogManager.getLoggerRepository());
} else {
event.getServletContext().log(
"No log4jConfiguration parameter specified");
}
}
public void contextDestroyed(
ServletContextEvent event
) {
this.context = null;
}
private ServletContext context = null;
}
I had to create a custom Hierarchyone as it shows that it hardcodes the factory default registry. This version ensures that my factory will be used when the only getLogger () method is called (which seems to be going through Logger.getLogger () -> LogManager.getLogger () -> Hierarchy.getLogger ()):
MyHierarchy.java
package com.example.myapp.log4j;
import org.apache.log4j.Hierarchy;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggerFactory;
public class MyHierarchy extends Hierarchy
{
public MyHierarchy(Logger root) { super(root); }
@Override
public Logger getLogger(String name) {
return getLogger(name, defaultFactory);
}
private LoggerFactory defaultFactory = new MyLoggerFactory();
}
Not sure what I need to set up the PropertyConfigurator, but I did it in case there is some kind of execution path that actually uses the factory log instance that the link is stored on.
MyPropertyConfigurator.java
package com.example.myapp.log4j;
import org.apache.log4j.PropertyConfigurator;
public class MyPropertyConfigurator extends PropertyConfigurator
{
public MyPropertyConfigurator() {
loggerFactory = new MyLoggerFactory();
}
@Override
protected void configureLoggerFactory(java.util.Properties props) {
}
}
logger factory. MyEscapedLogger Log4j Logger, protectedLog(), .
MyLoggerFactory.java
package com.example.myapp.log4j;
import org.apache.log4j.spi.LoggerFactory;
public class MyLoggerFactory implements LoggerFactory
{
public Logger makeNewLoggerInstance(String name) {
return new MyEscapedLogger(name);
}
}