I am trying to route specific logs to different files based on a pattern within the log using Log4j 1.2.17 on a JDK1.6 application. I found that the solution mentioned on posts here and here say the below configuration done within the log4j xml should work along with MDC context but for some reason they have no effect on the filter and the logs end up getting routed to both the console and the file.
My java code looks like below:
@RequestMapping(value = "/fetchBookById", method = RequestMethod.GET, produces="application/json")
public @ResponseBody Map<String,? extends Object> bookApi(ParamDTO params) throws InterruptedException {
log.info("SimpleBookController.bookApi: THIS IS A COMMON LOG");
Map<String,Object> rxdMap = null;
try{
//This below will put either ABCD or EFGH etc
MDC.put("COMPANYNAME", companyMap.get(params.getCompanyOwnerId()));
log.info("SimpleBookController.bookApi: THis is test with routing");
log.info("SimpleBookController.bookApi: Payload: "+params.toString());
//do something here
log.info("SimpleBookController.bookApi API completed for :"+params.getCompanyOwnerId());
} finally {
MDC.clear();
}
return rxdMap;
}
Log4j.xml:
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] COMPANYNAME:%X{COMPANYNAME} %-5p - %m%n" />
</layout>
<filter class="org.apache.log4j.varia.StringMatchFilter">
<param name="StringToMatch" value="COMPANYNAME:"/>
<param name="AcceptOnMatch" value="false"/>
</filter>
</appender>
<appender name="file" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="./logs/company.log" />
<param name="Append" value="true" />
<param name="ImmediateFlush" value="true" />
<param name="MaxFileSize" value="10MB" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] COMPANYNAME:%X{COMPANYNAME} %-5p - %m%n" />
</layout>
<filter class="org.apache.log4j.varia.StringMatchFilter">
<param name="StringToMatch" value="COMPANYNAME:ABCD"/>
<param name="AcceptOnMatch" value="true"/>
</filter>
<filter class="org.apache.log4j.varia.DenyAllFilter"/>
</appender>
<root>
<level value="info" />
<appender-ref ref="file" />
<appender-ref ref="console" />
</root>
In the above configuration I am trying to route any logs that do not have "COMPANYNAME:" string to console and the logs that have "COMPANYNAME:ABCD" should route to a specific file. But both console and the file Appender end up having all the logs irrespective of the string pattern. Much appreciated if anyone can point me in the right direction.
Sample logs:
2022-07-11 21:29:44 [[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'] COMPANYNAME: INFO - FrameworkServlet 'TestJDK6Logging': initialization completed in 423 ms
2022-07-11 21:29:56 [[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'] COMPANYNAME: INFO - SimpleBookController.bookApi: THIS IS A COMMON LOG
2022-07-11 21:29:56 [[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'] COMPANYNAME:ABCD INFO - SimpleBookController.bookApi: THis is test with routing
2022-07-11 21:29:56 [[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'] COMPANYNAME:ABCD INFO - SimpleBookController.bookApi: Payload: ParamDTO [identifier=sdfdf, bookId=1, companyOwnerId=4]
2022-07-11 21:30:01 [[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'] COMPANYNAME:ABCD INFO - SimpleBookController.bookApi API completed for :4
I ended up creating custom filter for console and logs to divert the log stream based on MDC map value, and this solved the filtering problem. This idea came from @KC Baltz from this post.
My log4j.xml looks as below:
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p - %m%n" />
</layout>
<!-- This is custom console filter, modify decide() function accordingly -->
<filter class="com.lumn.logtest.web.common.Log4JConsoleFilter">
<param name="keyToMatch" value="COMPANYNAME" />
<param name="valueToMatch" value="" />
<param name="acceptOnMatch" value="false" />
</filter>
</appender>
<appender name="LUMN" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="./logs/lumen.log" />
<param name="Append" value="true" />
<param name="ImmediateFlush" value="true" />
<param name="MaxFileSize" value="10MB" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p - %m%n" />
</layout>
<!-- This is custom company filter, modify decide() function accordingly -->
<filter class="com.lumn.logtest.web.common.Log4JCompanyFilter">
<param name="keyToMatch" value="COMPANYNAME" />
<param name="valueToMatch" value="ABCD" />
<param name="acceptOnMatch" value="true" />
</filter>
</appender>
And my custom Filters Log4JConsoleFilter and Log4JCompanyFilter are as below:
public class Log4JConsoleFilter extends Filter {
private String keyToMatch;
private String valueToMatch;
private boolean acceptOnMatch = true;
@Override
public int decide(LoggingEvent event) {
//Modify this condition based on the need, there is room for improvement to reduce number of conditions.
if (keyToMatch == null || valueToMatch == null)
return Filter.NEUTRAL;
else {
if(event.getMDC(keyToMatch) == null)
return Filter.NEUTRAL;
else{
//MDC has value
return acceptOnMatch ? Filter.ACCEPT: Filter.DENY;
}
}
}
public String getKeyToMatch() {
return keyToMatch;
}
public void setKeyToMatch(String keyToMatch) {
this.keyToMatch = keyToMatch;
}
public String getValueToMatch() {
return valueToMatch;
}
public void setValueToMatch(String valueToMatch) {
this.valueToMatch = valueToMatch;
}
public boolean isAcceptOnMatch() {
return acceptOnMatch;
}
public void setAcceptOnMatch(boolean acceptOnMatch) {
this.acceptOnMatch = acceptOnMatch;
}
}
public class Log4JCompanyFilter extends Filter {
private String keyToMatch;
private String valueToMatch;
private boolean acceptOnMatch = true;
@Override
public int decide(LoggingEvent event) {
if (keyToMatch == null || valueToMatch == null)
return Filter.DENY;
else {
if (valueToMatch.equals(event.getMDC(keyToMatch)))
return acceptOnMatch ? Filter.ACCEPT: Filter.DENY;
else
return Filter.DENY;
}
}
public String getKeyToMatch() {
return keyToMatch;
}
public void setKeyToMatch(String keyToMatch) {
this.keyToMatch = keyToMatch;
}
public String getValueToMatch() {
return valueToMatch;
}
public void setValueToMatch(String valueToMatch) {
this.valueToMatch = valueToMatch;
}
public boolean isAcceptOnMatch() {
return acceptOnMatch;
}
public void setAcceptOnMatch(boolean acceptOnMatch) {
this.acceptOnMatch = acceptOnMatch;
}
}