我想创建一个自定义的log4j2滚动文件追加器。我需要创建此自定义追加程序,因为我想用一些对应用程序唯一的信息来包装log4j日志事件。如userId,托管的应用程序名称。

我有一个扩展Log4jLogEvent实现LogEvent的类。此类包含需要用log事件包装的信息。
请查看此代码:

public class CustomLogEvent extends Log4jLogEvent implements LogEvent {

private String userId;
private String applicationName;

private static final long serialVersionUID = 1L;

public CustomLogEvent(String loggerName, Marker marker, String loggerFQCN, Level level, Message message, Throwable t, Map<String, String> mdc, ThreadContext.ContextStack ndc, String threadName, StackTraceElement location, long timestamp){
    super(loggerName,marker,loggerFQCN,level,message,t,mdc,ndc,threadName,location,timestamp);
}
         //Getters and setters for user Id and app name
}


在log4j2中,由于我们无法像在log4j 1.2中那样扩展滚动文件附加程序,因此我通过查看原始滚动文件附加程序的源代码创建了新的滚动文件附加程序。此类扩展了AbstractOutputStreamAppender。

这是我为滚动文件追加程序编写的代码。

@Plugin(name = "MyRollingFileAppender", category = "Core", elementType = "appender", printObject = true)
public class MyRollingFileAppender extends AbstractOutputStreamAppender<RollingFileManager> {

private static final int DEFAULT_BUFFER_SIZE = 8192;
private static final long serialVersionUID = 1L;

private final String fileName;
private final String filePattern;
private Object advertisement;
private final Advertiser advertiser;


private MyRollingFileAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
                            final RollingFileManager manager, final String fileName, final String filePattern,
                            final boolean ignoreExceptions, final boolean immediateFlush, final Advertiser advertiser) {
    super(name, layout, filter, ignoreExceptions, immediateFlush, manager);
    if (advertiser != null) {
        final Map<String, String> configuration = new HashMap<String, String>(layout.getContentFormat());
        configuration.put("contentType", layout.getContentType());
        configuration.put("name", name);
        advertisement = advertiser.advertise(configuration);
    }
    this.fileName = fileName;
    this.filePattern = filePattern;
    this.advertiser = advertiser;
}


@Override
public void append(final LogEvent logEvent) {

    int userId = //get user Id

    String appplicatinName = //get application name

    GetLoggingEvent myLogEvent = new GetLoggingEvent();

    LogEvent customLogEvent = myLogEvent.getCustomLogEvent(logEvent, userId, applicationName);
    getManager().checkRollover(customLogEvent);
    super.append(customLogEvent);
}

@PluginFactory
public static MyRollingFileAppender createAppender(
        @PluginAttribute("fileName") final String fileName,
        @PluginAttribute("filePattern") final String filePattern,
        @PluginAttribute("append") final String append,
        @PluginAttribute("name") final String name,
        @PluginAttribute("bufferedIO") final String bufferedIO,
        @PluginAttribute("bufferSize") final String bufferSizeStr,
        @PluginAttribute("immediateFlush") final String immediateFlush,
        @PluginElement("Policy") final TriggeringPolicy policy,
        @PluginElement("Strategy") RolloverStrategy strategy,
        @PluginElement("Layout") Layout<? extends Serializable> layout,
        @PluginElement("Filter") final Filter filter,
        @PluginAttribute("ignoreExceptions") final String ignore,
        @PluginAttribute("advertise") final String advertise,
        @PluginAttribute("advertiseURI") final String advertiseURI,
        @PluginConfiguration final Configuration config) {

    final boolean isAppend = Booleans.parseBoolean(append, true);
    final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
    final boolean isBuffered = Booleans.parseBoolean(bufferedIO, true);
    final boolean isFlush = Booleans.parseBoolean(immediateFlush, true);
    final boolean isAdvertise = Boolean.parseBoolean(advertise);
    final int bufferSize = Integers.parseInt(bufferSizeStr, DEFAULT_BUFFER_SIZE);
    if (!isBuffered && bufferSize > 0) {
        LOGGER.warn("The bufferSize is set to {} but bufferedIO is not true: {}", bufferSize, bufferedIO);
    }
    if (name == null) {
        LOGGER.error("No name provided for FileAppender");
        return null;
    }

    if (fileName == null) {
        LOGGER.error("No filename was provided for FileAppender with name "  + name);
        return null;
    }

    if (filePattern == null) {
        LOGGER.error("No filename pattern provided for FileAppender with name "  + name);
        return null;
    }

    if (policy == null) {
        LOGGER.error("A TriggeringPolicy must be provided");
        return null;
    }

    if (strategy == null) {
        strategy = DefaultRolloverStrategy.createStrategy(null, null, null,
                String.valueOf(Deflater.DEFAULT_COMPRESSION), config);
    }

    if (layout == null) {
        layout = PatternLayout.createDefaultLayout();
    }

    final RollingFileManager manager = RollingFileManager.getFileManager(fileName, filePattern, isAppend,
            isBuffered, policy, strategy, advertiseURI, layout, bufferSize);
    if (manager == null) {
        return null;
    }

    return new MyRollingFileAppender(name, layout, filter, manager, fileName, filePattern,
            ignoreExceptions, isFlush, isAdvertise ? config.getAdvertiser() : null);
}
}


我从log4j 1.2升级到log4j2的应用程序使用Apache Commons API,因此无法使用线程上下文映射添加信息。

这个追加器目前可以正常使用。但是我对程序有些困惑。

我要确保执行此操作的方式(即用自定义信息包装日志事件并为自定义信息创建滚动文件追加程序)是正确的,并且由于我们无法扩展现有的滚动文件追加程序,因此我需要重新编写所有我的自定义类中滚动文件追加程序的代码,仅向日志事件中添加了两个字段?
有没有简单的方法可以做到这一点?

谢谢!

最佳答案

您要升级的应用程序使用Apache Commons Logging,并且Commons Logging API中没有ThreadContext映射。

但是,您只需在应用程序中使用log4j2 ThreadContext映射即可完成您的对象。这样,您不需要任何自定义日志事件或追加器子类。

您的应用程序中应该只有几个地方设置或修改了userID。在那些地方,添加以下代码行:

int userId = //get user Id
String appplicationName = //get application name
ThreadContext.put("userID", String.valueOf(userId));
ThreadContext.put("appplicationName", appplicationName);
// ... your business logic


如果将Commons Logging委托给log4j2,则可以配置模式布局,例如:"%-5p [%t] %c: %X{userID}/%{appplicationName} %m%n"以使您的值出现在日志中。

10-08 18:56