问题描述
出于安全考虑,我需要查看应用中的每条已记录消息,并可能在进入日志文件之前对其进行修改。我想我可以写一个自定义appender(扩展DailyRollingFileAppender)并覆盖subAppend(LoggingEvent事件)。问题是,LoggingEvent中没有消息文本的setter,消息是私有属性。我可以使用修改后的消息创建一个新的LoggingEvent,但API不能轻松复制原始LoggingEvent的其余部分。这一切似乎都是为了阻止在自定义appender中插入消息。
For security reasons, I need to look at every logged message in my app and possibly modify it before it goes to the log file. I figured I could write a custom appender (extending DailyRollingFileAppender) and override subAppend(LoggingEvent event). Problem is, there's no setter for the message text in LoggingEvent, and the message is a private attribute. I could create a new LoggingEvent with my modified message, but the API doesn't make it easy to copy over the rest of the original LoggingEvent. It all seems designed to discourage meddling with the message in a custom appender.
我能看到的唯一其他选项是修改数百个日志语句以调用新的全局方法可以先修改文本然后进行Log4J调用。我不愿意!
The only other option I can see is to modify hundreds of logging statements to call a new global method that can modify the text first and then make the Log4J call. I'd rather not!
是否有其他人需要修改自定义appender中记录的消息?
Has anyone else had the need to modify the logged message in a custom appender?
推荐答案
我不完全确定为什么创建一个新的 LoggingEvent
是如此繁重。这似乎对我有用:
I'm not entirely sure why creating a new LoggingEvent
is so onerous. This seems to work for me:
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)
虽然我做了类似的事情,但我采用了略微不同的方法。我没有编写每种类型的 Appender
的子类,而是创建了一个 Appender
,它包装了其他 Appender
对象,并在发送到包装的 Appender
之前修改消息。这样的事情:
Although I have done something similar to this, I used a slightly different approach. Instead of writing subclasses of each type of Appender
I wanted to use, I created an Appender
which wraps other Appender
objects, and modifies the message before sending to the wrapped Appender
s. Something like this:
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>
这样邮件仍然会被发送到原始的appender但是带有修改后的消息。
That way the message is still sent to the original appenders but with the modified message.
这篇关于LOG4J:使用自定义appender修改记录的消息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!