#region License
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed 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.Reflection;
using System.Runtime.Serialization;
using AopAlliance.Intercept;
using Common.Logging;
using Spring.Aop.Framework;
namespace Spring.Aspects.Logging{
/// <summary>
/// Abstract base class for logging advice
/// </summary>
/// <remarks>
///
/// </remarks>
/// <author>Mark Pollack</author>
[Serializable]
public abstract class AbstractLoggingAdvice : IMethodInterceptor, IDeserializationCallback
{
#region Fields
/// <summary>
/// The default <code>ILog</code> instance used to write logging messages.
/// </summary>
[NonSerialized]
protected ILog defaultLogger;
/// <summary>
/// The name of the logger instance to use for obtaining from <see cref="LogManager.GetLogger(string)"/>.
/// </summary>
private string defaultLoggerName;
/// <summary>
/// Indicates whether or not proxy type names should be hidden when using dynamic loggers.
/// </summary>
private bool hideProxyTypeNames = false;
#endregion
#region Constructor
/// <summary>
/// Creates a new advice instance using this advice type's name for logging by default.
/// </summary>
protected AbstractLoggingAdvice()
{
SetDefaultLogger(MethodBase.GetCurrentMethod().DeclaringType.FullName);
}
/// <summary>
/// Creates a new advice instance using the given logger by default.
/// </summary>
protected AbstractLoggingAdvice(ILog defaultLogger)
{
this.defaultLogger = defaultLogger;
}
#endregion
#region Properties
/// <summary>
/// Sets a value indicating whether to use a dynamic logger or static logger
/// </summary>
/// <remarks>Default is to use a static logger.
/// <para>
/// Used to determine which ILog instance should be used to write log messages for
/// a particular method invocation: a dynamic one for the Type getting called,
/// or a static one for the Type of the trace interceptor.
/// </para>
/// <para>
/// Specify either this property or LoggerName, not both.
/// </para>
/// </remarks>
/// <value><c>true</c> if use dynamic logger; otherwise, <c>false</c>.</value>
public bool UseDynamicLogger
{
set
{
SetDefaultLogger(value ? null : this.GetType().FullName);
}
}
/// <summary>
/// Sets the name of the logger to use.
/// </summary>
/// <remarks>
/// The name will be passed to the underlying logging implementation through Common.Logging,
/// getting interpreted as the log category according to the loggers configuration.
/// <para>
/// This can be specified to not log into the category of a Type (whether this
/// interceptor's class or the class getting called) but rather to a specific named category.
/// </para>
/// <para>
/// Specify either this property or UseDynamicLogger, but not both.
/// </para>
/// </remarks>
/// <value>The name of the logger.</value>
public string LoggerName
{
set
{
SetDefaultLogger(value);
}
}
/// <summary>
/// Sets a value indicating whether hide proxy type names (whenever possible)
/// when using dynamic loggers, i.e. property UseDynamicLogger is set to true.
/// </summary>
/// <value><c>true</c> if [hide proxy type names]; otherwise, <c>false</c>.</value>
public bool HideProxyTypeNames
{
set { hideProxyTypeNames = value; }
}
#endregion
#region Methods
/// <summary>
/// Adds logging to the method invocation.
/// </summary>
/// <remarks>
/// The method IsInterceptorEnabled is called
/// as an optimization to determine if logging should be applied. If logging should be
/// applied, the method invocation is passed to the InvokeUnderLog method for handling.
/// If not, the method proceeds as normal.
/// </remarks>
/// <param name="invocation">
/// The method invocation that is being intercepted.
/// </param>
/// <returns>
/// The result of the call to the
/// <see cref="AopAlliance.Intercept.IJoinpoint.Proceed"/> method of
/// the supplied <paramref name="invocation"/>; this return value may
/// well have been intercepted by the interceptor.
/// </returns>
/// <exception cref="System.Exception">
/// If any of the interceptors in the chain or the target object itself
/// throws an exception.
/// </exception>
public object Invoke(IMethodInvocation invocation)
{
object o = invocation.This;
ILog log = GetLoggerForInvocation(invocation);
if (IsInterceptorEnabled(invocation, log))
{
return InvokeUnderLog(invocation, log);
}
else
{
return invocation.Proceed();
}
}
/// <summary>
/// Determines whether the interceptor is enabled for the specified invocation, that
/// is, whether the method InvokeUnderLog is called.
/// </summary>
/// <remarks>The default behavior is to check whether the given ILog instance
/// is enabled by calling IsLogEnabled, whose default behavior is to check if
/// the TRACE level of logging is enabled. Subclasses</remarks>
/// <param name="invocation">The invocation.</param>
/// <param name="log">The log to write messages to</param>
/// <returns>
/// <c>true</c> if [is interceptor enabled] [the specified invocation]; otherwise, <c>false</c>.
/// </returns>
protected virtual bool IsInterceptorEnabled(IMethodInvocation invocation, ILog log)
{
return IsLogEnabled(log);
}
/// <summary>
/// Determines whether the given log is enabled.
/// </summary>
/// <remarks>
/// Default is true when the trace level is enabled. Subclasses may override this
/// to change the level at which logging occurs, or return true to ignore level
/// checks.</remarks>
/// <param name="log">The log instance to check.</param>
/// <returns>
/// <c>true</c> if log is for a given log level; otherwise, <c>false</c>.
/// </returns>
protected virtual bool IsLogEnabled(ILog log)
{
return log.IsTraceEnabled;
}
/// <summary>
/// Subclasses must override this method to perform any tracing around the supplied
/// IMethodInvocation.
/// </summary>
/// <remarks>
/// Subclasses are resonsible for ensuring that the IMethodInvocation actually executes
/// by calling IMethodInvocation.Proceed().
/// <para>
/// By default, the passed-in ILog instance will have log level
/// "trace" enabled. Subclasses do not have to check for this again, unless
/// they overwrite the IsInterceptorEnabled method to modify
/// the default behavior.
/// </para>
/// </remarks>
/// <param name="invocation">The method invocation to log</param>
/// <param name="log">The log to write messages to</param>
/// <returns>The result of the call to IMethodInvocation.Proceed()
/// </returns>
/// <exception cref="System.Exception">
/// If any of the interceptors in the chain or the target object itself
/// throws an exception.
/// </exception>
protected abstract object InvokeUnderLog(IMethodInvocation invocation, ILog log);
/// <summary>
/// Gets the appropriate log instance to use for the given IMethodInvocation.
/// </summary>
/// <remarks>
/// If the UseDynamicLogger property is set to true, the ILog instance will be
/// for the target class of the IMethodInvocation, otherwise the log will be the
/// default static logger.
/// </remarks>
/// <param name="invocation">The method invocation being logged.</param>
/// <returns>The ILog instance to use.</returns>
protected virtual ILog GetLoggerForInvocation(IMethodInvocation invocation)
{
if (defaultLogger != null)
{
return defaultLogger;
}
else
{
object target = invocation.This;
Type logCategoryType = target.GetType();
if (hideProxyTypeNames)
{
logCategoryType = AopUtils.GetTargetType(target);
}
return LogManager.GetLogger(logCategoryType);
}
}
#endregion
/// <summary>
/// Sets the default logger to the given name.
/// </summary>
/// <param name="name">if <c>null</c>, the default logger is removed.</param>
protected void SetDefaultLogger(string name)
{
defaultLogger = (name == null ? null : LogManager.GetLogger(name));
defaultLoggerName = name;
}
void IDeserializationCallback.OnDeserialization(object sender)
{
OnDeserialization(sender);
}
/// <summary>
/// Override in case you need to initialized non-serialized fields on deserialization.
/// </summary>
protected virtual void OnDeserialization(object sender)
{
SetDefaultLogger(this.defaultLoggerName);
}
}
}
|