I want to create a custom log4j2 rolling file appender. I need to create this custom appender because I want to wrap the log4j log event with some information unique to my application. Such as userId, hosted application name.
I have a class which extends Log4jLogEvent implements LogEvent. This class has information that I need to wrap with the log event. Please see this code :
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
}
In log4j2 since we cannot extend rolling file appender like we did with log4j 1.2, I created new rolling file appender by seeing the source code of original rolling file appender. This class extends AbstractOutputStreamAppender.
This is the code I have written for rolling file appender.
@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);
}
}
The application I'm upgrading from log4j 1.2 to log4j2 uses Apache commons API therefore I cannot use thread context maps to add information.
This appender works fine for now.However I have some confusions of my procedure.
I want to make sure that the way I'm doing this (i.e wrapping log event with custom information and creating the rolling file appender for custom information) is correct and since we cannot extend existing rolling file appender do I need to re write all the code of rolling file appender in my custom class to just add two more fields in to the log event? Is there any easy way to do the same ?
Thanks!