Skip to content

Instantly share code, notes, and snippets.

@basinilya
Last active September 14, 2017 07:32
Show Gist options
  • Save basinilya/1f200f1394ddfac645be677461d7a316 to your computer and use it in GitHub Desktop.
Save basinilya/1f200f1394ddfac645be677461d7a316 to your computer and use it in GitHub Desktop.
Delay writing to log file in log4j 1x
/*
* How to delay log4j messages targeted for a FileAppender until certain condition is met?
*
* My java program is a command line tool that is supposed to run single-instanced. For that a
* pidfile support was added to it. However, there's some 3rd-party initialization code that runs
* before the pidfile check and writes to log4j.
*
* I'm not happy that my logfile contains messages from a process that exited due to the locked
* pidfile, however, I want the ConsoleAppender to keep working as today.
*
* I want to add some code at the beginning of my `main()` method which would temporarily replace
* all FileAppender descendants with MemoryAppender. Then, after the pidfile is successfully locked,
* restore the original FileAppenders from the log4j configuration file and flush the messages to
* them.
* */
package org.foo.delayingfilter;
import java.io.FileOutputStream;
import java.nio.channels.FileLock;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.LinkedList;
import org.apache.log4j.Appender;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.Filter;
import org.apache.log4j.spi.LoggingEvent;
/**
* Delays writing to log4j FileAppender appenders until we decide to flush them. ConsoleAppender
* keeps working normally.
*/
public class DelayingFilter extends Filter {
public static void main(final String[] args) throws Exception {
final Logger logger = Logger.getLogger("org.foo");
DelayingFilter.delayFileAppenders(true);
logger.info("app starting...");
final FileLock lck = new FileOutputStream("./test.lck").getChannel().tryLock();
if (lck == null) {
logger.error("Another instance is running; exiting");
System.exit(1);
}
DelayingFilter.delayFileAppenders(false);
logger.info("app started");
Thread.sleep(10000);
}
@SuppressWarnings({ "rawtypes" })
public static void delayFileAppenders(final boolean delaying) {
final HashSet<Appender> appenderSet = new HashSet<Appender>();
setDelaying(delaying, LogManager.getRootLogger(), appenderSet);
for (final Enumeration loggers = LogManager.getCurrentLoggers(); loggers.hasMoreElements();) {
final Logger logger = (Logger) loggers.nextElement();
setDelaying(delaying, logger, appenderSet);
}
}
private final Appender saveAppender;
private final LinkedList<LoggingEvent> bufferedEvents = new LinkedList<LoggingEvent>();
private boolean delaying;
public DelayingFilter(final Appender saveAppender, final boolean buffering) {
this.saveAppender = saveAppender;
this.delaying = buffering;
}
@Override
public synchronized int decide(final LoggingEvent event) {
if ("".length() == 1) {
if (event.getLevel() != null && event.getLevel().isGreaterOrEqual(Level.FATAL)) {
return NEUTRAL;
}
}
if (delaying) {
bufferedEvents.add(event);
return DENY;
}
return NEUTRAL;
}
public synchronized void setDelaying(final boolean delaying) {
this.delaying = delaying;
if (!delaying) {
LoggingEvent ev = null;
while ((ev = bufferedEvents.poll()) != null) {
saveAppender.doAppend(ev);
}
}
}
@SuppressWarnings({ "rawtypes" })
private static void setDelaying(
final boolean delaying,
final Logger logger,
final HashSet<Appender> appenderSet) {
for (final Enumeration appenders = logger.getAllAppenders(); appenders.hasMoreElements();) {
final Appender appender = (Appender) appenders.nextElement();
if ((appender instanceof ConsoleAppender) || !appenderSet.add(appender)) {
continue;
}
if (delaying) {
appender.addFilter(new DelayingFilter(appender, true));
} else {
Filter headFilterToRemove = null;
for (Filter f = appender.getFilter(), prevf = null; f != null; prevf = f, f = f
.getNext()) {
if (f instanceof DelayingFilter) {
((DelayingFilter) f).setDelaying(false);
if (prevf == null) {
headFilterToRemove = f;
} else {
prevf.setNext(f.getNext());
}
}
}
if (headFilterToRemove != null) {
appender.clearFilters();
appender.addFilter(headFilterToRemove.getNext());
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment