#region Apache License
//
// 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.
//
#endregion
using System;
using System.Collections;
using log4net.ObjectRenderer;
using log4net.Core;
using log4net.Util;
using log4net.Plugin;
using System.Threading;
namespace log4net.Repository
{
///
/// Base implementation of
///
///
///
/// Default abstract implementation of the interface.
///
///
/// Skeleton implementation of the interface.
/// All types can extend this type.
///
///
/// Nicko Cadell
/// Gert Driesen
public abstract class LoggerRepositorySkeleton : ILoggerRepository, Appender.IFlushable
{
#region Member Variables
private string m_name;
private RendererMap m_rendererMap;
private PluginMap m_pluginMap;
private LevelMap m_levelMap;
private Level m_threshold;
private bool m_configured;
private ICollection m_configurationMessages;
private event LoggerRepositoryShutdownEventHandler m_shutdownEvent;
private event LoggerRepositoryConfigurationResetEventHandler m_configurationResetEvent;
private event LoggerRepositoryConfigurationChangedEventHandler m_configurationChangedEvent;
private PropertiesDictionary m_properties;
#endregion
#region Constructors
///
/// Default Constructor
///
///
///
/// Initializes the repository with default (empty) properties.
///
///
protected LoggerRepositorySkeleton() : this(new PropertiesDictionary())
{
}
///
/// Construct the repository using specific properties
///
/// the properties to set for this repository
///
///
/// Initializes the repository with specified properties.
///
///
protected LoggerRepositorySkeleton(PropertiesDictionary properties)
{
m_properties = properties;
m_rendererMap = new RendererMap();
m_pluginMap = new PluginMap(this);
m_levelMap = new LevelMap();
m_configurationMessages = EmptyCollection.Instance;
m_configured = false;
AddBuiltinLevels();
// Don't disable any levels by default.
m_threshold = Level.All;
}
#endregion
#region Implementation of ILoggerRepository
///
/// The name of the repository
///
///
/// The string name of the repository
///
///
///
/// The name of this repository. The name is
/// used to store and lookup the repositories
/// stored by the .
///
///
public virtual string Name
{
get { return m_name; }
set { m_name = value; }
}
///
/// The threshold for all events in this repository
///
///
/// The threshold for all events in this repository
///
///
///
/// The threshold for all events in this repository
///
///
public virtual Level Threshold
{
get { return m_threshold; }
set
{
if (value != null)
{
m_threshold = value;
}
else
{
// Must not set threshold to null
LogLog.Warn(declaringType, "LoggerRepositorySkeleton: Threshold cannot be set to null. Setting to ALL");
m_threshold = Level.All;
}
}
}
///
/// RendererMap accesses the object renderer map for this repository.
///
///
/// RendererMap accesses the object renderer map for this repository.
///
///
///
/// RendererMap accesses the object renderer map for this repository.
///
///
/// The RendererMap holds a mapping between types and
/// objects.
///
///
public virtual RendererMap RendererMap
{
get { return m_rendererMap; }
}
///
/// The plugin map for this repository.
///
///
/// The plugin map for this repository.
///
///
///
/// The plugin map holds the instances
/// that have been attached to this repository.
///
///
public virtual PluginMap PluginMap
{
get { return m_pluginMap; }
}
///
/// Get the level map for the Repository.
///
///
///
/// Get the level map for the Repository.
///
///
/// The level map defines the mappings between
/// level names and objects in
/// this repository.
///
///
public virtual LevelMap LevelMap
{
get { return m_levelMap; }
}
///
/// Test if logger exists
///
/// The name of the logger to lookup
/// The Logger object with the name specified
///
///
/// Check if the named logger exists in the repository. If so return
/// its reference, otherwise returns null.
///
///
public abstract ILogger Exists(string name);
///
/// Returns all the currently defined loggers in the repository
///
/// All the defined loggers
///
///
/// Returns all the currently defined loggers in the repository as an Array.
///
///
public abstract ILogger[] GetCurrentLoggers();
///
/// Return a new logger instance
///
/// The name of the logger to retrieve
/// The logger object with the name specified
///
///
/// Return a new logger instance.
///
///
/// If a logger of that name already exists, then it will be
/// returned. Otherwise, a new logger will be instantiated and
/// then linked with its existing ancestors as well as children.
///
///
public abstract ILogger GetLogger(string name);
///
/// Shutdown the repository
///
///
///
/// Shutdown the repository. Can be overridden in a subclass.
/// This base class implementation notifies the
/// listeners and all attached plugins of the shutdown event.
///
///
public virtual void Shutdown()
{
// Shutdown attached plugins
foreach(IPlugin plugin in PluginMap.AllPlugins)
{
plugin.Shutdown();
}
// Notify listeners
OnShutdown(null);
}
///
/// Reset the repositories configuration to a default state
///
///
///
/// Reset all values contained in this instance to their
/// default state.
///
///
/// Existing loggers are not removed. They are just reset.
///
///
/// This method should be used sparingly and with care as it will
/// block all logging until it is completed.
///
///
public virtual void ResetConfiguration()
{
// Clear internal data structures
m_rendererMap.Clear();
m_levelMap.Clear();
m_configurationMessages = EmptyCollection.Instance;
// Add the predefined levels to the map
AddBuiltinLevels();
Configured = false;
// Notify listeners
OnConfigurationReset(null);
}
///
/// Log the logEvent through this repository.
///
/// the event to log
///
///
/// This method should not normally be used to log.
/// The interface should be used
/// for routine logging. This interface can be obtained
/// using the method.
///
///
/// The logEvent is delivered to the appropriate logger and
/// that logger is then responsible for logging the event.
///
///
public abstract void Log(LoggingEvent logEvent);
///
/// Flag indicates if this repository has been configured.
///
///
/// Flag indicates if this repository has been configured.
///
///
///
/// Flag indicates if this repository has been configured.
///
///
public virtual bool Configured
{
get { return m_configured; }
set { m_configured = value; }
}
///
/// Contains a list of internal messages captures during the
/// last configuration.
///
public virtual ICollection ConfigurationMessages
{
get { return m_configurationMessages; }
set { m_configurationMessages = value; }
}
///
/// Event to notify that the repository has been shutdown.
///
///
/// Event to notify that the repository has been shutdown.
///
///
///
/// Event raised when the repository has been shutdown.
///
///
public event LoggerRepositoryShutdownEventHandler ShutdownEvent
{
add { m_shutdownEvent += value; }
remove { m_shutdownEvent -= value; }
}
///
/// Event to notify that the repository has had its configuration reset.
///
///
/// Event to notify that the repository has had its configuration reset.
///
///
///
/// Event raised when the repository's configuration has been
/// reset to default.
///
///
public event LoggerRepositoryConfigurationResetEventHandler ConfigurationReset
{
add { m_configurationResetEvent += value; }
remove { m_configurationResetEvent -= value; }
}
///
/// Event to notify that the repository has had its configuration changed.
///
///
/// Event to notify that the repository has had its configuration changed.
///
///
///
/// Event raised when the repository's configuration has been changed.
///
///
public event LoggerRepositoryConfigurationChangedEventHandler ConfigurationChanged
{
add { m_configurationChangedEvent += value; }
remove { m_configurationChangedEvent -= value; }
}
///
/// Repository specific properties
///
///
/// Repository specific properties
///
///
/// These properties can be specified on a repository specific basis
///
public PropertiesDictionary Properties
{
get { return m_properties; }
}
///
/// Returns all the Appenders that are configured as an Array.
///
/// All the Appenders
///
///
/// Returns all the Appenders that are configured as an Array.
///
///
public abstract log4net.Appender.IAppender[] GetAppenders();
#endregion
#region Private Static Fields
///
/// The fully qualified type of the LoggerRepositorySkeleton class.
///
///
/// Used by the internal logger to record the Type of the
/// log message.
///
private static readonly Type declaringType = typeof(LoggerRepositorySkeleton);
#endregion Private Static Fields
private void AddBuiltinLevels()
{
// Add the predefined levels to the map
m_levelMap.Add(Level.Off);
// Unrecoverable errors
m_levelMap.Add(Level.Emergency);
m_levelMap.Add(Level.Fatal);
m_levelMap.Add(Level.Alert);
// Recoverable errors
m_levelMap.Add(Level.Critical);
m_levelMap.Add(Level.Severe);
m_levelMap.Add(Level.Error);
m_levelMap.Add(Level.Warn);
// Information
m_levelMap.Add(Level.Notice);
m_levelMap.Add(Level.Info);
// Debug
m_levelMap.Add(Level.Debug);
m_levelMap.Add(Level.Fine);
m_levelMap.Add(Level.Trace);
m_levelMap.Add(Level.Finer);
m_levelMap.Add(Level.Verbose);
m_levelMap.Add(Level.Finest);
m_levelMap.Add(Level.All);
}
///
/// Adds an object renderer for a specific class.
///
/// The type that will be rendered by the renderer supplied.
/// The object renderer used to render the object.
///
///
/// Adds an object renderer for a specific class.
///
///
public virtual void AddRenderer(Type typeToRender, IObjectRenderer rendererInstance)
{
if (typeToRender == null)
{
throw new ArgumentNullException("typeToRender");
}
if (rendererInstance == null)
{
throw new ArgumentNullException("rendererInstance");
}
m_rendererMap.Put(typeToRender, rendererInstance);
}
///
/// Notify the registered listeners that the repository is shutting down
///
/// Empty EventArgs
///
///
/// Notify any listeners that this repository is shutting down.
///
///
protected virtual void OnShutdown(EventArgs e)
{
if (e == null)
{
e = EventArgs.Empty;
}
LoggerRepositoryShutdownEventHandler handler = m_shutdownEvent;
if (handler != null)
{
handler(this, e);
}
}
///
/// Notify the registered listeners that the repository has had its configuration reset
///
/// Empty EventArgs
///
///
/// Notify any listeners that this repository's configuration has been reset.
///
///
protected virtual void OnConfigurationReset(EventArgs e)
{
if (e == null)
{
e = EventArgs.Empty;
}
LoggerRepositoryConfigurationResetEventHandler handler = m_configurationResetEvent;
if (handler != null)
{
handler(this, e);
}
}
///
/// Notify the registered listeners that the repository has had its configuration changed
///
/// Empty EventArgs
///
///
/// Notify any listeners that this repository's configuration has changed.
///
///
protected virtual void OnConfigurationChanged(EventArgs e)
{
if (e == null)
{
e = EventArgs.Empty;
}
LoggerRepositoryConfigurationChangedEventHandler handler = m_configurationChangedEvent;
if (handler != null)
{
handler(this, e);
}
}
///
/// Raise a configuration changed event on this repository
///
/// EventArgs.Empty
///
///
/// Applications that programmatically change the configuration of the repository should
/// raise this event notification to notify listeners.
///
///
public void RaiseConfigurationChanged(EventArgs e)
{
OnConfigurationChanged(e);
}
private static int GetWaitTime(DateTime startTimeUtc, int millisecondsTimeout)
{
if (millisecondsTimeout == Timeout.Infinite) return Timeout.Infinite;
if (millisecondsTimeout == 0) return 0;
int elapsedMilliseconds = (int)(DateTime.UtcNow - startTimeUtc).TotalMilliseconds;
int timeout = millisecondsTimeout - elapsedMilliseconds;
if (timeout < 0) timeout = 0;
return timeout;
}
///
/// Flushes all configured Appenders that implement .
///
/// The maximum time in milliseconds to wait for logging events from asycnhronous appenders to be flushed,
/// or to wait indefinitely.
/// True if all logging events were flushed successfully, else false.
public bool Flush(int millisecondsTimeout)
{
if (millisecondsTimeout < -1) throw new ArgumentOutOfRangeException("millisecondsTimeout", "Timeout must be -1 (Timeout.Infinite) or non-negative");
// Assume success until one of the appenders fails
bool result = true;
// Use DateTime.UtcNow rather than a System.Diagnostics.Stopwatch for compatibility with .NET 1.x
DateTime startTimeUtc = DateTime.UtcNow;
// Do buffering appenders first. These may be forwarding to other appenders
foreach(log4net.Appender.IAppender appender in GetAppenders())
{
log4net.Appender.IFlushable flushable = appender as log4net.Appender.IFlushable;
if (flushable == null) continue;
if (appender is Appender.BufferingAppenderSkeleton)
{
int timeout = GetWaitTime(startTimeUtc, millisecondsTimeout);
if (!flushable.Flush(timeout)) result = false;
}
}
// Do non-buffering appenders.
foreach (log4net.Appender.IAppender appender in GetAppenders())
{
log4net.Appender.IFlushable flushable = appender as log4net.Appender.IFlushable;
if (flushable == null) continue;
if (!(appender is Appender.BufferingAppenderSkeleton))
{
int timeout = GetWaitTime(startTimeUtc, millisecondsTimeout);
if (!flushable.Flush(timeout)) result = false;
}
}
return result;
}
}
}