/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.logging.log4j.core.appender;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.AbstractLifeCycle;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.ConfigurationException;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.status.StatusLogger;

Abstract base class used to register managers.

This class implements AutoCloseable mostly to allow unit tests to be written safely and succinctly. While managers do need to allocate resources (usually on construction) and then free these resources, a manager is longer lived than other auto-closeable objects like streams. None the less, making a manager AutoCloseable forces readers to be aware of the the pattern: allocate resources on construction and call close() at some point.

/** * Abstract base class used to register managers. * <p> * This class implements {@link AutoCloseable} mostly to allow unit tests to be written safely and succinctly. While * managers do need to allocate resources (usually on construction) and then free these resources, a manager is longer * lived than other auto-closeable objects like streams. None the less, making a manager AutoCloseable forces readers to * be aware of the the pattern: allocate resources on construction and call {@link #close()} at some point. * </p> */
public abstract class AbstractManager implements AutoCloseable {
Allow subclasses access to the status logger without creating another instance.
/** * Allow subclasses access to the status logger without creating another instance. */
protected static final Logger LOGGER = StatusLogger.getLogger(); // Need to lock that map instead of using a ConcurrentMap due to stop removing the // manager from the map and closing the stream, requiring the whole stop method to be locked. private static final Map<String, AbstractManager> MAP = new HashMap<>(); private static final Lock LOCK = new ReentrantLock();
Number of Appenders using this manager.
/** * Number of Appenders using this manager. */
protected int count; private final String name; private final LoggerContext loggerContext; protected AbstractManager(final LoggerContext loggerContext, final String name) { this.loggerContext = loggerContext; this.name = name; LOGGER.debug("Starting {} {}", this.getClass().getSimpleName(), name); }
Called to signify that this Manager is no longer required by an Appender.
/** * Called to signify that this Manager is no longer required by an Appender. */
@Override public void close() { stop(AbstractLifeCycle.DEFAULT_STOP_TIMEOUT, AbstractLifeCycle.DEFAULT_STOP_TIMEUNIT); } public boolean stop(final long timeout, final TimeUnit timeUnit) { boolean stopped = true; LOCK.lock(); try { --count; if (count <= 0) { MAP.remove(name); LOGGER.debug("Shutting down {} {}", this.getClass().getSimpleName(), getName()); stopped = releaseSub(timeout, timeUnit); LOGGER.debug("Shut down {} {}, all resources released: {}", this.getClass().getSimpleName(), getName(), stopped); } } finally { LOCK.unlock(); } return stopped; }
Retrieves a Manager if it has been previously created or creates a new Manager.
Params:
  • name – The name of the Manager to retrieve.
  • factory – The Factory to use to create the Manager.
  • data – An Object that should be passed to the factory when creating the Manager.
Type parameters:
  • <M> – The Type of the Manager to be created.
  • <T> – The type of the Factory data.
Returns:A Manager with the specified name and type.
/** * Retrieves a Manager if it has been previously created or creates a new Manager. * @param name The name of the Manager to retrieve. * @param factory The Factory to use to create the Manager. * @param data An Object that should be passed to the factory when creating the Manager. * @param <M> The Type of the Manager to be created. * @param <T> The type of the Factory data. * @return A Manager with the specified name and type. */
// @SuppressWarnings("resource"): this is a factory method, the resource is allocated and released elsewhere. @SuppressWarnings("resource") public static <M extends AbstractManager, T> M getManager(final String name, final ManagerFactory<M, T> factory, final T data) { LOCK.lock(); try { @SuppressWarnings("unchecked") M manager = (M) MAP.get(name); if (manager == null) { manager = factory.createManager(name, data); if (manager == null) { throw new IllegalStateException("ManagerFactory [" + factory + "] unable to create manager for [" + name + "] with data [" + data + "]"); } MAP.put(name, manager); } else { manager.updateData(data); } manager.count++; return manager; } finally { LOCK.unlock(); } } public void updateData(final Object data) { // This default implementation does nothing. }
Determines if a Manager with the specified name exists.
Params:
  • name – The name of the Manager.
Returns:True if the Manager exists, false otherwise.
/** * Determines if a Manager with the specified name exists. * @param name The name of the Manager. * @return True if the Manager exists, false otherwise. */
public static boolean hasManager(final String name) { LOCK.lock(); try { return MAP.containsKey(name); } finally { LOCK.unlock(); } }
Returns the specified manager, cast to the specified narrow type.
Params:
  • narrowClass – the type to cast to
  • manager – the manager object to return
Type parameters:
  • <M> – the narrow type
Throws:
  • ConfigurationException – if the manager cannot be cast to the specified type, which only happens when the configuration has multiple incompatible appenders pointing to the same resource
See Also:
Returns:the specified manager, cast to the specified narrow type
Since:2.9
/** * Returns the specified manager, cast to the specified narrow type. * @param narrowClass the type to cast to * @param manager the manager object to return * @param <M> the narrow type * @return the specified manager, cast to the specified narrow type * @throws ConfigurationException if the manager cannot be cast to the specified type, which only happens when * the configuration has multiple incompatible appenders pointing to the same resource * @since 2.9 * @see <a href="https://issues.apache.org/jira/browse/LOG4J2-1908">LOG4J2-1908</a> */
protected static <M extends AbstractManager> M narrow(final Class<M> narrowClass, final AbstractManager manager) { if (narrowClass.isAssignableFrom(manager.getClass())) { return (M) manager; } throw new ConfigurationException( "Configuration has multiple incompatible Appenders pointing to the same resource '" + manager.getName() + "'"); } protected static StatusLogger logger() { return StatusLogger.getLogger(); }
May be overridden by managers to perform processing while the manager is being released and the lock is held. A timeout is passed for implementors to use as they see fit.
Params:
  • timeout – timeout
  • timeUnit – timeout time unit
Returns:true if all resources were closed normally, false otherwise.
/** * May be overridden by managers to perform processing while the manager is being released and the * lock is held. A timeout is passed for implementors to use as they see fit. * @param timeout timeout * @param timeUnit timeout time unit * @return true if all resources were closed normally, false otherwise. */
protected boolean releaseSub(final long timeout, final TimeUnit timeUnit) { // This default implementation does nothing. return true; } protected int getCount() { return count; }
Gets the logger context used to create this instance or null. The logger context is usually set when an appender creates a manager and that appender is given a Configuration. Not all appenders are given a Configuration by their factory method or builder.
Returns:the logger context used to create this instance or null.
/** * Gets the logger context used to create this instance or null. The logger context is usually set when an appender * creates a manager and that appender is given a Configuration. Not all appenders are given a Configuration by * their factory method or builder. * * @return the logger context used to create this instance or null. */
public LoggerContext getLoggerContext() { return loggerContext; }
Called to signify that this Manager is no longer required by an Appender.
Deprecated:In 2.7, use close().
/** * Called to signify that this Manager is no longer required by an Appender. * @deprecated In 2.7, use {@link #close()}. */
@Deprecated public void release() { close(); }
Returns the name of the Manager.
Returns:The name of the Manager.
/** * Returns the name of the Manager. * @return The name of the Manager. */
public String getName() { return name; }
Provide a description of the content format supported by this Manager. Default implementation returns an empty (unspecified) Map.
Returns:a Map of key/value pairs describing the Manager-specific content format, or an empty Map if no content format descriptors are specified.
/** * Provide a description of the content format supported by this Manager. Default implementation returns an empty * (unspecified) Map. * * @return a Map of key/value pairs describing the Manager-specific content format, or an empty Map if no content * format descriptors are specified. */
public Map<String, String> getContentFormat() { return new HashMap<>(); } protected void log(final Level level, final String message, final Throwable throwable) { final Message m = LOGGER.getMessageFactory().newMessage("{} {} {}: {}", getClass().getSimpleName(), getName(), message, throwable); LOGGER.log(level, m, throwable); } protected void logDebug(final String message, final Throwable throwable) { log(Level.DEBUG, message, throwable); } protected void logError(final String message, final Throwable throwable) { log(Level.ERROR, message, throwable); } protected void logWarn(final String message, final Throwable throwable) { log(Level.WARN, message, throwable); } }