
reactor.logback.AsyncAppender Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2013 GoPivotal, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package reactor.logback;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicReference;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.LogbackException;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.AppenderAttachable;
import ch.qos.logback.core.spi.AppenderAttachableImpl;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.FilterAttachableImpl;
import ch.qos.logback.core.spi.FilterReply;
import reactor.core.processor.Processor;
import reactor.core.processor.spec.ProcessorSpec;
import reactor.function.Consumer;
import reactor.function.Supplier;
import reactor.queue.BlockingQueueFactory;
/**
* A Logback appender that logs asynchronously, using a {@link reactor.core.processor.Processor} to hold {@link
* ILoggingEvent logging events} that have yet to be processed.
*
* @author Jon Brisbin
*/
public class AsyncAppender
extends ContextAwareBase
implements Appender,
AppenderAttachable {
private final AppenderAttachableImpl aai = new AppenderAttachableImpl();
private final FilterAttachableImpl fai = new FilterAttachableImpl();
private final AtomicReference> delegate = new AtomicReference>();
private final BlockingQueue publishQueue = BlockingQueueFactory.createQueue();
private volatile boolean started = false;
private int backlog = 1024 * 16;
private String name;
private Processor processor;
private Thread publisher;
@Override public String getName() {
return name;
}
@Override public void setName(String name) {
this.name = name;
}
/**
* Get the backlog size for the internal Reactor {@link reactor.core.processor.Processor}.
*
* @return the size of the {@link reactor.core.processor.Processor} backlog
*/
public int getBacklog() {
return backlog;
}
/**
* Set the backlog size for the internal Reactor {@link reactor.core.processor.Processor}.
*
* @param backlog
* the size of the {@link reactor.core.processor.Processor} backlog
*/
public void setBacklog(int backlog) {
this.backlog = backlog;
}
@Override public void start() {
processor = new ProcessorSpec()
.dataBufferSize(backlog)
.dataSupplier(new Supplier() {
@Override public LogEvent get() {
return new LogEvent();
}
})
.singleThreadedProducer()
.when(Throwable.class, new Consumer() {
@Override public void accept(Throwable t) {
addError(t.getMessage(), t);
}
})
.consume(new Consumer() {
@Override public void accept(LogEvent ev) {
aai.appendLoopOnAppenders(ev.event);
}
})
.get();
publisher = new Thread("async-appender-publisher") {
@Override public void run() {
final List batch = new ArrayList();
ILoggingEvent ev;
while(!Thread.currentThread().isInterrupted()) {
try {
ev = publishQueue.take();
batch.add(ev);
publishQueue.drainTo(batch);
final ListIterator events = new ArrayList(batch).listIterator();
processor.batch(batch.size(), new Consumer() {
public void accept(LogEvent lev) {
lev.event = events.next();
}
});
batch.clear();
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
};
publisher.start();
started = true;
}
@Override public void stop() {
processor.shutdown();
publisher.interrupt();
if(null != delegate.get()) {
delegate.get().stop();
}
started = false;
}
@Override public void doAppend(ILoggingEvent ev) throws LogbackException {
if(getFilterChainDecision(ev) == FilterReply.DENY) {
return;
}
ev.prepareForDeferredProcessing();
publishQueue.add(ev);
}
@Override public void addFilter(Filter newFilter) {
fai.addFilter(newFilter);
}
@Override public void clearAllFilters() {
fai.clearAllFilters();
}
@Override public List> getCopyOfAttachedFiltersList() {
return fai.getCopyOfAttachedFiltersList();
}
@Override public FilterReply getFilterChainDecision(ILoggingEvent event) {
return fai.getFilterChainDecision(event);
}
@Override public boolean isStarted() {
return started;
}
@Override public void addAppender(Appender newAppender) {
if(delegate.compareAndSet(null, newAppender)) {
aai.addAppender(newAppender);
} else {
throw new IllegalArgumentException(delegate.get() + " already attached.");
}
}
@Override public Iterator> iteratorForAppenders() {
return aai.iteratorForAppenders();
}
@Override public Appender getAppender(String name) {
return aai.getAppender(name);
}
@Override public boolean isAttached(Appender appender) {
return aai.isAttached(appender);
}
@Override public void detachAndStopAllAppenders() {
aai.detachAndStopAllAppenders();
}
@Override public boolean detachAppender(Appender appender) {
return aai.detachAppender(appender);
}
@Override public boolean detachAppender(String name) {
return aai.detachAppender(name);
}
private static class LogEvent {
ILoggingEvent event;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy