Last active
September 14, 2017 07:32
-
-
Save basinilya/1f200f1394ddfac645be677461d7a316 to your computer and use it in GitHub Desktop.
Delay writing to log file in log4j 1x
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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