#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 . /// /// virtual public 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 /// /// virtual public 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. /// /// virtual public 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. /// /// virtual public 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. /// /// virtual public 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. /// /// abstract public 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. /// /// abstract public 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. /// /// abstract public 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. /// /// virtual public 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. /// /// virtual public 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. /// /// abstract public 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. /// /// virtual public bool Configured { get { return m_configured; } set { m_configured = value; } } /// /// Contains a list of internal messages captures during the /// last configuration. /// virtual public 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. /// /// abstract public 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 readonly static 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. /// /// virtual public 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; } } }