diff --git a/api/src/org/labkey/api/util/logging/LogHelper.java b/api/src/org/labkey/api/util/logging/LogHelper.java index ac3172bdb1d..4d6a776d4be 100644 --- a/api/src/org/labkey/api/util/logging/LogHelper.java +++ b/api/src/org/labkey/api/util/logging/LogHelper.java @@ -2,6 +2,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.spi.ExtendedLogger; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -44,4 +45,9 @@ public static String getLabKeyLogDir() { return System.getProperty(LOG_HOME_PROPERTY_NAME); } + + public static Logger getThrottlingLogger(Class c, String note) + { + return new ThrottlingLogger((ExtendedLogger)getLogger(c, note)); + } } diff --git a/api/src/org/labkey/api/util/logging/ThrottlingLogger.java b/api/src/org/labkey/api/util/logging/ThrottlingLogger.java new file mode 100644 index 00000000000..af0e8a60f87 --- /dev/null +++ b/api/src/org/labkey/api/util/logging/ThrottlingLogger.java @@ -0,0 +1,93 @@ +package org.labkey.api.util.logging; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.message.MessageFactory; +import org.apache.logging.log4j.spi.ExtendedLogger; +import org.apache.logging.log4j.spi.ExtendedLoggerWrapper; +import org.labkey.api.cache.Cache; +import org.labkey.api.cache.CacheManager; + +import java.util.concurrent.atomic.AtomicInteger; + +public class ThrottlingLogger extends ExtendedLoggerWrapper +{ + private static final Cache THROTTLING_CACHE = CacheManager.getBlockingCache(500, CacheManager.HOUR, "Throttle for Loggers", (_, _) -> new AtomicInteger(0)); + private static final Message DUMMY_MESSAGE = new Message() + { + @Override + public String getFormattedMessage() + { + return null; + } + + @Override + public Object[] getParameters() + { + return null; + } + + @Override + public Throwable getThrowable() + { + return null; + } + }; + + public ThrottlingLogger(ExtendedLogger logger) + { + super(logger, logger.getName(), new ThrottlingMessageFactory(logger.getMessageFactory(), 20)); + } + + @Override + public void logMessage(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable t) + { + if (message != DUMMY_MESSAGE) + super.logMessage(fqcn, level, marker, message, t); + } + + private static class ThrottlingMessageFactory implements MessageFactory + { + private final MessageFactory _factory; + private final int _maxBurst; + + private ThrottlingMessageFactory(MessageFactory factory, int maxBurst) + { + _factory = factory; + _maxBurst = maxBurst; + } + + @Override + public Message newMessage(Object message) + { + String key = message == null ? "null" : message.toString(); + return shouldLog(key) ? _factory.newMessage(message) : DUMMY_MESSAGE; + } + + @Override + public Message newMessage(String message) + { + return shouldLog(message) ? _factory.newMessage(message) : DUMMY_MESSAGE; + } + + @Override + public Message newMessage(String message, Object... params) + { + return shouldLog(message) ? _factory.newMessage(message, params) : DUMMY_MESSAGE; + } + + private boolean shouldLog(String message) + { + AtomicInteger count = THROTTLING_CACHE.get(message); + + if (count.intValue() < _maxBurst) + { + count.incrementAndGet(); + return true; + } + + return false; + } + } +} diff --git a/search/src/org/labkey/search/model/LuceneSearchServiceImpl.java b/search/src/org/labkey/search/model/LuceneSearchServiceImpl.java index f3cea527fdf..a4c0586bd91 100644 --- a/search/src/org/labkey/search/model/LuceneSearchServiceImpl.java +++ b/search/src/org/labkey/search/model/LuceneSearchServiceImpl.java @@ -161,7 +161,7 @@ public class LuceneSearchServiceImpl extends AbstractSearchService implements SearchMXBean { - private static final Logger _log = LogHelper.getLogger(LuceneSearchServiceImpl.class, "Full-text searching indexing operations"); + private static final Logger _log = LogHelper.getThrottlingLogger(LuceneSearchServiceImpl.class, "Full-text searching indexing operations"); // Changes to _index are rare (only when admin changes the index path), but we want any changes to be visible to // other threads immediately. Initialize to Noop class to prevent rare NPE (e.g., system maintenance runs before index @@ -1334,7 +1334,7 @@ private void logAsWarning(WebdavResource r, String message) private void logAsWarning(WebdavResource r, String message, @Nullable String rootMessage) { - _log.warn("Can't index file \"" + getNameToLog(r) + "\" due to: " + message + (null != rootMessage ? " [" + rootMessage + "]" : "")); + _log.warn("Can't index file \"{}\" due to: {}{}", getNameToLog(r), message, null != rootMessage ? " [" + rootMessage + "]" : ""); } private static class PreProcessingException extends Exception