I want to use Rewrite appender in my log4j2.xml file so that before logging I can modify logs. I have not get much helps from google. As per log4j2 documents Rewrite is an interface has rewrite method and MapRewritePolicy is implementation class, when I run this I am able to see my web3.log file generating but not seeing any modification in log content. I seen MapRewritePolicy source code and created local implementation class as MapRewritePolicyImpl.java in my project and put some System.out to see code flow is coming into this class from log4j2.xml file. I have modified my log4j2.xml to use MapRewritePolicyImpl.java but code flow is not going into my MapRewritePolicyImpl.java class.
<Rewrite name="rewrite" >
<Appender-Ref ref="web3" />
<MapRewritePolicyImpl">
<KeyValuePair key="creditCard" value="new12345"/>
</MapRewritePolicyImpl>
</Rewrite>
<Configuration monitorInterval="5" status="debug" strict="true">
<Appenders>
<RollingFile name="web3" fileName="../logs/web3.log"
filePattern="${sys:catalina.home}/logs/$${date:yyyy-MM-dd}/web3-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout
pattern="%d{dd/MM/yyyy HH:mm:ss,SSS} [%X{cartID}] [%X{sessionId}] [%p] [%t] [%c] (%F:%L) - %m%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1"
modulate="true" />
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
</RollingFile>
<Rewrite name="rewrite" >
<Appender-Ref ref="web3" />
<MapRewritePolicy">
<KeyValuePair key="creditCard" value="new12345"/>
</MapRewritePolicy>
</Rewrite>
</Appenders>
<Loggers>
<Logger name="com.virginamerica" level="info" additivity="false">
<!-- <Appender-Ref ref="web3" /> -->
<Appender-Ref ref="rewrite"/>
</Logger>
</Loggers>
</Configuration>
MapRewritePolicy will evaluate LogEvents that contain a MapMessage and will add or update elements of the Map. This only works if your application calls logger.info(new MapMessage(keyValueMap))
. I suspect that this is not what your application is currently doing.
Usually, your messages will be either a SimpleMessage (if you called logger.info("Just a string without parameters")
) or a ParameterizedMessage (if you called logger.info("Hi {}!", name)
). RewriteAppender will not be able to do anything with either SimpleMessage or ParameterizedMessages, so nothing is replaced...
You may want to take a look at the documentation for PatternLayout: this has the capability to replace regular expressions in string messages with some replacement value, if you use the replace{pattern}{regex}{substitution}
pattern.
Solution 1: Using RewriteAppender
Your example seems almost correct. As mentioned by @Remko Popma the Rewrite implementation you selected is not fit for your usecase.
If you use your own Rewriter implementation you can get it to work.
I found a very good example here: RewriteAppender Example
The example contains the log4j2.xml and the implementation of the RewriteAppender.
In the RewriteAppender (here called MarkerInjectorRewritePolicy
) you can modify the event to fit your needs.
In the example the author injects some marker-data, you would not need that part.
.
Solution 2: Using LogEventFactory
Solution 1 has some drawbacks. Imagine you have a 100 loggers and 50 Appenders.
Trying to implement solution 1 now you end up with another 50 RewriteAppender declarations plus adjustments to all loggers to use the redirect logger instead of the actual target Appender. This can be very tedious and error-prone.
A much easier solution is this:
// Do this somewhere before using the logger
System.setProperty(Constants.LOG4J_LOG_EVENT_FACTORY, MyLogEventFactory.class.getName());
Or set it by using the start parameter:
-DLog4jLogEventFactory=my.package.MyLogEventFactory
EDIT: BETTER: A more robust way to set this is by using a log4j2.component.properties
:
- Create a text file called
log4j2.component.properties
and put it in your classpath.
- Put this code in it:
Log4jLogEventFactory = my.package.MyLogEventFactory
This file is loaded automatically by log4j and used to configure multiple core settings. Using this file you do not have to pass start-parameters every time plus you don't run into the potential problem of class initialization ordering problems (like when using System.setProperty()
).
The LogEventFactory implementation is pretty simple:
public class MyLogEventFactory implements LogEventFactory {
@Override
public LogEvent createEvent(String loggerName, Marker marker, String fqcn, Level level, Message message,
List<Property> properties, Throwable t) {
{ // In my case i wanted "log.error(new Exception(msg))"
// to pass the exception properly to the event,
// as if "log.error(msg, new Exception(msg))" was called.
if (t == null && message instanceof ObjectMessage) {
ObjectMessage msg = (ObjectMessage) message;
t = msg.getThrowable();
}
}
// XXX do your adjustments here
return new Log4jLogEvent(loggerName, marker, fqcn, level, message, properties, t);
}
}
The LogEventFactory
is used to create the LogEvents
. So instead of intercepting and manipulating them later, we simply go to the source and do our work here.
With this we get ALL events!
Good luck! Have fun!