net.logstash.logback.appender.DelegatingAsyncDisruptorAppender Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of logstash-logback-encoder Show documentation
Show all versions of logstash-logback-encoder Show documentation
Provides logback encoders, layouts, and appenders to log in JSON and other formats supported by Jackson
/*
* Copyright 2013-2021 the original author or authors.
*
* 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 net.logstash.logback.appender;
import java.io.Flushable;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import net.logstash.logback.appender.listener.AppenderListener;
import ch.qos.logback.access.spi.IAccessEvent;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.AsyncAppenderBase;
import ch.qos.logback.core.OutputStreamAppender;
import ch.qos.logback.core.spi.AppenderAttachable;
import ch.qos.logback.core.spi.AppenderAttachableImpl;
import ch.qos.logback.core.spi.DeferredProcessingAware;
import net.logstash.logback.encoder.com.lmax.disruptor.EventHandler;
import net.logstash.logback.encoder.com.lmax.disruptor.RingBuffer;
/**
* An {@link AsyncDisruptorAppender} that delegates appending of an event
* to delegate {@link #appenders}.
*
* This is very similar to logback's {@link AsyncAppenderBase}, except that:
*
* - it uses a {@link RingBuffer} instead of a {@link BlockingQueue}
* - it allows any number of delegate appenders, instead of just one
* - it flushes appenders of type {@link OutputStreamAppender} or {@link Flushable} at the end of a batch
* - it is resilient to exceptions and guarantees that all appenders are invoked
*
*
* @param type of event ({@link ILoggingEvent} or {@link IAccessEvent}).
*/
public abstract class DelegatingAsyncDisruptorAppender> extends AsyncDisruptorAppender implements AppenderAttachable {
/**
* The delegate appenders.
*/
private final AppenderAttachableImpl appenders = new AppenderAttachableImpl<>();
private class DelegatingEventHandler implements EventHandler> {
/**
* Whether exceptions should be reported with a error status or not.
*/
private boolean silentError;
@Override
public void onEvent(LogEvent logEvent, long sequence, boolean endOfBatch) throws Exception {
boolean exceptionThrown = false;
for (Iterator> it = appenders.iteratorForAppenders(); it.hasNext();) {
Appender appender = it.next();
try {
appender.doAppend(logEvent.event);
/*
* Optimization:
*
* If any of the delegate appenders are instances of OutputStreamAppender or Flushable,
* then flush them at the end of the batch.
*/
if (endOfBatch) {
flushAppender(appender);
}
} catch (Exception e) {
exceptionThrown = true;
if (!this.silentError) {
addError(String.format("Unable to forward event to appender [%s]: %s", appender.getName(), e.getMessage()), e);
}
}
}
this.silentError = exceptionThrown;
}
private void flushAppender(Appender appender) throws IOException {
// Similar to #doAppend() - don't flush if appender is stopped
if (!appender.isStarted()) {
return;
}
if (appender instanceof Flushable) {
flushAppender((Flushable) appender);
} else if (appender instanceof OutputStreamAppender) {
flushAppender((OutputStreamAppender) appender);
}
}
private void flushAppender(OutputStreamAppender appender) throws IOException {
if (!appender.isImmediateFlush()) {
OutputStream os = appender.getOutputStream();
if (os != null) {
os.flush();
}
}
}
private void flushAppender(Flushable appender) throws IOException {
appender.flush();
}
}
@Override
protected EventHandler> createEventHandler() {
return new DelegatingEventHandler();
}
@Override
public void start() {
startDelegateAppenders();
super.start();
}
@Override
public void stop() {
if (!isStarted()) {
return;
}
super.stop();
stopDelegateAppenders();
}
private void startDelegateAppenders() {
for (Iterator> appenderIter = appenders.iteratorForAppenders(); appenderIter.hasNext();) {
Appender appender = appenderIter.next();
if (appender.getContext() == null) {
appender.setContext(getContext());
}
if (!appender.isStarted()) {
appender.start();
}
}
}
private void stopDelegateAppenders() {
for (Iterator> appenderIter = appenders.iteratorForAppenders(); appenderIter.hasNext();) {
Appender appender = appenderIter.next();
if (appender.isStarted()) {
appender.stop();
}
}
}
@Override
public void addAppender(Appender newAppender) {
appenders.addAppender(newAppender);
}
@Override
public Iterator> iteratorForAppenders() {
return appenders.iteratorForAppenders();
}
@Override
public Appender getAppender(String name) {
return appenders.getAppender(name);
}
@Override
public boolean isAttached(Appender appender) {
return appenders.isAttached(appender);
}
@Override
public void detachAndStopAllAppenders() {
appenders.detachAndStopAllAppenders();
}
@Override
public boolean detachAppender(Appender appender) {
return appenders.detachAppender(appender);
}
@Override
public boolean detachAppender(String name) {
return appenders.detachAppender(name);
}
}