2011-10-13 46 views
13

出于安全原因,我需要查看我的应用程序中每个记录的消息,并在进入日志文件之前对其进行修改。我想我可以写一个自定义的appender(扩展DailyRollingFileAppender)并覆盖subAppend(LoggingEvent事件)。问题是,LoggingEvent中的消息文本没有setter,并且消息是私有属性。我可以用修改后的消息创建一个新的LoggingEvent,但是API不会轻易复制原始LoggingEvent的其余部分。这一切似乎都旨在阻止在自定义appender中插入消息。LOG4J:使用自定义appender修改记录的消息

我能看到的唯一的其他选项是修改数百条日志语句来调用一个新的全局方法,该方法可以先修改文本然后再进行Log4J调用。我宁愿不!

是否有其他人有需要修改自定义appender中记录的消息?

回答

0

我会去为Logger创建代理类,并将所有导入从org.apache.log4j.Logger更改为your.own.Logger。这是一个简单且自动的过程,因为您考虑更改代码中的记录器调用,我想你可以完全访问源代码。在委托中,您可以完全调用log4j Logger的方法,但首先会根据您的内容添加字符串。

我浏览过此代码,并在版本1.2.15之前的版本创建新的LoggingEvent是不可能的,且无需挖掘其中的一半。从1.2.15开始就没有问题了。

11

我不完全确定为什么创建一个新的LoggingEvent是如此繁重。这似乎为我工作:

package test.logging; 

import org.apache.log4j.DailyRollingFileAppender; 
import org.apache.log4j.spi.LoggingEvent; 

public class MyDailyRollingFileAppender extends DailyRollingFileAppender { 

    @Override 
    protected void subAppend(LoggingEvent event) { 
     String modifiedMessage = String.format("**** Message modified by MyDailyRollingFileAppender ****\n\n%s\n\n**** Finished modified message ****", event.getMessage()); 
     LoggingEvent modifiedEvent = new LoggingEvent(event.getFQNOfLoggerClass(), event.getLogger(), event.getTimeStamp(), event.getLevel(), modifiedMessage, 
                 event.getThreadName(), event.getThrowableInformation(), event.getNDC(), event.getLocationInformation(), 
                 event.getProperties()); 

     super.subAppend(modifiedEvent); 
    } 

} 

有了这个测试:

package test; 

import org.apache.log4j.Logger; 

public class TestLogging { 

    public static void main(String[] args) { 
     Logger log = Logger.getLogger(TestLogging.class); 
     log.info("I am testing my logging"); 
     log.info("Here is an exception", new Exception()); 
    } 

} 

与此配置:

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> 

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> 

    <appender name="MyDailyRollingFileAppender" class="test.logging.MyDailyRollingFileAppender"> 
     <param name="Append" value="true"/> 
     <param name="datePattern" value="'.'yyyy-MM-dd"/> 
     <param name="File" value="mine.log"/> 
     <layout class="org.apache.log4j.PatternLayout"> 
      <param name="ConversionPattern" value="%d %-5p (%x) [%t] %c{1} - %m%n" /> 
     </layout> 
    </appender> 

    <root> 
     <priority value="debug"/> 
     <appender-ref ref="MyDailyRollingFileAppender"/> 
    </root> 

</log4j:configuration> 

我得到以下输出:

2011-10-14 10:09:09,322 INFO () [main] TestLogging - **** Message modified by MyDailyRollingFileAppender **** 

I am testing my logging 

**** Finished modified message **** 
2011-10-14 10:09:09,333 INFO () [main] TestLogging - **** Message modified by MyDailyRollingFileAppender **** 

Here is an exception 

**** Finished modified message **** 
java.lang.Exception 
    at test.TestLogging.main(TestLogging.java:10) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120) 

虽然我做了一些simila对此,我使用了一种稍微不同的方法。我不想编写每种类型的Appender的子类,我创建了一个Appender,它包装其他Appender对象,并在发送到包装的Appender之前修改该消息。事情是这样的:

package test.logging; 

import org.apache.log4j.Appender; 
import org.apache.log4j.AppenderSkeleton; 
import org.apache.log4j.spi.AppenderAttachable; 
import org.apache.log4j.spi.LoggingEvent; 

import java.util.ArrayList; 
import java.util.Collections; 
import java.util.Enumeration; 
import java.util.Iterator; 
import java.util.List; 

public class MyAppenderWrapper extends AppenderSkeleton implements AppenderAttachable { 

    private final List<Appender> appenders = new ArrayList<Appender>(); 

    public void close() { 
     synchronized (appenders) { 
      for (Appender appender : appenders) { 
       appender.close(); 
      } 
     } 
    } 

    public boolean requiresLayout() { 
     return false; 
    } 

    public void addAppender(Appender appender) { 
     synchronized (appenders) { 
      appenders.add(appender); 
     } 
    } 

    public Enumeration getAllAppenders() { 
     return Collections.enumeration(appenders); 
    } 

    public Appender getAppender(String name) { 
     synchronized (appenders) { 
      for (Appender appender : appenders) { 
       if (appender.getName().equals(name)) { 
        return appender; 
       } 
      } 
     } 
     return null; 
    } 

    public boolean isAttached(Appender appender) { 
     synchronized (appenders) { 
      for (Appender wrapped : appenders) { 
       if (wrapped.equals(appender)) { 
        return true; 
       } 
      } 
      return false; 
     } 
    } 

    public void removeAllAppenders() { 
     synchronized (appenders) { 
      appenders.clear(); 
     } 
    } 

    public void removeAppender(Appender appender) { 
     synchronized (appenders) { 
      for (Iterator<Appender> i = appenders.iterator(); i.hasNext();) { 
       if (i.next().equals(appender)) { 
        i.remove(); 
       } 
      } 
     } 
    } 

    public void removeAppender(String name) { 
     synchronized (appenders) { 
      for (Iterator<Appender> i = appenders.iterator(); i.hasNext();) { 
       if (i.next().getName().equals(name)) { 
        i.remove(); 
       } 
      } 
     } 
    } 

    @Override 
    protected void append(LoggingEvent event) { 
     String modifiedMessage = String.format("**** Message modified by MyAppenderWrapper ****\n\n%s\n\n**** Finished modified message ****", event.getMessage()); 
     LoggingEvent modifiedEvent = new LoggingEvent(event.getFQNOfLoggerClass(), event.getLogger(), event.getTimeStamp(), event.getLevel(), modifiedMessage, 
                 event.getThreadName(), event.getThrowableInformation(), event.getNDC(), event.getLocationInformation(), 
                 event.getProperties()); 

     synchronized (appenders) { 
      for (Appender appender : appenders) { 
       appender.doAppend(modifiedEvent); 
      } 
     } 
    } 

} 

,你可以配置,如:

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> 

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> 

    <appender name="StdOut" class="org.apache.log4j.ConsoleAppender"> 
     <layout class="org.apache.log4j.PatternLayout"> 
      <param name="ConversionPattern" value="%d %-5p (%x) [%t] %c{1} - %m%n" /> 
     </layout> 
    </appender> 

    <appender name="FileAppender" class="org.apache.log4j.DailyRollingFileAppender"> 
     <param name="Append" value="true"/> 
     <param name="datePattern" value="'.'yyyy-MM-dd"/> 
     <param name="File" value="mine.log"/> 
     <layout class="org.apache.log4j.PatternLayout"> 
      <param name="ConversionPattern" value="%d %-5p (%x) [%t] %c{1} - %m%n" /> 
     </layout> 
    </appender> 

    <appender name="AppenderWrapper" class="test.logging.MyAppenderWrapper"> 
     <appender-ref ref="StdOut"/> 
     <appender-ref ref="FileAppender"/> 
    </appender> 

    <root> 
     <priority value="debug"/> 
     <appender-ref ref="AppenderWrapper"/> 
    </root> 

</log4j:configuration> 

这样的消息仍发送到原来的附加目的地,但与修改的消息。

+2

它的工作原理是因为您使用的是1.2.15+版本。以前的log4j版本没有你的例子中使用的一些方法,因此作者的麻烦。许多项目使用1.2.14版本,因为它曾经是最近的一个很长一段时间。 – MaDa

+1

我升级到1.2.16,并能够使用您的示例创建一个新的LoggingEvent。谢谢您的帮助! – user993719

4

另一种选择是自定义您的appender使用的布局。由于布局负责将日志事件序列化为字符串,因此我会检查抽动布局是否比改变appender和事件更简单。只是一个想法...

+0

是的!这听起来像是一种利基方法,而不是修改Appenders。自定义布局也可以用来代替多个appender – hellojava

3

那我该怎么做,因为我用的是旧版本的log4j的合作定制的appender

相反,我定制的布局,并提到该布局在哪个追加程序,我需要这个功能

public class LogValidatorLayout extends PatternLayout { 

    public LogValidatorLayout() { 
     super(); 
    } 

    public LogValidatorLayout(String pattern) { 
     super(pattern); 
    } 

    @Override 
    public String format(LoggingEvent event) { 

     // only process String type messages 
     if (event.getMessage() != null && event.getMessage() instanceof String) { 

      String message = event.getMessage().toString(); 
      message = StringUtils.trim("Some custom text --->>"+message); 

      // earlier versions of log4j don't provide any way to update messages, 
      // so use reflections to do this 
      try { 
       Field field = LoggingEvent.class.getDeclaredField("message"); 
       field.setAccessible(true); 
       field.set(event, message); 
      } catch (Exception e) { 
       // Dont log it as it will lead to infinite loop. Simply print the trace 
       e.printStackTrace(); 
      } 

     } 

     return super.format(event); 
    } 

} 

而且在您的log4j.properties或xml中,注册此布局

log4j.appender.STDOUT.layout=a.b.c.package.LogValidatorLayout 
相关问题