All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.qos.logback.ext.spring.DelegatingLogbackAppender Maven / Gradle / Ivy

There is a newer version: 0.1.5
Show newest version
/**
 * Copyright (C) 2014 The logback-extensions developers ([email protected])
 *
 * 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 ch.qos.logback.ext.spring;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;

import java.util.List;

/**
 * A Logback {@code Appender} implementation which delegates the actual appending to a named bean contained in a Spring
 * {@code ApplicationContext}.
 * 

* This appender is similar in spirit to Spring's {@code DelegatingFilterProxy}, which allows servlet filters to be * created and wired in the ApplicationContext and then accessed in the filter chain. As with the filter proxy, the * delegating appender uses its own name to find the target appender in the context. *

* Because the logging framework is usually started before the Spring context, this appender supports caching for * {@code ILoggingEvent}s which are received before the {@code ApplicationContext} is available. This caching has * 3 possible modes: *

    *
  • off - Events are discarded until the {@code ApplicationContext} is available.
  • *
  • on - Events are cached with strong references until the {@code ApplicationContext} is available, at * which time they will be forwarded to the delegate appender. In systems which produce substantial amounts of log * events while starting up the {@code ApplicationContext} this mode may result in heavy memory usage.
  • *
  • soft - Events are wrapped in {@code SoftReference}s and cached until the {@code ApplicationContext} * is available. Memory pressure may cause the garbage collector to collect some or all of the cached events before * the {@code ApplicationContext} is available, so some or all events may be lost. However, in systems with heavy * logging, this mode may result in more efficient memory usage.
  • *
* Caching is {@code on} by default, so strong references will be used for all events. *

* An example of how to use this appender in {@code logback.xml}: *

 * <appender name="appenderBeanName" class="ch.qos.logback.ext.spring.DelegatingLogbackAppender"/>
 * 
*

* Or, if specifying a different cache mode, e.g.: *

 * <appender name="appenderBeanName" class="ch.qos.logback.ext.spring.DelegatingLogbackAppender">
 *     <cacheMode>soft</cacheMode>
 * </appender>
 * 
* Using this appender requires that the {@link ApplicationContextHolder} be included in the {@code ApplicationContext}. * * @author Bryan Turner * @since 0.1 */ public class DelegatingLogbackAppender extends UnsynchronizedAppenderBase { private final Object lock; private String beanName; private ILoggingEventCache cache; private EventCacheMode cacheMode; private volatile Appender delegate; public DelegatingLogbackAppender() { cacheMode = EventCacheMode.ON; lock = new Object(); } public void setCacheMode(String mode) { cacheMode = Enum.valueOf(EventCacheMode.class, mode.toUpperCase()); } @Override public void start() { if (isStarted()) { return; } if (beanName == null || beanName.trim().isEmpty()) { if (name == null || name.trim().isEmpty()) { throw new IllegalStateException("A 'name' or 'beanName' is required for DelegatingLogbackAppender"); } beanName = name; } cache = cacheMode.createCache(); super.start(); } @Override public void stop() { super.stop(); if (cache != null) { cache = null; } if (delegate != null) { delegate.stop(); delegate = null; } } @Override protected void append(ILoggingEvent event) { //Double-check locking here to optimize out the synchronization after the delegate is in place. This also has //the benefit of dealing with the race condition where 2 threads are trying to log and one gets the lock with //the other waiting and the lead thread sets the delegate, logs all cached events and then returns, allowing //the blocked thread to acquire the lock. At that time, the delegate is no longer null and the event is logged //directly to it, rather than being cached. if (delegate == null) { synchronized (lock) { //Note the isStarted() check here. If multiple threads are logging at the time the ApplicationContext //becomes available, the first thread to acquire the lock _may_ stop this appender if the context does //not contain an Appender with the expected name. If that happens, when the lock is released and other //threads acquire it, isStarted() will return false and those threads should return without trying to //use either the delegate or the cache--both of which will be null. if (!isStarted()) { return; } //If we're still started either no thread has attempted to load the delegate yet, or the delegate has //been loaded successfully. If the latter, the delegate will no longer be null if (delegate == null) { if (ApplicationContextHolder.hasApplicationContext()) { //First, load the delegate Appender from the ApplicationContext. If it cannot be loaded, this //appender will be stopped and null will be returned. Appender appender = getDelegate(); if (appender == null) { return; } //Once we have the appender, unload the cache to it. List cachedEvents = cache.get(); for (ILoggingEvent cachedEvent : cachedEvents) { appender.doAppend(cachedEvent); } //If we've found our delegate appender, we no longer need the cache. cache = null; delegate = appender; } else { //Otherwise, if the ApplicationContext is not ready yet, cache this event and wait cache.put(event); return; } } } } //If we make it here, the delegate should always be non-null and safe to append to. delegate.doAppend(event); } private Appender getDelegate() { ApplicationContext context = ApplicationContextHolder.getApplicationContext(); try { @SuppressWarnings("unchecked") Appender appender = context.getBean(beanName, Appender.class); appender.setContext(getContext()); if (!appender.isStarted()) { appender.start(); } return appender; } catch (NoSuchBeanDefinitionException e) { stop(); addError("The ApplicationContext does not contain an Appender named [" + beanName + "]. This delegating appender will now stop processing events.", e); } return null; } public String getBeanName() { return beanName; } public void setBeanName(String beanName) { this.beanName = beanName; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy