本文介绍了LOG4J:使用自定义 appender 修改记录的消息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

出于安全原因,我需要查看我的应用程序中记录的每条消息,并可能在它进入日志文件之前对其进行修改.我想我可以编写一个自定义 appender(扩展 DailyRollingFileAppender)并覆盖 subAppend(LoggingEvent 事件).问题是,LoggingEvent 中的消息文本没有设置器,并且消息是私有属性.我可以使用修改后的消息创建一个新的 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 ****

%s

**** 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 对象,并在之前修改消息发送到包装好的 Appenders.像这样:

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 Appenders. 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 ****

%s

**** 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>

这样,消息仍会发送到原始附加程序,但带有修改后的消息.

That way the message is still sent to the original appenders but with the modified message.

这篇关于LOG4J:使用自定义 appender 修改记录的消息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-01 00:49