#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.Appender; using log4net.Core; using log4net.Repository; using log4net.Util; namespace log4net.Repository.Hierarchy { #region LoggerCreationEvent /// <summary> /// Delegate used to handle logger creation event notifications. /// </summary> /// <param name="sender">The <see cref="Hierarchy"/> in which the <see cref="Logger"/> has been created.</param> /// <param name="e">The <see cref="LoggerCreationEventArgs"/> event args that hold the <see cref="Logger"/> instance that has been created.</param> /// <remarks> /// <para> /// Delegate used to handle logger creation event notifications. /// </para> /// </remarks> public delegate void LoggerCreationEventHandler(object sender, LoggerCreationEventArgs e); /// <summary> /// Provides data for the <see cref="Hierarchy.LoggerCreatedEvent"/> event. /// </summary> /// <remarks> /// <para> /// A <see cref="Hierarchy.LoggerCreatedEvent"/> event is raised every time a /// <see cref="Logger"/> is created. /// </para> /// </remarks> public class LoggerCreationEventArgs : EventArgs { /// <summary> /// The <see cref="Logger"/> created /// </summary> private Logger m_log; /// <summary> /// Constructor /// </summary> /// <param name="log">The <see cref="Logger"/> that has been created.</param> /// <remarks> /// <para> /// Initializes a new instance of the <see cref="LoggerCreationEventArgs" /> event argument /// class,with the specified <see cref="Logger"/>. /// </para> /// </remarks> public LoggerCreationEventArgs(Logger log) { m_log = log; } /// <summary> /// Gets the <see cref="Logger"/> that has been created. /// </summary> /// <value> /// The <see cref="Logger"/> that has been created. /// </value> /// <remarks> /// <para> /// The <see cref="Logger"/> that has been created. /// </para> /// </remarks> public Logger Logger { get { return m_log; } } } #endregion LoggerCreationEvent /// <summary> /// Hierarchical organization of loggers /// </summary> /// <remarks> /// <para> /// <i>The casual user should not have to deal with this class /// directly.</i> /// </para> /// <para> /// This class is specialized in retrieving loggers by name and /// also maintaining the logger hierarchy. Implements the /// <see cref="ILoggerRepository"/> interface. /// </para> /// <para> /// The structure of the logger hierarchy is maintained by the /// <see cref="M:GetLogger(string)"/> method. The hierarchy is such that children /// link to their parent but parents do not have any references to their /// children. Moreover, loggers can be instantiated in any order, in /// particular descendant before ancestor. /// </para> /// <para> /// In case a descendant is created before a particular ancestor, /// then it creates a provision node for the ancestor and adds itself /// to the provision node. Other descendants of the same ancestor add /// themselves to the previously created provision node. /// </para> /// </remarks> /// <author>Nicko Cadell</author> /// <author>Gert Driesen</author> public class Hierarchy : LoggerRepositorySkeleton, IBasicRepositoryConfigurator, IXmlRepositoryConfigurator { #region Public Events /// <summary> /// Event used to notify that a logger has been created. /// </summary> /// <remarks> /// <para> /// Event raised when a logger is created. /// </para> /// </remarks> public event LoggerCreationEventHandler LoggerCreatedEvent { add { m_loggerCreatedEvent += value; } remove { m_loggerCreatedEvent -= value; } } #endregion Public Events #region Public Instance Constructors /// <summary> /// Default constructor /// </summary> /// <remarks> /// <para> /// Initializes a new instance of the <see cref="Hierarchy" /> class. /// </para> /// </remarks> public Hierarchy() : this(new DefaultLoggerFactory()) { } /// <summary> /// Construct with properties /// </summary> /// <param name="properties">The properties to pass to this repository.</param> /// <remarks> /// <para> /// Initializes a new instance of the <see cref="Hierarchy" /> class. /// </para> /// </remarks> public Hierarchy(PropertiesDictionary properties) : this(properties, new DefaultLoggerFactory()) { } /// <summary> /// Construct with a logger factory /// </summary> /// <param name="loggerFactory">The factory to use to create new logger instances.</param> /// <remarks> /// <para> /// Initializes a new instance of the <see cref="Hierarchy" /> class with /// the specified <see cref="ILoggerFactory" />. /// </para> /// </remarks> public Hierarchy(ILoggerFactory loggerFactory) : this(new PropertiesDictionary(), loggerFactory) { } /// <summary> /// Construct with properties and a logger factory /// </summary> /// <param name="properties">The properties to pass to this repository.</param> /// <param name="loggerFactory">The factory to use to create new logger instances.</param> /// <remarks> /// <para> /// Initializes a new instance of the <see cref="Hierarchy" /> class with /// the specified <see cref="ILoggerFactory" />. /// </para> /// </remarks> public Hierarchy(PropertiesDictionary properties, ILoggerFactory loggerFactory) : base(properties) { if (loggerFactory == null) { throw new ArgumentNullException("loggerFactory"); } m_defaultFactory = loggerFactory; m_ht = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable()); } #endregion Public Instance Constructors #region Public Instance Properties /// <summary> /// Has no appender warning been emitted /// </summary> /// <remarks> /// <para> /// Flag to indicate if we have already issued a warning /// about not having an appender warning. /// </para> /// </remarks> public bool EmittedNoAppenderWarning { get { return m_emittedNoAppenderWarning; } set { m_emittedNoAppenderWarning = value; } } /// <summary> /// Get the root of this hierarchy /// </summary> /// <remarks> /// <para> /// Get the root of this hierarchy. /// </para> /// </remarks> public Logger Root { get { if (m_root == null) { lock(this) { if (m_root == null) { // Create the root logger Logger root = m_defaultFactory.CreateLogger(this, null); root.Hierarchy = this; // Store root m_root = root; } } } return m_root; } } /// <summary> /// Gets or sets the default <see cref="ILoggerFactory" /> instance. /// </summary> /// <value>The default <see cref="ILoggerFactory" /></value> /// <remarks> /// <para> /// The logger factory is used to create logger instances. /// </para> /// </remarks> public ILoggerFactory LoggerFactory { get { return m_defaultFactory; } set { if (value == null) { throw new ArgumentNullException("value"); } m_defaultFactory = value; } } #endregion Public Instance Properties #region Override Implementation of LoggerRepositorySkeleton /// <summary> /// Test if a logger exists /// </summary> /// <param name="name">The name of the logger to lookup</param> /// <returns>The Logger object with the name specified</returns> /// <remarks> /// <para> /// Check if the named logger exists in the hierarchy. If so return /// its reference, otherwise returns <c>null</c>. /// </para> /// </remarks> override public ILogger Exists(string name) { if (name == null) { throw new ArgumentNullException("name"); } lock(m_ht) { return m_ht[new LoggerKey(name)] as Logger; } } /// <summary> /// Returns all the currently defined loggers in the hierarchy as an Array /// </summary> /// <returns>All the defined loggers</returns> /// <remarks> /// <para> /// Returns all the currently defined loggers in the hierarchy as an Array. /// The root logger is <b>not</b> included in the returned /// enumeration. /// </para> /// </remarks> override public ILogger[] GetCurrentLoggers() { // The accumulation in loggers is necessary because not all elements in // ht are Logger objects as there might be some ProvisionNodes // as well. lock(m_ht) { System.Collections.ArrayList loggers = new System.Collections.ArrayList(m_ht.Count); // Iterate through m_ht values foreach(object node in m_ht.Values) { if (node is Logger) { loggers.Add(node); } } return (Logger[])loggers.ToArray(typeof(Logger)); } } /// <summary> /// Return a new logger instance named as the first parameter using /// the default factory. /// </summary> /// <remarks> /// <para> /// Return a new logger instance named as the first parameter using /// the default factory. /// </para> /// <para> /// 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. /// </para> /// </remarks> /// <param name="name">The name of the logger to retrieve</param> /// <returns>The logger object with the name specified</returns> override public ILogger GetLogger(string name) { if (name == null) { throw new ArgumentNullException("name"); } return GetLogger(name, m_defaultFactory); } /// <summary> /// Shutting down a hierarchy will <i>safely</i> close and remove /// all appenders in all loggers including the root logger. /// </summary> /// <remarks> /// <para> /// Shutting down a hierarchy will <i>safely</i> close and remove /// all appenders in all loggers including the root logger. /// </para> /// <para> /// Some appenders need to be closed before the /// application exists. Otherwise, pending logging events might be /// lost. /// </para> /// <para> /// The <c>Shutdown</c> method is careful to close nested /// appenders before closing regular appenders. This is allows /// configurations where a regular appender is attached to a logger /// and again to a nested appender. /// </para> /// </remarks> override public void Shutdown() { LogLog.Debug(declaringType, "Shutdown called on Hierarchy ["+this.Name+"]"); // begin by closing nested appenders Root.CloseNestedAppenders(); lock(m_ht) { ILogger[] currentLoggers = this.GetCurrentLoggers(); foreach(Logger logger in currentLoggers) { logger.CloseNestedAppenders(); } // then, remove all appenders Root.RemoveAllAppenders(); foreach(Logger logger in currentLoggers) { logger.RemoveAllAppenders(); } } base.Shutdown(); } /// <summary> /// Reset all values contained in this hierarchy instance to their default. /// </summary> /// <remarks> /// <para> /// Reset all values contained in this hierarchy instance to their /// default. This removes all appenders from all loggers, sets /// the level of all non-root loggers to <c>null</c>, /// sets their additivity flag to <c>true</c> and sets the level /// of the root logger to <see cref="Level.Debug"/>. Moreover, /// message disabling is set its default "off" value. /// </para> /// <para> /// Existing loggers are not removed. They are just reset. /// </para> /// <para> /// This method should be used sparingly and with care as it will /// block all logging until it is completed. /// </para> /// </remarks> override public void ResetConfiguration() { Root.Level = LevelMap.LookupWithDefault(Level.Debug); Threshold = LevelMap.LookupWithDefault(Level.All); // the synchronization is needed to prevent hashtable surprises lock(m_ht) { Shutdown(); // nested locks are OK foreach(Logger l in this.GetCurrentLoggers()) { l.Level = null; l.Additivity = true; } } base.ResetConfiguration(); // Notify listeners OnConfigurationChanged(null); } /// <summary> /// Log the logEvent through this hierarchy. /// </summary> /// <param name="logEvent">the event to log</param> /// <remarks> /// <para> /// This method should not normally be used to log. /// The <see cref="ILog"/> interface should be used /// for routine logging. This interface can be obtained /// using the <see cref="M:log4net.LogManager.GetLogger(string)"/> method. /// </para> /// <para> /// The <c>logEvent</c> is delivered to the appropriate logger and /// that logger is then responsible for logging the event. /// </para> /// </remarks> override public void Log(LoggingEvent logEvent) { if (logEvent == null) { throw new ArgumentNullException("logEvent"); } this.GetLogger(logEvent.LoggerName, m_defaultFactory).Log(logEvent); } /// <summary> /// Returns all the Appenders that are currently configured /// </summary> /// <returns>An array containing all the currently configured appenders</returns> /// <remarks> /// <para> /// Returns all the <see cref="log4net.Appender.IAppender"/> instances that are currently configured. /// All the loggers are searched for appenders. The appenders may also be containers /// for appenders and these are also searched for additional loggers. /// </para> /// <para> /// The list returned is unordered but does not contain duplicates. /// </para> /// </remarks> override public Appender.IAppender[] GetAppenders() { System.Collections.ArrayList appenderList = new System.Collections.ArrayList(); CollectAppenders(appenderList, Root); foreach(Logger logger in GetCurrentLoggers()) { CollectAppenders(appenderList, logger); } return (Appender.IAppender[])appenderList.ToArray(typeof(Appender.IAppender)); } #endregion Override Implementation of LoggerRepositorySkeleton #region Private Static Methods /// <summary> /// Collect the appenders from an <see cref="IAppenderAttachable"/>. /// The appender may also be a container. /// </summary> /// <param name="appenderList"></param> /// <param name="appender"></param> private static void CollectAppender(System.Collections.ArrayList appenderList, Appender.IAppender appender) { if (!appenderList.Contains(appender)) { appenderList.Add(appender); IAppenderAttachable container = appender as IAppenderAttachable; if (container != null) { CollectAppenders(appenderList, container); } } } /// <summary> /// Collect the appenders from an <see cref="IAppenderAttachable"/> container /// </summary> /// <param name="appenderList"></param> /// <param name="container"></param> private static void CollectAppenders(System.Collections.ArrayList appenderList, IAppenderAttachable container) { foreach(Appender.IAppender appender in container.Appenders) { CollectAppender(appenderList, appender); } } #endregion #region Implementation of IBasicRepositoryConfigurator /// <summary> /// Initialize the log4net system using the specified appender /// </summary> /// <param name="appender">the appender to use to log all logging events</param> void IBasicRepositoryConfigurator.Configure(IAppender appender) { BasicRepositoryConfigure(appender); } /// <summary> /// Initialize the log4net system using the specified appenders /// </summary> /// <param name="appenders">the appenders to use to log all logging events</param> void IBasicRepositoryConfigurator.Configure(params IAppender[] appenders) { BasicRepositoryConfigure(appenders); } /// <summary> /// Initialize the log4net system using the specified appenders /// </summary> /// <param name="appenders">the appenders to use to log all logging events</param> /// <remarks> /// <para> /// This method provides the same functionality as the /// <see cref="M:IBasicRepositoryConfigurator.Configure(IAppender)"/> method implemented /// on this object, but it is protected and therefore can be called by subclasses. /// </para> /// </remarks> protected void BasicRepositoryConfigure(params IAppender[] appenders) { ArrayList configurationMessages = new ArrayList(); using (new LogLog.LogReceivedAdapter(configurationMessages)) { foreach (IAppender appender in appenders) { Root.AddAppender(appender); } } Configured = true; ConfigurationMessages = configurationMessages; // Notify listeners OnConfigurationChanged(new ConfigurationChangedEventArgs(configurationMessages)); } #endregion Implementation of IBasicRepositoryConfigurator #region Implementation of IXmlRepositoryConfigurator /// <summary> /// Initialize the log4net system using the specified config /// </summary> /// <param name="element">the element containing the root of the config</param> void IXmlRepositoryConfigurator.Configure(System.Xml.XmlElement element) { XmlRepositoryConfigure(element); } /// <summary> /// Initialize the log4net system using the specified config /// </summary> /// <param name="element">the element containing the root of the config</param> /// <remarks> /// <para> /// This method provides the same functionality as the /// <see cref="M:IBasicRepositoryConfigurator.Configure(IAppender)"/> method implemented /// on this object, but it is protected and therefore can be called by subclasses. /// </para> /// </remarks> protected void XmlRepositoryConfigure(System.Xml.XmlElement element) { ArrayList configurationMessages = new ArrayList(); using (new LogLog.LogReceivedAdapter(configurationMessages)) { XmlHierarchyConfigurator config = new XmlHierarchyConfigurator(this); config.Configure(element); } Configured = true; ConfigurationMessages = configurationMessages; // Notify listeners OnConfigurationChanged(new ConfigurationChangedEventArgs(configurationMessages)); } #endregion Implementation of IXmlRepositoryConfigurator #region Public Instance Methods /// <summary> /// Test if this hierarchy is disabled for the specified <see cref="Level"/>. /// </summary> /// <param name="level">The level to check against.</param> /// <returns> /// <c>true</c> if the repository is disabled for the level argument, <c>false</c> otherwise. /// </returns> /// <remarks> /// <para> /// If this hierarchy has not been configured then this method will /// always return <c>true</c>. /// </para> /// <para> /// This method will return <c>true</c> if this repository is /// disabled for <c>level</c> object passed as parameter and /// <c>false</c> otherwise. /// </para> /// <para> /// See also the <see cref="ILoggerRepository.Threshold"/> property. /// </para> /// </remarks> public bool IsDisabled(Level level) { // Cast level to object for performance if ((object)level == null) { throw new ArgumentNullException("level"); } if (Configured) { return Threshold > level; } else { // If not configured the hierarchy is effectively disabled return true; } } /// <summary> /// Clear all logger definitions from the internal hashtable /// </summary> /// <remarks> /// <para> /// This call will clear all logger definitions from the internal /// hashtable. Invoking this method will irrevocably mess up the /// logger hierarchy. /// </para> /// <para> /// You should <b>really</b> know what you are doing before /// invoking this method. /// </para> /// </remarks> public void Clear() { lock(m_ht) { m_ht.Clear(); } } /// <summary> /// Return a new logger instance named as the first parameter using /// <paramref name="factory"/>. /// </summary> /// <param name="name">The name of the logger to retrieve</param> /// <param name="factory">The factory that will make the new logger instance</param> /// <returns>The logger object with the name specified</returns> /// <remarks> /// <para> /// If a logger of that name already exists, then it will be /// returned. Otherwise, a new logger will be instantiated by the /// <paramref name="factory"/> parameter and linked with its existing /// ancestors as well as children. /// </para> /// </remarks> public Logger GetLogger(string name, ILoggerFactory factory) { if (name == null) { throw new ArgumentNullException("name"); } if (factory == null) { throw new ArgumentNullException("factory"); } LoggerKey key = new LoggerKey(name); // Synchronize to prevent write conflicts. Read conflicts (in // GetEffectiveLevel() method) are possible only if variable // assignments are non-atomic. lock(m_ht) { Logger logger = null; Object node = m_ht[key]; if (node == null) { logger = factory.CreateLogger(this, name); logger.Hierarchy = this; m_ht[key] = logger; UpdateParents(logger); OnLoggerCreationEvent(logger); return logger; } Logger nodeLogger = node as Logger; if (nodeLogger != null) { return nodeLogger; } ProvisionNode nodeProvisionNode = node as ProvisionNode; if (nodeProvisionNode != null) { logger = factory.CreateLogger(this, name); logger.Hierarchy = this; m_ht[key] = logger; UpdateChildren(nodeProvisionNode, logger); UpdateParents(logger); OnLoggerCreationEvent(logger); return logger; } // It should be impossible to arrive here but let's keep the compiler happy. return null; } } #endregion Public Instance Methods #region Protected Instance Methods /// <summary> /// Sends a logger creation event to all registered listeners /// </summary> /// <param name="logger">The newly created logger</param> /// <remarks> /// Raises the logger creation event. /// </remarks> protected virtual void OnLoggerCreationEvent(Logger logger) { LoggerCreationEventHandler handler = m_loggerCreatedEvent; if (handler != null) { handler(this, new LoggerCreationEventArgs(logger)); } } #endregion Protected Instance Methods #region Private Instance Methods /// <summary> /// Updates all the parents of the specified logger /// </summary> /// <param name="log">The logger to update the parents for</param> /// <remarks> /// <para> /// This method loops through all the <i>potential</i> parents of /// <paramref name="log"/>. There 3 possible cases: /// </para> /// <list type="number"> /// <item> /// <term>No entry for the potential parent of <paramref name="log"/> exists</term> /// <description> /// We create a ProvisionNode for this potential /// parent and insert <paramref name="log"/> in that provision node. /// </description> /// </item> /// <item> /// <term>The entry is of type Logger for the potential parent.</term> /// <description> /// The entry is <paramref name="log"/>'s nearest existing parent. We /// update <paramref name="log"/>'s parent field with this entry. We also break from /// he loop because updating our parent's parent is our parent's /// responsibility. /// </description> /// </item> /// <item> /// <term>The entry is of type ProvisionNode for this potential parent.</term> /// <description> /// We add <paramref name="log"/> to the list of children for this /// potential parent. /// </description> /// </item> /// </list> /// </remarks> private void UpdateParents(Logger log) { string name = log.Name; int length = name.Length; bool parentFound = false; // if name = "w.x.y.z", loop through "w.x.y", "w.x" and "w", but not "w.x.y.z" for(int i = name.LastIndexOf('.', length-1); i >= 0; i = name.LastIndexOf('.', i-1)) { string substr = name.Substring(0, i); LoggerKey key = new LoggerKey(substr); // simple constructor Object node = m_ht[key]; // Create a provision node for a future parent. if (node == null) { ProvisionNode pn = new ProvisionNode(log); m_ht[key] = pn; } else { Logger nodeLogger = node as Logger; if (nodeLogger != null) { parentFound = true; log.Parent = nodeLogger; break; // no need to update the ancestors of the closest ancestor } else { ProvisionNode nodeProvisionNode = node as ProvisionNode; if (nodeProvisionNode != null) { nodeProvisionNode.Add(log); } else { LogLog.Error(declaringType, "Unexpected object type ["+node.GetType()+"] in ht.", new LogException()); } } } if (i == 0) { // logger name starts with a dot // and we've hit the start break; } } // If we could not find any existing parents, then link with root. if (!parentFound) { log.Parent = this.Root; } } /// <summary> /// Replace a <see cref="ProvisionNode"/> with a <see cref="Logger"/> in the hierarchy. /// </summary> /// <param name="pn"></param> /// <param name="log"></param> /// <remarks> /// <para> /// We update the links for all the children that placed themselves /// in the provision node 'pn'. The second argument 'log' is a /// reference for the newly created Logger, parent of all the /// children in 'pn'. /// </para> /// <para> /// We loop on all the children 'c' in 'pn'. /// </para> /// <para> /// If the child 'c' has been already linked to a child of /// 'log' then there is no need to update 'c'. /// </para> /// <para> /// Otherwise, we set log's parent field to c's parent and set /// c's parent field to log. /// </para> /// </remarks> private static void UpdateChildren(ProvisionNode pn, Logger log) { for(int i = 0; i < pn.Count; i++) { Logger childLogger = (Logger)pn[i]; // Unless this child already points to a correct (lower) parent, // make log.Parent point to childLogger.Parent and childLogger.Parent to log. if (!childLogger.Parent.Name.StartsWith(log.Name)) { log.Parent = childLogger.Parent; childLogger.Parent = log; } } } /// <summary> /// Define or redefine a Level using the values in the <see cref="LevelEntry"/> argument /// </summary> /// <param name="levelEntry">the level values</param> /// <remarks> /// <para> /// Define or redefine a Level using the values in the <see cref="LevelEntry"/> argument /// </para> /// <para> /// Supports setting levels via the configuration file. /// </para> /// </remarks> internal void AddLevel(LevelEntry levelEntry) { if (levelEntry == null) throw new ArgumentNullException("levelEntry"); if (levelEntry.Name == null) throw new ArgumentNullException("levelEntry.Name"); // Lookup replacement value if (levelEntry.Value == -1) { Level previousLevel = LevelMap[levelEntry.Name]; if (previousLevel == null) { throw new InvalidOperationException("Cannot redefine level ["+levelEntry.Name+"] because it is not defined in the LevelMap. To define the level supply the level value."); } levelEntry.Value = previousLevel.Value; } LevelMap.Add(levelEntry.Name, levelEntry.Value, levelEntry.DisplayName); } /// <summary> /// A class to hold the value, name and display name for a level /// </summary> /// <remarks> /// <para> /// A class to hold the value, name and display name for a level /// </para> /// </remarks> internal class LevelEntry { private int m_levelValue = -1; private string m_levelName = null; private string m_levelDisplayName = null; /// <summary> /// Value of the level /// </summary> /// <remarks> /// <para> /// If the value is not set (defaults to -1) the value will be looked /// up for the current level with the same name. /// </para> /// </remarks> public int Value { get { return m_levelValue; } set { m_levelValue = value; } } /// <summary> /// Name of the level /// </summary> /// <value> /// The name of the level /// </value> /// <remarks> /// <para> /// The name of the level. /// </para> /// </remarks> public string Name { get { return m_levelName; } set { m_levelName = value; } } /// <summary> /// Display name for the level /// </summary> /// <value> /// The display name of the level /// </value> /// <remarks> /// <para> /// The display name of the level. /// </para> /// </remarks> public string DisplayName { get { return m_levelDisplayName; } set { m_levelDisplayName = value; } } /// <summary> /// Override <c>Object.ToString</c> to return sensible debug info /// </summary> /// <returns>string info about this object</returns> public override string ToString() { return "LevelEntry(Value="+m_levelValue+", Name="+m_levelName+", DisplayName="+m_levelDisplayName+")"; } } /// <summary> /// Set a Property using the values in the <see cref="LevelEntry"/> argument /// </summary> /// <param name="propertyEntry">the property value</param> /// <remarks> /// <para> /// Set a Property using the values in the <see cref="LevelEntry"/> argument. /// </para> /// <para> /// Supports setting property values via the configuration file. /// </para> /// </remarks> internal void AddProperty(PropertyEntry propertyEntry) { if (propertyEntry == null) throw new ArgumentNullException("propertyEntry"); if (propertyEntry.Key == null) throw new ArgumentNullException("propertyEntry.Key"); Properties[propertyEntry.Key] = propertyEntry.Value; } #endregion Private Instance Methods #region Private Instance Fields private ILoggerFactory m_defaultFactory; private System.Collections.Hashtable m_ht; private Logger m_root; private bool m_emittedNoAppenderWarning = false; private event LoggerCreationEventHandler m_loggerCreatedEvent; #endregion Private Instance Fields #region Private Static Fields /// <summary> /// The fully qualified type of the Hierarchy class. /// </summary> /// <remarks> /// Used by the internal logger to record the Type of the /// log message. /// </remarks> private readonly static Type declaringType = typeof(Hierarchy); #endregion Private Static Fields } }