com.almworks.jira.structure.api.util.ConsiderateLogger Maven / Gradle / Ivy
Show all versions of structure-api Show documentation
package com.almworks.jira.structure.api.util;
import org.slf4j.Logger;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* Utility class that is used to log errors that are likely to happen often. Muffles repetitive errors.
*
* First method invocation will set the skipped messages log level.
* If you use {@link #warn(String, String)} and then {@link #info(String, String)} for the same source, then the messages about skipped messages will be logged at warn level.
*
*/
public class ConsiderateLogger {
private static long DEFAULT_INTERVAL = TimeUnit.MINUTES.toNanos(5);
private final Logger myLogger;
private final ConcurrentMap myStats = new ConcurrentHashMap<>();
private volatile long myInterval = DEFAULT_INTERVAL;
public ConsiderateLogger(Logger logger) {
myLogger = logger;
}
public ConsiderateLogger(Logger logger, long interval) {
myLogger = logger;
myInterval = interval;
}
public void info(String source, String message) {
if (acceptSource(source, Level.INFO)) {
myLogger.info(source + " " + message);
}
}
public void info(String source, String message, Throwable throwable) {
if (acceptSource(source, Level.INFO)) {
myLogger.info(source + " " + message, throwable);
}
}
public void warn(String source, String message) {
if (acceptSource(source, Level.WARN)) {
myLogger.warn(source + " " + message);
}
}
public void warn(String source, String message, Throwable throwable) {
if (acceptSource(source, Level.WARN)) {
myLogger.warn(source + " " + message, throwable);
}
}
private boolean acceptSource(String source, Level level) {
SourceStats stats = myStats.get(source);
if (stats == null) {
stats = new SourceStats(source, level);
myStats.putIfAbsent(source, stats);
}
return stats.isAcceptable();
}
public void reset() {
myStats.clear();
}
public void setInterval(long interval) {
myInterval = interval;
}
private enum Level {
INFO, WARN
}
private class SourceStats {
private final String mySource;
private final AtomicInteger myCount = new AtomicInteger();
private final Level myLevel;
private volatile long myLastLogTime;
public SourceStats(String source, Level level) {
mySource = source;
myLevel = level;
}
public boolean isAcceptable() {
long now = System.nanoTime();
if (myLastLogTime != 0 && now - myLastLogTime <= myInterval) {
myCount.incrementAndGet();
return false;
}
myLastLogTime = now;
int count = myCount.getAndSet(0);
if (count > 0) {
switch (myLevel) {
case INFO:
myLogger.info(count + " info log entries from " + mySource + " were not shown");
break;
case WARN:
default:
myLogger.warn(count + " warnings from " + mySource + " were not shown");
}
}
return true;
}
}
}