#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.Util; using log4net.Core; namespace log4net.Repository.Hierarchy { /// /// Implementation of used by /// /// /// /// Internal class used to provide implementation of /// interface. Applications should use to get /// logger instances. /// /// /// This is one of the central classes in the log4net implementation. One of the /// distinctive features of log4net are hierarchical loggers and their /// evaluation. The organizes the /// instances into a rooted tree hierarchy. /// /// /// The class is abstract. Only concrete subclasses of /// can be created. The /// is used to create instances of this type for the . /// /// /// Nicko Cadell /// Gert Driesen /// Aspi Havewala /// Douglas de la Torre public abstract class Logger : IAppenderAttachable, ILogger { #region Protected Instance Constructors /// /// This constructor created a new instance and /// sets its name. /// /// The name of the . /// /// /// This constructor is protected and designed to be used by /// a subclass that is not abstract. /// /// /// Loggers are constructed by /// objects. See for the default /// logger creator. /// /// protected Logger(string name) { #if NETCF // NETCF: String.Intern causes Native Exception m_name = name; #else m_name = string.Intern(name); #endif } #endregion Protected Instance Constructors #region Public Instance Properties /// /// Gets or sets the parent logger in the hierarchy. /// /// /// The parent logger in the hierarchy. /// /// /// /// Part of the Composite pattern that makes the hierarchy. /// The hierarchy is parent linked rather than child linked. /// /// virtual public Logger Parent { get { return m_parent; } set { m_parent = value; } } /// /// Gets or sets a value indicating if child loggers inherit their parent's appenders. /// /// /// true if child loggers inherit their parent's appenders. /// /// /// /// Additivity is set to true by default, that is children inherit /// the appenders of their ancestors by default. If this variable is /// set to false then the appenders found in the /// ancestors of this logger are not used. However, the children /// of this logger will inherit its appenders, unless the children /// have their additivity flag set to false too. See /// the user manual for more details. /// /// virtual public bool Additivity { get { return m_additive; } set { m_additive = value; } } /// /// Gets the effective level for this logger. /// /// The nearest level in the logger hierarchy. /// /// /// Starting from this logger, searches the logger hierarchy for a /// non-null level and returns it. Otherwise, returns the level of the /// root logger. /// /// The Logger class is designed so that this method executes as /// quickly as possible. /// virtual public Level EffectiveLevel { get { for(Logger c = this; c != null; c = c.m_parent) { Level level = c.m_level; // Casting level to Object for performance, otherwise the overloaded operator is called if ((object)level != null) { return level; } } return null; // If reached will cause an NullPointerException. } } /// /// Gets or sets the where this /// Logger instance is attached to. /// /// The hierarchy that this logger belongs to. /// /// /// This logger must be attached to a single . /// /// virtual public Hierarchy Hierarchy { get { return m_hierarchy; } set { m_hierarchy = value; } } /// /// Gets or sets the assigned , if any, for this Logger. /// /// /// The of this logger. /// /// /// /// The assigned can be null. /// /// virtual public Level Level { get { return m_level; } set { m_level = value; } } #endregion Public Instance Properties #region Implementation of IAppenderAttachable /// /// Add to the list of appenders of this /// Logger instance. /// /// An appender to add to this logger /// /// /// Add to the list of appenders of this /// Logger instance. /// /// /// If is already in the list of /// appenders, then it won't be added again. /// /// virtual public void AddAppender(IAppender newAppender) { if (newAppender == null) { throw new ArgumentNullException("newAppender"); } m_appenderLock.AcquireWriterLock(); try { if (m_appenderAttachedImpl == null) { m_appenderAttachedImpl = new log4net.Util.AppenderAttachedImpl(); } m_appenderAttachedImpl.AddAppender(newAppender); } finally { m_appenderLock.ReleaseWriterLock(); } } /// /// Get the appenders contained in this logger as an /// . /// /// A collection of the appenders in this logger /// /// /// Get the appenders contained in this logger as an /// . If no appenders /// can be found, then a is returned. /// /// virtual public AppenderCollection Appenders { get { m_appenderLock.AcquireReaderLock(); try { if (m_appenderAttachedImpl == null) { return AppenderCollection.EmptyCollection; } else { return m_appenderAttachedImpl.Appenders; } } finally { m_appenderLock.ReleaseReaderLock(); } } } /// /// Look for the appender named as name /// /// The name of the appender to lookup /// The appender with the name specified, or null. /// /// /// Returns the named appender, or null if the appender is not found. /// /// virtual public IAppender GetAppender(string name) { m_appenderLock.AcquireReaderLock(); try { if (m_appenderAttachedImpl == null || name == null) { return null; } return m_appenderAttachedImpl.GetAppender(name); } finally { m_appenderLock.ReleaseReaderLock(); } } /// /// Remove all previously added appenders from this Logger instance. /// /// /// /// Remove all previously added appenders from this Logger instance. /// /// /// This is useful when re-reading configuration information. /// /// virtual public void RemoveAllAppenders() { m_appenderLock.AcquireWriterLock(); try { if (m_appenderAttachedImpl != null) { m_appenderAttachedImpl.RemoveAllAppenders(); m_appenderAttachedImpl = null; } } finally { m_appenderLock.ReleaseWriterLock(); } } /// /// Remove the appender passed as parameter form the list of appenders. /// /// The appender to remove /// The appender removed from the list /// /// /// Remove the appender passed as parameter form the list of appenders. /// The appender removed is not closed. /// If you are discarding the appender you must call /// on the appender removed. /// /// virtual public IAppender RemoveAppender(IAppender appender) { m_appenderLock.AcquireWriterLock(); try { if (appender != null && m_appenderAttachedImpl != null) { return m_appenderAttachedImpl.RemoveAppender(appender); } } finally { m_appenderLock.ReleaseWriterLock(); } return null; } /// /// Remove the appender passed as parameter form the list of appenders. /// /// The name of the appender to remove /// The appender removed from the list /// /// /// Remove the named appender passed as parameter form the list of appenders. /// The appender removed is not closed. /// If you are discarding the appender you must call /// on the appender removed. /// /// virtual public IAppender RemoveAppender(string name) { m_appenderLock.AcquireWriterLock(); try { if (name != null && m_appenderAttachedImpl != null) { return m_appenderAttachedImpl.RemoveAppender(name); } } finally { m_appenderLock.ReleaseWriterLock(); } return null; } #endregion #region Implementation of ILogger /// /// Gets the logger name. /// /// /// The name of the logger. /// /// /// /// The name of this logger /// /// virtual public string Name { get { return m_name; } } /// /// This generic form is intended to be used by wrappers. /// /// The declaring type of the method that is /// the stack boundary into the logging system for this call. /// The level of the message to be logged. /// The message object to log. /// The exception to log, including its stack trace. /// /// /// Generate a logging event for the specified using /// the and . /// /// /// This method must not throw any exception to the caller. /// /// virtual public void Log(Type callerStackBoundaryDeclaringType, Level level, object message, Exception exception) { try { if (IsEnabledFor(level)) { ForcedLog((callerStackBoundaryDeclaringType != null) ? callerStackBoundaryDeclaringType : declaringType, level, message, exception); } } catch (Exception ex) { log4net.Util.LogLog.Error(declaringType, "Exception while logging", ex); } #if !NET_2_0 && !MONO_2_0 catch { log4net.Util.LogLog.Error(declaringType, "Exception while logging"); } #endif } /// /// This is the most generic printing method that is intended to be used /// by wrappers. /// /// The event being logged. /// /// /// Logs the specified logging event through this logger. /// /// /// This method must not throw any exception to the caller. /// /// virtual public void Log(LoggingEvent logEvent) { try { if (logEvent != null) { if (IsEnabledFor(logEvent.Level)) { ForcedLog(logEvent); } } } catch (Exception ex) { log4net.Util.LogLog.Error(declaringType, "Exception while logging", ex); } #if !NET_2_0 && !MONO_2_0 catch { log4net.Util.LogLog.Error(declaringType, "Exception while logging"); } #endif } /// /// Checks if this logger is enabled for a given passed as parameter. /// /// The level to check. /// /// true if this logger is enabled for level, otherwise false. /// /// /// /// Test if this logger is going to log events of the specified . /// /// /// This method must not throw any exception to the caller. /// /// virtual public bool IsEnabledFor(Level level) { try { if (level != null) { if (m_hierarchy.IsDisabled(level)) { return false; } return level >= this.EffectiveLevel; } } catch (Exception ex) { log4net.Util.LogLog.Error(declaringType, "Exception while logging", ex); } #if !NET_2_0 && !MONO_2_0 catch { log4net.Util.LogLog.Error(declaringType, "Exception while logging"); } #endif return false; } /// /// Gets the where this /// Logger instance is attached to. /// /// /// The that this logger belongs to. /// /// /// /// Gets the where this /// Logger instance is attached to. /// /// public ILoggerRepository Repository { get { return m_hierarchy; } } #endregion Implementation of ILogger /// /// Deliver the to the attached appenders. /// /// The event to log. /// /// /// Call the appenders in the hierarchy starting at /// this. If no appenders could be found, emit a /// warning. /// /// /// This method calls all the appenders inherited from the /// hierarchy circumventing any evaluation of whether to log or not /// to log the particular log request. /// /// virtual protected void CallAppenders(LoggingEvent loggingEvent) { if (loggingEvent == null) { throw new ArgumentNullException("loggingEvent"); } int writes = 0; for(Logger c=this; c != null; c=c.m_parent) { if (c.m_appenderAttachedImpl != null) { // Protected against simultaneous call to addAppender, removeAppender,... c.m_appenderLock.AcquireReaderLock(); try { if (c.m_appenderAttachedImpl != null) { writes += c.m_appenderAttachedImpl.AppendLoopOnAppenders(loggingEvent); } } finally { c.m_appenderLock.ReleaseReaderLock(); } } if (!c.m_additive) { break; } } // No appenders in hierarchy, warn user only once. // // Note that by including the AppDomain values for the currently running // thread, it becomes much easier to see which application the warning // is from, which is especially helpful in a multi-AppDomain environment // (like IIS with multiple VDIRS). Without this, it can be difficult // or impossible to determine which .config file is missing appender // definitions. // if (!m_hierarchy.EmittedNoAppenderWarning && writes == 0) { LogLog.Debug(declaringType, "No appenders could be found for logger [" + Name + "] repository [" + Repository.Name + "]"); LogLog.Debug(declaringType, "Please initialize the log4net system properly."); try { LogLog.Debug(declaringType, " Current AppDomain context information: "); LogLog.Debug(declaringType, " BaseDirectory : " + SystemInfo.ApplicationBaseDirectory); #if !NETCF LogLog.Debug(declaringType, " FriendlyName : " + AppDomain.CurrentDomain.FriendlyName); LogLog.Debug(declaringType, " DynamicDirectory: " + AppDomain.CurrentDomain.DynamicDirectory); #endif } catch(System.Security.SecurityException) { // Insufficient permissions to display info from the AppDomain } m_hierarchy.EmittedNoAppenderWarning = true; } } /// /// Closes all attached appenders implementing the interface. /// /// /// /// Used to ensure that the appenders are correctly shutdown. /// /// virtual public void CloseNestedAppenders() { m_appenderLock.AcquireWriterLock(); try { if (m_appenderAttachedImpl != null) { AppenderCollection appenders = m_appenderAttachedImpl.Appenders; foreach(IAppender appender in appenders) { if (appender is IAppenderAttachable) { appender.Close(); } } } } finally { m_appenderLock.ReleaseWriterLock(); } } /// /// This is the most generic printing method. This generic form is intended to be used by wrappers /// /// The level of the message to be logged. /// The message object to log. /// The exception to log, including its stack trace. /// /// /// Generate a logging event for the specified using /// the . /// /// virtual public void Log(Level level, object message, Exception exception) { if (IsEnabledFor(level)) { ForcedLog(declaringType, level, message, exception); } } /// /// Creates a new logging event and logs the event without further checks. /// /// The declaring type of the method that is /// the stack boundary into the logging system for this call. /// The level of the message to be logged. /// The message object to log. /// The exception to log, including its stack trace. /// /// /// Generates a logging event and delivers it to the attached /// appenders. /// /// virtual protected void ForcedLog(Type callerStackBoundaryDeclaringType, Level level, object message, Exception exception) { CallAppenders(new LoggingEvent(callerStackBoundaryDeclaringType, this.Hierarchy, this.Name, level, message, exception)); } /// /// Creates a new logging event and logs the event without further checks. /// /// The event being logged. /// /// /// Delivers the logging event to the attached appenders. /// /// virtual protected void ForcedLog(LoggingEvent logEvent) { // The logging event may not have been created by this logger // the Repository may not be correctly set on the event. This // is required for the appenders to correctly lookup renderers etc... logEvent.EnsureRepository(this.Hierarchy); CallAppenders(logEvent); } #region Private Static Fields /// /// The fully qualified type of the Logger class. /// private readonly static Type declaringType = typeof(Logger); #endregion Private Static Fields #region Private Instance Fields /// /// The name of this logger. /// private readonly string m_name; /// /// The assigned level of this logger. /// /// /// /// The level variable need not be /// assigned a value in which case it is inherited /// form the hierarchy. /// /// private Level m_level; /// /// The parent of this logger. /// /// /// /// The parent of this logger. /// All loggers have at least one ancestor which is the root logger. /// /// private Logger m_parent; /// /// Loggers need to know what Hierarchy they are in. /// /// /// /// Loggers need to know what Hierarchy they are in. /// The hierarchy that this logger is a member of is stored /// here. /// /// private Hierarchy m_hierarchy; /// /// Helper implementation of the interface /// private log4net.Util.AppenderAttachedImpl m_appenderAttachedImpl; /// /// Flag indicating if child loggers inherit their parents appenders /// /// /// /// Additivity is set to true by default, that is children inherit /// the appenders of their ancestors by default. If this variable is /// set to false then the appenders found in the /// ancestors of this logger are not used. However, the children /// of this logger will inherit its appenders, unless the children /// have their additivity flag set to false too. See /// the user manual for more details. /// /// private bool m_additive = true; /// /// Lock to protect AppenderAttachedImpl variable m_appenderAttachedImpl /// private readonly ReaderWriterLock m_appenderLock = new ReaderWriterLock(); #endregion } }