in src/log4net/Repository/Hierarchy/Logger.cs [54:597]
public abstract class Logger(string name) : IAppenderAttachable, ILogger
{
/// <summary>
/// The fully qualified type of the Logger class.
/// </summary>
private static readonly Type _declaringType = typeof(Logger);
/// <summary>
/// The parent of this logger.
/// </summary>
/// <remarks>
/// <para>
/// All loggers have at least one ancestor which is the root logger.
/// </para>
/// </remarks>
private Logger? _parent;
/// <summary>
/// Loggers need to know what Hierarchy they are in.
/// </summary>
private Hierarchy? _hierarchy;
/// <summary>
/// Helper implementation of the <see cref="IAppenderAttachable"/> interface
/// </summary>
private AppenderAttachedImpl? _appenderAttachedImpl;
/// <summary>
/// Lock to protect AppenderAttachedImpl variable appenderAttachedImpl
/// </summary>
private readonly ReaderWriterLock _appenderLock = new();
/// <summary>
/// Gets or sets the parent logger in the hierarchy.
/// </summary>
/// <value>
/// The parent logger in the hierarchy.
/// </value>
/// <remarks>
/// <para>
/// Part of the Composite pattern that makes the hierarchy.
/// The hierarchy is parent linked rather than child linked.
/// </para>
/// </remarks>
public virtual Logger? Parent
{
get => _parent;
set => _parent = value;
}
/// <summary>
/// Gets or sets a value indicating if child loggers inherit their parent's appenders.
/// </summary>
/// <value>
/// <see langword="true"/> if child loggers inherit their parent's appenders.
/// </value>
/// <remarks>
/// <para>
/// Additivity is set to <see langword="true"/> by default, that is children inherit
/// the appenders of their ancestors by default. If this variable is
/// set to <see langword="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 <see langword="false"/> too. See
/// the user manual for more details.
/// </para>
/// </remarks>
public virtual bool Additivity { get; set; } = true;
/// <summary>
/// Gets the effective level for this logger.
/// </summary>
/// <returns>The nearest level in the logger hierarchy.</returns>
/// <remarks>
/// <para>
/// Starting from this logger, searches the logger hierarchy for a
/// non-null level and returns it. Otherwise, returns the level of the
/// root logger.
/// </para>
/// <para>The Logger class is designed so that this method executes as
/// quickly as possible.</para>
/// </remarks>
public virtual Level EffectiveLevel
{
get
{
for (Logger? c = this; c is not null; c = c._parent)
{
if (c.Level is Level level)
{
return level;
}
}
return null!; // If reached will cause an NullPointerException.
}
}
/// <summary>
/// Gets or sets the <see cref="Hierarchy"/> where this <see cref="Logger"/> instance is attached to.
/// </summary>
public virtual Hierarchy? Hierarchy
{
get => _hierarchy;
set => _hierarchy = value;
}
/// <summary>
/// Gets or sets the assigned <see cref="Level"/> for this Logger.
/// </summary>
public virtual Level? Level { get; set; }
/// <summary>
/// Add <paramref name="appender"/> to the list of appenders of this
/// Logger instance.
/// </summary>
/// <param name="appender">An appender to add to this logger</param>
/// <remarks>
/// <para>
/// If <paramref name="appender"/> is already in the list of
/// appenders, then it won't be added again.
/// </para>
/// </remarks>
public virtual void AddAppender(IAppender appender)
{
appender.EnsureNotNull();
_appenderLock.AcquireWriterLock();
try
{
_appenderAttachedImpl ??= new();
_appenderAttachedImpl.AddAppender(appender);
}
finally
{
_appenderLock.ReleaseWriterLock();
}
}
/// <summary>
/// Get the appenders contained in this logger as an
/// <see cref="System.Collections.ICollection"/>.
/// </summary>
/// <returns>
/// A collection of the appenders in this logger. If no appenders
/// can be found, then a <see cref="EmptyCollection"/> is returned.
/// </returns>
public virtual AppenderCollection Appenders
{
get
{
_appenderLock.AcquireReaderLock();
try
{
if (_appenderAttachedImpl is null)
{
return AppenderCollection.EmptyCollection;
}
return _appenderAttachedImpl.Appenders;
}
finally
{
_appenderLock.ReleaseReaderLock();
}
}
}
/// <summary>
/// Look for the appender named as <paramref name="name"/>
/// </summary>
/// <param name="name">The name of the appender to lookup</param>
/// <returns>The appender with the name specified, or <see langword="null"/>.</returns>
public virtual IAppender? GetAppender(string? name)
{
_appenderLock.AcquireReaderLock();
try
{
if (_appenderAttachedImpl is null || name is null)
{
return null;
}
return _appenderAttachedImpl.GetAppender(name);
}
finally
{
_appenderLock.ReleaseReaderLock();
}
}
/// <summary>
/// Removes all previously added appenders from this Logger instance.
/// </summary>
/// <remarks>
/// <para>
/// This is useful when re-reading configuration information.
/// </para>
/// </remarks>
public virtual void RemoveAllAppenders()
{
_appenderLock.AcquireWriterLock();
try
{
if (_appenderAttachedImpl is not null)
{
_appenderAttachedImpl.RemoveAllAppenders();
_appenderAttachedImpl = null;
}
}
finally
{
_appenderLock.ReleaseWriterLock();
}
}
/// <summary>
/// Remove the appender passed as parameter form the list of appenders.
/// </summary>
/// <param name="appender">The appender to remove</param>
/// <returns>The appender removed from the list</returns>
/// <remarks>
/// <para>
/// The appender removed is not closed.
/// If you are discarding the appender you must call
/// <see cref="IAppender.Close"/> on the appender removed.
/// </para>
/// </remarks>
public virtual IAppender? RemoveAppender(IAppender? appender)
{
_appenderLock.AcquireWriterLock();
try
{
if (appender is not null && _appenderAttachedImpl is not null)
{
return _appenderAttachedImpl.RemoveAppender(appender);
}
}
finally
{
_appenderLock.ReleaseWriterLock();
}
return null;
}
/// <summary>
/// Remove the appender passed as parameter form the list of appenders.
/// </summary>
/// <param name="name">The name of the appender to remove</param>
/// <returns>The appender removed from the list</returns>
/// <remarks>
/// <para>
/// The appender removed is not closed.
/// If you are discarding the appender you must call
/// <see cref="IAppender.Close"/> on the appender removed.
/// </para>
/// </remarks>
public virtual IAppender? RemoveAppender(string? name)
{
_appenderLock.AcquireWriterLock();
try
{
if (name is not null && _appenderAttachedImpl is not null)
{
return _appenderAttachedImpl.RemoveAppender(name);
}
}
finally
{
_appenderLock.ReleaseWriterLock();
}
return null;
}
/// <summary>
/// Gets the logger name.
/// </summary>
public virtual string Name { get; } = string.Intern(name);
/// <summary>
/// Generates a logging event for the specified <paramref name="level"/> using
/// the <paramref name="message"/> and <paramref name="exception"/>.
/// </summary>
/// <param name="callerStackBoundaryDeclaringType">The declaring type of the method that is
/// the stack boundary into the logging system for this call.</param>
/// <param name="level">The level of the message to be logged.</param>
/// <param name="message">The message object to log.</param>
/// <param name="exception">The exception to log, including its stack trace.</param>
/// <remarks>
/// <para>
/// This generic form is intended to be used by wrappers.
/// </para>
/// <para>
/// This method must not throw any exception to the caller.
/// </para>
/// </remarks>
public virtual void Log(Type? callerStackBoundaryDeclaringType, Level? level, object? message, Exception? exception)
{
try
{
if (IsEnabledFor(level))
{
ForcedLog(callerStackBoundaryDeclaringType ?? _declaringType, level, message, exception);
}
}
catch (Exception e) when (!e.IsFatal())
{
LogLog.Error(_declaringType, "Exception while logging", e);
}
}
/// <summary>
/// Logs the specified logging event through this logger.
/// </summary>
/// <param name="logEvent">The event being logged.</param>
/// <remarks>
/// <para>
/// This is the most generic printing method that is intended to be used
/// by wrappers.
/// </para>
/// <para>
/// This method must not throw any exception to the caller.
/// </para>
/// </remarks>
public virtual void Log(LoggingEvent? logEvent)
{
try
{
if (logEvent is not null)
{
if (IsEnabledFor(logEvent.Level))
{
ForcedLog(logEvent);
}
}
}
catch (Exception e) when (!e.IsFatal())
{
LogLog.Error(_declaringType, "Exception while logging", e);
}
}
/// <summary>
/// Checks if this logger is enabled for a given <see cref="Level"/> passed as parameter.
/// </summary>
/// <param name="level">The level to check.</param>
/// <returns>
/// <see langword="true"/> if this logger is enabled for <paramref name="level"/>,
/// otherwise <see langword="false"/>.
/// </returns>
/// <remarks>
/// <para>
/// This method must not throw any exception to the caller.
/// </para>
/// </remarks>
public virtual bool IsEnabledFor(Level? level)
{
try
{
if (level is not null)
{
if (_hierarchy is not null && _hierarchy.IsDisabled(level))
{
return false;
}
return level >= EffectiveLevel;
}
}
catch (Exception e) when (!e.IsFatal())
{
LogLog.Error(_declaringType, "Exception while logging", e);
}
return false;
}
/// <summary>
/// Gets the <see cref="ILoggerRepository"/> where this
/// <see cref="Logger"/> instance is attached to.
/// </summary>
public ILoggerRepository? Repository => _hierarchy;
/// <summary>
/// Deliver the <see cref="LoggingEvent"/> to the attached appenders.
/// </summary>
/// <param name="loggingEvent">The event to log.</param>
/// <remarks>
/// <para>
/// Call the appenders in the hierarchy starting at <see langword="this"/>.
/// If no appenders could be found, emit a warning.
/// </para>
/// <para>
/// 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.
/// </para>
/// </remarks>
protected virtual void CallAppenders(LoggingEvent loggingEvent)
{
loggingEvent.EnsureNotNull();
int writes = 0;
for (Logger? c = this; c is not null; c = c._parent)
{
if (c._appenderAttachedImpl is not null)
{
// Protected against simultaneous call to addAppender, removeAppender,...
c._appenderLock.AcquireReaderLock();
try
{
if (c._appenderAttachedImpl is not null)
{
writes += c._appenderAttachedImpl.AppendLoopOnAppenders(loggingEvent);
}
}
finally
{
c._appenderLock.ReleaseReaderLock();
}
}
if (!c.Additivity)
{
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 (_hierarchy is not null && !_hierarchy.EmittedNoAppenderWarning && writes == 0)
{
_hierarchy.EmittedNoAppenderWarning = true;
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);
LogLog.Debug(_declaringType, " FriendlyName : " + AppDomain.CurrentDomain.FriendlyName);
LogLog.Debug(_declaringType, " DynamicDirectory: " + AppDomain.CurrentDomain.DynamicDirectory);
}
catch (System.Security.SecurityException)
{
// Insufficient permissions to display info from the AppDomain
}
}
}
/// <summary>
/// Closes all attached appenders implementing the <see cref="IAppenderAttachable"/> interface.
/// </summary>
/// <remarks>
/// <para>
/// Used to ensure that the appenders are correctly shutdown.
/// </para>
/// </remarks>
public virtual void CloseNestedAppenders()
{
_appenderLock.AcquireWriterLock();
try
{
if (_appenderAttachedImpl is not null)
{
AppenderCollection appenders = _appenderAttachedImpl.Appenders;
foreach (IAppender appender in appenders)
{
if (appender is IAppenderAttachable)
{
appender.Close();
}
}
}
}
finally
{
_appenderLock.ReleaseWriterLock();
}
}
/// <summary>
/// This is the most generic printing method. This generic form is intended to be used by wrappers
/// </summary>
/// <param name="level">The level of the message to be logged.</param>
/// <param name="message">The message object to log.</param>
/// <param name="exception">The exception to log, including its stack trace.</param>
/// <remarks>
/// <para>
/// Generate a logging event for the specified <paramref name="level"/> using
/// the <paramref name="message"/>.
/// </para>
/// </remarks>
public virtual void Log(Level level, object? message, Exception? exception)
{
if (IsEnabledFor(level))
{
ForcedLog(_declaringType, level, message, exception);
}
}
/// <summary>
/// Creates a new logging event and logs the event without further checks.
/// </summary>
/// <param name="callerStackBoundaryDeclaringType">The declaring type of the method that is
/// the stack boundary into the logging system for this call.</param>
/// <param name="level">The level of the message to be logged.</param>
/// <param name="message">The message object to log.</param>
/// <param name="exception">The exception to log, including its stack trace.</param>
/// <remarks>
/// <para>
/// Generates a logging event and delivers it to the attached
/// appenders.
/// </para>
/// </remarks>
protected virtual void ForcedLog(Type callerStackBoundaryDeclaringType, Level? level,
object? message, Exception? exception)
{
CallAppenders(new LoggingEvent(callerStackBoundaryDeclaringType, Hierarchy, Name, level,
message, exception));
}
/// <summary>
/// Creates a new logging event and logs the event without further checks.
/// </summary>
/// <param name="logEvent">The event being logged.</param>
/// <remarks>
/// <para>
/// Delivers the logging event to the attached appenders.
/// </para>
/// </remarks>
protected virtual 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.EnsureNotNull().EnsureRepository(Hierarchy);
CallAppenders(logEvent);
}
}