Skip to content

Commit

Permalink
Incremental progress to reimplement DeferredLogger as Logback Appende…
Browse files Browse the repository at this point in the history
…r, re #7
  • Loading branch information
safris committed Jan 3, 2023
1 parent f709ce1 commit 83a35ac
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 222 deletions.
181 changes: 87 additions & 94 deletions src/main/java/org/libj/logging/DeferredLogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;

import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -70,6 +70,8 @@
* {@link org.slf4j.Logger} instances.</b>
*/
public final class DeferredLogger {
private static final ReentrantLock classLock = new ReentrantLock();

private static class FlushFilter extends Filter<ILoggingEvent> {
private Level level;

Expand All @@ -88,32 +90,39 @@ public FilterReply decide(final ILoggingEvent event) {
}
}

private static final class AppenderBuffer {
private final Deque<ILoggingEvent> events;
private final class AppenderBuffer {
private final FlushFilter flushFilter = new FlushFilter();
private final Deque<ILoggingEvent> events;
private final Appender<ILoggingEvent> appender;
private final int maxEvents;

/**
* Create a new {@link AppenderBuffer} with the specified {@link Appender} to which deferred events will be flushed.
*
* @param appender The {@link Appender} to which deferred events will be flushed.
* @throws IllegalArgumentException If {@code appender} is null.
*/
private AppenderBuffer(final Appender<ILoggingEvent> appender, final int maxEvents, final Supplier<Deque> listSupplier) {
this.appender = appender;
if (appender == null)
throw new IllegalArgumentException("appender is null");
private AppenderBuffer() {
this.appender = getAppender(logger);
this.appender.addFilter(flushFilter);

this.events = listSupplier.get();

if (listSupplier == null)
throw new IllegalArgumentException("listSupplier is null");
appender.addFilter(new Filter<ILoggingEvent>() {
@Override
public FilterReply decide(final ILoggingEvent event) {
if (!matchesLogger(event, logger, appender))
return FilterReply.NEUTRAL;

if (maxEvents <= 0)
throw new IllegalArgumentException("maxEvents (" + maxEvents + ") must be positive");
final Level level = event.getLevel();
final boolean loggable = level.levelInt >= logger.getEffectiveLevel().levelInt;
if (level.levelInt < deferredLevel.levelInt)
return loggable ? FilterReply.ACCEPT : FilterReply.DENY;

this.events = listSupplier.get();
this.appender.addFilter(flushFilter);
this.maxEvents = maxEvents;
if (loggable)
addEvent(event);

return FilterReply.DENY;
}
});
}

/**
Expand All @@ -131,7 +140,9 @@ private void addEvent(final ILoggingEvent event) {
* Clears the buffer of deferred events.
*/
private void clear() {
lock.lock();
events.clear();
lock.unlock();
}

/**
Expand All @@ -142,16 +153,16 @@ private void clear() {
* it will not be flushed.
*/
private void flush(final Level level) {
synchronized (flushFilter) {
flushFilter.setLevel(level);
for (int i = 0, i$ = events.size(); i < i$; ++i) { // [RA]
final ILoggingEvent event = events.removeFirst();
if (event != null && event.getLevel().isGreaterOrEqual(level))
appender.doAppend(event);
}

flushFilter.setLevel(null);
lock.lock();
flushFilter.setLevel(level);
for (int i = 0, i$ = events.size(); i < i$; ++i) { // [RA]
final ILoggingEvent event = events.removeFirst();
if (event != null && event.getLevel().isGreaterOrEqual(level))
appender.doAppend(event);
}

flushFilter.setLevel(null);
lock.unlock();
}
}

Expand Down Expand Up @@ -196,7 +207,7 @@ private static Appender<ILoggingEvent> getAppender(final Logger logger) {
* @throws IllegalArgumentException If the specified {@link org.slf4j.event.Level} is null.
* @throws IllegalArgumentException If {@code logger} is null.
*/
public static synchronized org.slf4j.Logger defer(final org.slf4j.Logger logger, final org.slf4j.event.Level deferredLevel, final int maxEvents, final Supplier<Deque> listSupplier) {
public static org.slf4j.Logger defer(final org.slf4j.Logger logger, final org.slf4j.event.Level deferredLevel, final int maxEvents, final Supplier<Deque> listSupplier) {
return defer((Logger)logger, LoggerUtil.logbackLevel[deferredLevel.ordinal()], maxEvents, listSupplier);
}

Expand All @@ -215,7 +226,7 @@ public static synchronized org.slf4j.Logger defer(final org.slf4j.Logger logger,
* @throws IllegalArgumentException If the specified {@link org.slf4j.event.Level} is null.
* @throws IllegalArgumentException If {@code logger} is null.
*/
public static synchronized org.slf4j.Logger defer(final org.slf4j.Logger logger, final org.slf4j.event.Level deferredLevel, final Supplier<Deque> listSupplier) {
public static org.slf4j.Logger defer(final org.slf4j.Logger logger, final org.slf4j.event.Level deferredLevel, final Supplier<Deque> listSupplier) {
return defer((Logger)logger, LoggerUtil.logbackLevel[deferredLevel.ordinal()], Integer.MAX_VALUE, listSupplier);
}

Expand All @@ -234,7 +245,7 @@ public static synchronized org.slf4j.Logger defer(final org.slf4j.Logger logger,
* @throws IllegalArgumentException If the specified {@link org.slf4j.event.Level} is null.
* @throws IllegalArgumentException If {@code logger} is null.
*/
public static synchronized org.slf4j.Logger defer(final org.slf4j.Logger logger, final org.slf4j.event.Level deferredLevel) {
public static org.slf4j.Logger defer(final org.slf4j.Logger logger, final org.slf4j.event.Level deferredLevel) {
return defer((Logger)logger, LoggerUtil.logbackLevel[deferredLevel.ordinal()], Integer.MAX_VALUE, LinkedList::new);
}

Expand All @@ -251,40 +262,14 @@ public static synchronized org.slf4j.Logger defer(final org.slf4j.Logger logger,
* @throws IllegalStateException If the specified {@link Logger} and the root logger do not have an appender.
* @throws IllegalArgumentException If {@code logger} or {@code deferredLevel} is null.
*/
private static synchronized org.slf4j.Logger defer(final Logger logger, final Level deferredLevel, final int maxEvents, final Supplier<Deque> listSupplier) {
if (logger == null)
throw new IllegalArgumentException("logger is null");

if (deferredLevel == null)
throw new IllegalArgumentException("deferredLevel is null");

final Appender<ILoggingEvent> appender = getAppender(logger);
final Level defaultLevel = logger.getEffectiveLevel();
logger.setLevel(Objects.requireNonNull(deferredLevel));
if (deferrers.containsKey(logger))
return logger;

final AppenderBuffer buffer = new AppenderBuffer(appender, maxEvents, listSupplier);
final DeferredLogger deferredLogger = new DeferredLogger(logger, defaultLevel, buffer);
deferrers.put(logger, deferredLogger);
appender.addFilter(new Filter<ILoggingEvent>() {
@Override
public FilterReply decide(final ILoggingEvent event) {
if (!matchesLogger(event, logger, appender))
return FilterReply.NEUTRAL;

if (event.getLevel().levelInt < deferredLevel.levelInt)
return FilterReply.DENY;

if (event.getLevel().levelInt < deferredLogger.level.levelInt) {
buffer.addEvent(event);
return FilterReply.DENY;
}

return FilterReply.ACCEPT;
}
});
private static org.slf4j.Logger defer(final Logger logger, final Level deferredLevel, final int maxEvents, final Supplier<Deque> listSupplier) {
classLock.lock();
DeferredLogger deferredLogger = deferrers.get(logger);
if (deferredLogger == null)
deferrers.put(logger, deferredLogger = new DeferredLogger(logger, maxEvents, listSupplier));

deferredLogger.setDeferredLevel(deferredLevel);
classLock.unlock();
return logger;
}

Expand Down Expand Up @@ -320,30 +305,20 @@ public FilterReply decide(final ILoggingEvent event) {
* @param appender The {@link Appender}.
* @return {@code true} if the specified {@link ILoggingEvent} matches the specified {@link Logger} and {@link Appender};
* otherwise {@code false}.
* @throws IllegalArgumentException If {@code event} or {@code logger} is null.
*/
private static boolean matchesLogger(final ILoggingEvent event, final Logger logger, final Appender<ILoggingEvent> appender) {
if (event == null)
throw new IllegalArgumentException("event == null");

if (logger == null)
throw new IllegalArgumentException("logger == null");

final String loggerName = logger.getName();
private boolean matchesLogger(final ILoggingEvent event, final Logger logger, final Appender<ILoggingEvent> appender) {
final String eventLoggerName = event.getLoggerName();
if (org.slf4j.Logger.ROOT_LOGGER_NAME.equals(loggerName))
if (isRootLogger)
return org.slf4j.Logger.ROOT_LOGGER_NAME.equals(eventLoggerName) || !((Logger)LoggerFactory.getLogger(event.getLoggerName())).isAttached(appender);

if (loggerName.length() == eventLoggerName.length())
return loggerName.equals(eventLoggerName);

return loggerName.length() < eventLoggerName.length() && eventLoggerName.startsWith(loggerName + ".");
final char ch;
return eventLoggerName.startsWith(loggerName) && (eventLoggerName.length() == loggerNameLength || (ch = eventLoggerName.charAt(loggerNameLength)) == '.' || ch == '$');
}

/**
* Clears the buffers of deferred events for all deferred loggers.
*/
public static synchronized void clear() {
public static void clear() {
if (deferrers.size() > 0)
for (final DeferredLogger dererrer : deferrers.values()) // [C]
dererrer.buffer.clear();
Expand All @@ -355,7 +330,7 @@ public static synchronized void clear() {
* @param logger The deferred {@link org.slf4j.Logger}.
* @throws IllegalArgumentException If the specified {@link org.slf4j.Logger} is not a {@link DeferredLogger}.
*/
public static synchronized void clear(final org.slf4j.Logger logger) {
public static void clear(final org.slf4j.Logger logger) {
final DeferredLogger deferredLogger = deferrers.get(logger);
if (deferredLogger == null)
throw new IllegalArgumentException("The specified logger is not a " + DeferredLogger.class.getSimpleName());
Expand All @@ -370,7 +345,7 @@ public static synchronized void clear(final org.slf4j.Logger logger) {
* @param level The lowest {@link Level} condition for events to be flushed. If an event has a level lower than {@code level}, it
* will not be flushed.
*/
public static synchronized void flush(final org.slf4j.event.Level level) {
public static void flush(final org.slf4j.event.Level level) {
if (deferrers.size() > 0)
for (final DeferredLogger dererrer : deferrers.values()) // [C]
dererrer.buffer.flush(LoggerUtil.logbackLevel[level.ordinal()]);
Expand All @@ -381,7 +356,7 @@ public static synchronized void flush(final org.slf4j.event.Level level) {
* {@link Appender#doAppend(Object)} method for each event with level at or above the {@code deferredLevel} (specified in
* {@link DeferredLogger#defer(org.slf4j.Logger,org.slf4j.event.Level)}), and below the default level set in {@code logback.xml}.
*/
public static synchronized void flush() {
public static void flush() {
if (deferrers.size() > 0)
for (final DeferredLogger dererrer : deferrers.values()) // [C]
dererrer.buffer.flush(dererrer.logger.getLevel());
Expand All @@ -396,7 +371,7 @@ public static synchronized void flush() {
* will not be flushed.
* @throws IllegalArgumentException If the specified {@link org.slf4j.Logger} is not a {@link DeferredLogger}.
*/
public static synchronized void flush(final org.slf4j.Logger logger, final org.slf4j.event.Level level) {
public static void flush(final org.slf4j.Logger logger, final org.slf4j.event.Level level) {
final DeferredLogger deferredLogger = deferrers.get(logger);
if (deferredLogger == null)
throw new IllegalArgumentException("The specified logger is not a " + DeferredLogger.class.getSimpleName());
Expand All @@ -412,37 +387,55 @@ public static synchronized void flush(final org.slf4j.Logger logger, final org.s
* @param logger The deferred {@link org.slf4j.Logger}.
* @throws IllegalArgumentException If the specified {@link org.slf4j.Logger} is not a {@link DeferredLogger}.
*/
public static synchronized void flush(final org.slf4j.Logger logger) {
public static void flush(final org.slf4j.Logger logger) {
final DeferredLogger deferredLogger = deferrers.get(logger);
if (deferredLogger == null)
throw new IllegalArgumentException("The specified logger is not a " + DeferredLogger.class.getSimpleName());

deferredLogger.buffer.flush(deferredLogger.logger.getLevel());
}

private final ReentrantLock lock = new ReentrantLock();
private final Logger logger;
private final Level level;
private final String loggerName;
private final boolean isRootLogger;
private final int loggerNameLength;
private final AppenderBuffer buffer;
private final int maxEvents;
private final Supplier<Deque> listSupplier;
private Level deferredLevel;

/**
* Creates a new {@link DeferredLogger} with the specified parameters.
*
* @param logger The {@link Logger}.
* @param level The current {@link Level} value as configured in {@code logback.xml}.
* @param buffer The {@link AppenderBuffer}.
* @throws IllegalArgumentException If any of the specified parameters is null.
* @param maxEvents The maximum number of events to buffer.
* @param listSupplier The {@link Supplier} to create the instance of the {@link Deque} buffer.
* @throws IllegalArgumentException If {@code logger} is null.
*/
private DeferredLogger(final Logger logger, final Level level, final AppenderBuffer buffer) {
this.logger = logger;
if (logger == null)
private DeferredLogger(final Logger logger, final int maxEvents, final Supplier<Deque> listSupplier) {
if ((this.logger = logger) == null)
throw new IllegalArgumentException("logger is null");

this.level = level;
if (level == null)
throw new IllegalArgumentException("level is null");
this.loggerName = logger.getName();
this.loggerNameLength = loggerName.length();
this.isRootLogger = org.slf4j.Logger.ROOT_LOGGER_NAME.equals(loggerName);

if ((this.maxEvents = maxEvents) <= 0)
throw new IllegalArgumentException("maxEvents (" + maxEvents + ") must be positive");

this.buffer = buffer;
if (buffer == null)
throw new IllegalArgumentException("buffer is null");
if ((this.listSupplier = listSupplier) == null)
throw new IllegalArgumentException("listSupplier is null");

this.buffer = new AppenderBuffer();
}

/**
* Set the {@code deferredLevel} value.
*
* @param deferredLevel The deferred {@link Level} value.
*/
private void setDeferredLevel(final Level deferredLevel) {
this.deferredLevel = deferredLevel;
}
}
Loading

0 comments on commit 83a35ac

Please sign in to comment.