nebula.plugin.metrics.collector.LogbackCollector Maven / Gradle / Ivy
/*
* Copyright 2015 Netflix, Inc.
*
* 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 nebula.plugin.metrics.collector;
import nebula.plugin.metrics.MetricsPluginExtension;
import nebula.plugin.metrics.dispatcher.MetricsDispatcher;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.classic.spi.TurboFilterList;
import ch.qos.logback.classic.turbo.TurboFilter;
import ch.qos.logback.core.spi.FilterReply;
import com.google.common.base.Supplier;
import com.google.common.util.concurrent.Service;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Collector that intercepts Logback events by binding an appender to a provided {@link Logger}.
*
* @author Danny THomas
*/
public class LogbackCollector {
/**
* Thread local to prevent stack overflows when logging statements within the event dispatcher chain log while
* processing another event.
*/
private static final ThreadLocal IN_FILTER = new ThreadLocal() {
@Override
protected Boolean initialValue() {
return false;
}
};
/**
* Logging marker to indicate messages that should be collected, regardless of logging level.
*/
private static final Marker MARKER = MarkerFactory.getMarker("metrics");
/**
* Configure a logback filter to capture all root logging events.
*
* Avoids having to depend on a particular Gradle logging level being set. Gradle's logging is such that
* encoders/layouts/etc aren't an option and LogbackLoggingConfigurer.doConfigure() adds a TurboFilter which
* prevents us getting at those events, so we re-wire the filters so ours comes first.
*/
public static void configureLogbackCollection(final Supplier dispatcherSupplier, final MetricsPluginExtension extension) {
checkNotNull(dispatcherSupplier);
checkNotNull(extension);
final BlockingQueue logbackEvents = new LinkedBlockingQueue<>();
TurboFilter metricsFilter = new TurboFilter() {
@Override
public FilterReply decide(Marker marker, Logger logger, Level level, String s, Object[] objects, Throwable throwable) {
if (IN_FILTER.get()) {
return FilterReply.NEUTRAL;
}
try {
IN_FILTER.set(true);
if (level.isGreaterOrEqual(extension.getLogLevel()) || MARKER.equals(marker)) {
LoggingEvent event = new LoggingEvent(Logger.class.getCanonicalName(), logger, level, s, throwable, objects);
MetricsDispatcher dispatcher = dispatcherSupplier.get();
if (dispatcher.state() == Service.State.NEW || dispatcher.state() == Service.State.STARTING) {
logbackEvents.add(event);
} else {
if (!logbackEvents.isEmpty()) {
for (LoggingEvent loggingEvent : logbackEvents) {
dispatcher.logbackEvent(loggingEvent);
}
}
dispatcher.logbackEvent(event);
}
}
return FilterReply.NEUTRAL;
} finally {
IN_FILTER.set(false);
}
}
};
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
TurboFilterList filterList = context.getTurboFilterList();
if (!filterList.isEmpty()) {
TurboFilter gradleFilter = filterList.get(0);
context.resetTurboFilterList();
context.addTurboFilter(metricsFilter);
context.addTurboFilter(gradleFilter);
} else {
context.resetTurboFilterList();
context.addTurboFilter(metricsFilter);
}
}
public static void resetLogbackCollection() {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
TurboFilterList filterList = context.getTurboFilterList();
if (filterList.size() == 2) {
TurboFilter gradleFilter = filterList.get(1);
context.resetTurboFilterList();
context.addTurboFilter(gradleFilter);
} else {
assert filterList.size() == 1 : "Expected 1 filter to be registered, found " + filterList.size();
context.resetTurboFilterList();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy