LOG4J:使用自定义appender修改记录的消息

use*_*719 15 java logging log4j

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

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

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

小智 14

我不完全确定为什么创造一个新的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);
    }

}
Run Code Online (Sandbox Code Playgroud)

通过这个测试:

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());
    }

}
Run Code Online (Sandbox Code Playgroud)

而这个配置:

<?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>
Run Code Online (Sandbox Code Playgroud)

我得到以下输出:

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)
Run Code Online (Sandbox Code Playgroud)

虽然我做了类似的事情,但我采用了略微不同的方法.Appender我没有编写我想要使​​用的每种类型的子类,而是创建了一个Appender包装其他Appender对象的子类,并在发送到包装的Appenders 之前修改了消息.像这样的东西:

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);
            }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

您可以配置如下:

<?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>
Run Code Online (Sandbox Code Playgroud)

这样,消息仍然会被发送到原始的appender但是带有修改后的消息.

  • 它的工作原理是因为你使用的是版本1.2.15+.以前的log4j版本没有您在示例中使用的某些方法,因此作者的麻烦.许多项目使用版本1.2.14,因为它曾经是最近的最新版本. (3认同)

hel*_*ava 5

多数民众赞成我自从使用旧版本的log4j以来的表现

我没有自定义appender,而是自定义布局,并在我需要此功能的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);
    }

}
Run Code Online (Sandbox Code Playgroud)

在log4j.properties或xml中,注册此布局

log4j.appender.STDOUT.layout=a.b.c.package.LogValidatorLayout
Run Code Online (Sandbox Code Playgroud)


MaD*_*aDa 0

我会去创建一个委托类Logger并将所有导入从 更改org.apache.log4j.Loggeryour.own.Logger。这是一个简单且自动的过程 - 因为您考虑更改代码中的记录器调用,所以我认为您可以完全访问源代码。在委托中,您将准确调用 log4j Logger 中的方法,但首先根据自己的喜好对字符串进行处理。

我浏览过代码,在 1.2.15 版本之前,LoggingEvent如果不挖掘一半的库,就不可能从现有的库中创建新的库。从1.2.15开始就没有问题了。