package org.apache.cassandra.utils;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.cliffc.high_scale_lib.NonBlockingHashMap;
import org.slf4j.Logger;
import com.google.common.annotations.VisibleForTesting;
public class NoSpamLogger
{
public enum Level
{
INFO, WARN, ERROR;
}
@VisibleForTesting
static interface Clock
{
long nanoTime();
}
@VisibleForTesting
static Clock CLOCK = new Clock()
{
public long nanoTime()
{
return System.nanoTime();
}
};
public class NoSpamLogStatement extends AtomicLong
{
private static final long serialVersionUID = 1L;
private final String statement;
private final long minIntervalNanos;
public NoSpamLogStatement(String statement, long minIntervalNanos)
{
this.statement = statement;
this.minIntervalNanos = minIntervalNanos;
}
private boolean shouldLog(long nowNanos)
{
long expected = get();
return nowNanos - expected >= minIntervalNanos && compareAndSet(expected, nowNanos);
}
public boolean log(Level l, long nowNanos, Object... objects)
{
if (!shouldLog(nowNanos)) return false;
switch (l)
{
case INFO:
wrapped.info(statement, objects);
break;
case WARN:
wrapped.warn(statement, objects);
break;
case ERROR:
wrapped.error(statement, objects);
break;
default:
throw new AssertionError();
}
return true;
}
public boolean info(long nowNanos, Object... objects)
{
return NoSpamLogStatement.this.log(Level.INFO, nowNanos, objects);
}
public boolean info(Object... objects)
{
return NoSpamLogStatement.this.info(CLOCK.nanoTime(), objects);
}
public boolean warn(long nowNanos, Object... objects)
{
return NoSpamLogStatement.this.log(Level.WARN, nowNanos, objects);
}
public boolean warn(Object... objects)
{
return NoSpamLogStatement.this.warn(CLOCK.nanoTime(), objects);
}
public boolean error(long nowNanos, Object... objects)
{
return NoSpamLogStatement.this.log(Level.ERROR, nowNanos, objects);
}
public boolean error(Object... objects)
{
return NoSpamLogStatement.this.error(CLOCK.nanoTime(), objects);
}
}
private static final NonBlockingHashMap<Logger, NoSpamLogger> wrappedLoggers = new NonBlockingHashMap<>();
@VisibleForTesting
static void clearWrappedLoggersForTest()
{
wrappedLoggers.clear();
}
public static NoSpamLogger getLogger(Logger logger, long minInterval, TimeUnit unit)
{
NoSpamLogger wrapped = wrappedLoggers.get(logger);
if (wrapped == null)
{
wrapped = new NoSpamLogger(logger, minInterval, unit);
NoSpamLogger temp = wrappedLoggers.putIfAbsent(logger, wrapped);
if (temp != null)
wrapped = temp;
}
return wrapped;
}
public static boolean log(Logger logger, Level level, long minInterval, TimeUnit unit, String message, Object... objects)
{
return log(logger, level, message, minInterval, unit, CLOCK.nanoTime(), message, objects);
}
public static boolean log(Logger logger, Level level, String key, long minInterval, TimeUnit unit, String message, Object... objects)
{
return log(logger, level, key, minInterval, unit, CLOCK.nanoTime(), message, objects);
}
public static boolean log(Logger logger, Level level, String key, long minInterval, TimeUnit unit, long nowNanos, String message, Object... objects)
{
NoSpamLogger wrapped = getLogger(logger, minInterval, unit);
NoSpamLogStatement statement = wrapped.getStatement(key, message);
return statement.log(level, nowNanos, objects);
}
public static NoSpamLogStatement getStatement(Logger logger, String message, long minInterval, TimeUnit unit)
{
NoSpamLogger wrapped = getLogger(logger, minInterval, unit);
return wrapped.getStatement(message);
}
private final Logger wrapped;
private final long minIntervalNanos;
private final NonBlockingHashMap<String, NoSpamLogStatement> lastMessage = new NonBlockingHashMap<>();
private NoSpamLogger(Logger wrapped, long minInterval, TimeUnit timeUnit)
{
this.wrapped = wrapped;
minIntervalNanos = timeUnit.toNanos(minInterval);
}
public boolean info(long nowNanos, String s, Object... objects)
{
return NoSpamLogger.this.log( Level.INFO, s, nowNanos, objects);
}
public boolean info(String s, Object... objects)
{
return NoSpamLogger.this.info(CLOCK.nanoTime(), s, objects);
}
public boolean warn(long nowNanos, String s, Object... objects)
{
return NoSpamLogger.this.log( Level.WARN, s, nowNanos, objects);
}
public boolean warn(String s, Object... objects)
{
return NoSpamLogger.this.warn(CLOCK.nanoTime(), s, objects);
}
public boolean error(long nowNanos, String s, Object... objects)
{
return NoSpamLogger.this.log( Level.ERROR, s, nowNanos, objects);
}
public boolean error(String s, Object... objects)
{
return NoSpamLogger.this.error(CLOCK.nanoTime(), s, objects);
}
public boolean log(Level l, String s, long nowNanos, Object... objects)
{
return NoSpamLogger.this.getStatement(s, minIntervalNanos).log(l, nowNanos, objects);
}
public NoSpamLogStatement getStatement(String s)
{
return NoSpamLogger.this.getStatement(s, minIntervalNanos);
}
public NoSpamLogStatement getStatement(String key, String s)
{
return NoSpamLogger.this.getStatement(key, s, minIntervalNanos);
}
public NoSpamLogStatement getStatement(String s, long minInterval, TimeUnit unit)
{
return NoSpamLogger.this.getStatement(s, unit.toNanos(minInterval));
}
public NoSpamLogStatement getStatement(String s, long minIntervalNanos)
{
return getStatement(s, s, minIntervalNanos);
}
public NoSpamLogStatement getStatement(String key, String s, long minIntervalNanos)
{
NoSpamLogStatement statement = lastMessage.get(key);
if (statement == null)
{
statement = new NoSpamLogStatement(s, minIntervalNanos);
NoSpamLogStatement temp = lastMessage.putIfAbsent(key, statement);
if (temp != null)
statement = temp;
}
return statement;
}
}