package de.rwth.idsg.steve.utils; import lombok.extern.slf4j.Slf4j; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.appender.FileAppender; import org.apache.logging.log4j.core.appender.MemoryMappedFileAppender; import org.apache.logging.log4j.core.appender.RandomAccessFileAppender; import org.apache.logging.log4j.core.appender.RollingFileAppender; import org.apache.logging.log4j.core.appender.RollingRandomAccessFileAppender; import org.apache.logging.log4j.core.impl.Log4jContextFactory; import org.apache.logging.log4j.core.selector.ContextSelector; import org.apache.logging.log4j.spi.LoggerContextFactory; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Random; /** * @author Sevket Goekay <goekay@dbis.rwth-aachen.de> * @since 05.11.2015 */ @Slf4j public final class LogFileRetriever { public static final LogFileRetriever INSTANCE = new LogFileRetriever(); private List<Path> logPathList; private Random random = new Random(); private LogFileRetriever() { logPathList = getActiveLogFilePaths(); } public Optional<Path> getPath() { Path p; if (logPathList.isEmpty()) { p = null; } else if (logPathList.size() == 1) { p = logPathList.get(0); } else { p = rollTheDice(); } return Optional.ofNullable(p); } public String getLogFilePathOrErrorMessage() { Optional<Path> p = getPath(); if (p.isPresent()) { return p.get().toAbsolutePath().toString(); } else { return getErrorMessage(); } } public String getErrorMessage() { return "Not available"; } // ------------------------------------------------------------------------- // Private helpers // ------------------------------------------------------------------------- /** * If the user configured multiple file appenders, which log file should we choose? * Clearly, the only sane solution is rolling the dice. * Easter egg mode: On */ private Path rollTheDice() { log.trace("Rolling the dice..."); int index = random.nextInt(logPathList.size()); return logPathList.get(index); } /** * We cannot presume that the default file name/location setting won't be changed by the user. * Therefore, we should be able to retrieve that info from the underlying logging mechanism * by iterating over appenders. */ private List<Path> getActiveLogFilePaths() { LoggerContextFactory factory = LogManager.getFactory(); ContextSelector selector = ((Log4jContextFactory) factory).getSelector(); List<Path> fileNameList = new ArrayList<>(); for (LoggerContext ctx : selector.getLoggerContexts()) { for (Appender appender : ctx.getConfiguration().getAppenders().values()) { String fileName = extractFileName(appender); if (fileName != null) { fileNameList.add(Paths.get(fileName)); } } } return fileNameList; } /** * File appender types do not share a "write-to-file" superclass. */ private String extractFileName(Appender a) { if (a instanceof FileAppender) { return ((FileAppender) a).getFileName(); } else if (a instanceof RollingFileAppender) { return ((RollingFileAppender) a).getFileName(); } else if (a instanceof RollingRandomAccessFileAppender) { return ((RollingRandomAccessFileAppender) a).getFileName(); } else if (a instanceof RandomAccessFileAppender) { return ((RandomAccessFileAppender) a).getFileName(); } else if (a instanceof MemoryMappedFileAppender) { return ((MemoryMappedFileAppender) a).getFileName(); } else { return null; } } }