SPRNET-1057 - Add new 'execute' action for ExceptionTranslationAdvice

SPRNET-1056 - Provide easier configuration of exception handlers in ExceptionTranslationAdvice.
This commit is contained in:
markpollack
2008-10-13 21:34:01 +00:00
parent b444429cf8
commit 60b85ad33c
10 changed files with 719 additions and 44 deletions

View File

@@ -0,0 +1,210 @@
using System;
using System.Collections;
using System.Globalization;
using System.Text;
using Common.Logging;
namespace Spring.Aspects.Exceptions
{
public class CaptureOutputLogger : ILog
{
private LogLevel _currentLogLevel = LogLevel.All;
private IList logMessages = new ArrayList();
public IList LogMessages
{
get { return logMessages; }
set { logMessages = value; }
}
/// <summary>
/// Do the actual logging by constructing the log message using a <see cref="StringBuilder" /> then
/// sending the output to <see cref="Console.Out" />.
/// </summary>
/// <param name="level">The <see cref="LogLevel" /> of the message.</param>
/// <param name="message">The log message.</param>
/// <param name="e">An optional <see cref="Exception" /> associated with the message.</param>
private void Write(LogLevel level, object message, Exception e)
{
// Use a StringBuilder for better performance
StringBuilder sb = new StringBuilder();
// Append date-time if so configured
// Append a readable representation of the log level
sb.Append(("[" + level.ToString().ToUpper() + "]").PadRight(8));
// Append the message
sb.Append(message);
// Append stack trace if not null
if (e != null)
{
sb.Append(Environment.NewLine).Append(e.ToString());
}
// Print to the appropriate destination
logMessages.Add(sb.ToString());
}
/// <summary>
/// Determines if the given log level is currently enabled.
/// </summary>
/// <param name="level"></param>
/// <returns></returns>
private bool IsLevelEnabled(LogLevel level)
{
int iLevel = (int)level;
int iCurrentLogLevel = (int)_currentLogLevel;
// return iLevel.CompareTo(iCurrentLogLevel); better ???
return (iLevel >= iCurrentLogLevel);
}
#region ILog Members
public void Trace(object message)
{
Trace(message, null);
}
public void Trace(object message, Exception e)
{
if (IsLevelEnabled(LogLevel.Trace))
{
Write(LogLevel.Trace, message, e);
}
}
public void Debug(object message)
{
Debug(message, null);
}
public void Debug(object message, Exception e)
{
if (IsLevelEnabled(LogLevel.Debug))
{
Write(LogLevel.Debug, message, e);
}
}
public void Error(object message)
{
Error(message, null);
}
public void Error(object message, Exception e)
{
if (IsLevelEnabled(LogLevel.Error))
{
Write(LogLevel.Error, message, e);
}
}
public void Fatal(object message)
{
Fatal(message, null);
}
public void Fatal(object message, Exception e)
{
if (IsLevelEnabled(LogLevel.Fatal))
{
Write(LogLevel.Fatal, message, e);
}
}
public void Info(object message)
{
Info(message, null);
}
public void Info(object message, Exception e)
{
if (IsLevelEnabled(LogLevel.Info))
{
Write(LogLevel.Info, message, e);
}
}
public void Warn(object message)
{
Warn(message, null);
}
public void Warn(object message, Exception e)
{
if (IsLevelEnabled(LogLevel.Warn))
{
Write(LogLevel.Warn, message, e);
}
}
/// <summary>
/// Returns <see langword="true" /> if the current <see cref="LogLevel" /> is greater than or
/// equal to <see cref="LogLevel.Trace" />. If it is, all messages will be sent to <see cref="Console.Out" />.
/// </summary>
public bool IsTraceEnabled
{
get { return IsLevelEnabled(LogLevel.Trace); }
}
/// <summary>
/// Returns <see langword="true" /> if the current <see cref="LogLevel" /> is greater than or
/// equal to <see cref="LogLevel.Debug" />. If it is, all messages will be sent to <see cref="Console.Out" />.
/// </summary>
public bool IsDebugEnabled
{
get { return IsLevelEnabled(LogLevel.Debug); }
}
/// <summary>
/// Returns <see langword="true" /> if the current <see cref="LogLevel" /> is greater than or
/// equal to <see cref="LogLevel.Error" />. If it is, only messages with a <see cref="LogLevel" /> of
/// <see cref="LogLevel.Error" /> and <see cref="LogLevel.Fatal" /> will be sent to <see cref="Console.Out" />.
/// </summary>
public bool IsErrorEnabled
{
get { return IsLevelEnabled(LogLevel.Error); }
}
/// <summary>
/// Returns <see langword="true" /> if the current <see cref="LogLevel" /> is greater than or
/// equal to <see cref="LogLevel.Fatal" />. If it is, only messages with a <see cref="LogLevel" /> of
/// <see cref="LogLevel.Fatal" /> will be sent to <see cref="Console.Out" />.
/// </summary>
public bool IsFatalEnabled
{
get { return IsLevelEnabled(LogLevel.Fatal); }
}
/// <summary>
/// Returns <see langword="true" /> if the current <see cref="LogLevel" /> is greater than or
/// equal to <see cref="LogLevel.Info" />. If it is, only messages with a <see cref="LogLevel" /> of
/// <see cref="LogLevel.Info" />, <see cref="LogLevel.Warn" />, <see cref="LogLevel.Error" />, and
/// <see cref="LogLevel.Fatal" /> will be sent to <see cref="Console.Out" />.
/// </summary>
public bool IsInfoEnabled
{
get { return IsLevelEnabled(LogLevel.Info); }
}
/// <summary>
/// Returns <see langword="true" /> if the current <see cref="LogLevel" /> is greater than or
/// equal to <see cref="LogLevel.Warn" />. If it is, only messages with a <see cref="LogLevel" /> of
/// <see cref="LogLevel.Warn" />, <see cref="LogLevel.Error" />, and <see cref="LogLevel.Fatal" />
/// will be sent to <see cref="Console.Out" />.
/// </summary>
public bool IsWarnEnabled
{
get { return IsLevelEnabled(LogLevel.Warn); }
}
#endregion
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Specialized;
using Common.Logging;
namespace Spring.Aspects.Exceptions
{
public class CaptureOutputLoggerFactoryAdapter : ILoggerFactoryAdapter
{
private CaptureOutputLogger adviceLogger;
public CaptureOutputLoggerFactoryAdapter()
{
}
public CaptureOutputLoggerFactoryAdapter(NameValueCollection properties)
{
}
public CaptureOutputLogger AdviceLogger
{
get { return adviceLogger; }
}
#region ILoggerFactoryAdapter Members
public ILog GetLogger(Type type)
{
return GetLogger(type.FullName);
}
public ILog GetLogger(string name)
{
CaptureOutputLogger logger = new CaptureOutputLogger();
if (name.Equals("adviceHandler") || name.IndexOf("LogExceptionHandler") >= 0)
{
adviceLogger = logger;
}
return logger;
}
#endregion
}
}

View File

@@ -30,6 +30,7 @@ using Spring.Aop.Framework;
using Spring.Aspects.Exceptions;
using Spring.Expressions;
using Spring.Objects;
using Spring.Util;
#endregion
@@ -43,24 +44,64 @@ namespace Spring.Aspects.Exceptions
public class ExceptionHandlerAspectIntegrationTests
{
private ExceptionHandlerAdvice exceptionHandlerAdvice;
private CaptureOutputLoggerFactoryAdapter loggerFactoryAdapter;
private ILoggerFactoryAdapter originalAdapter;
private static bool spelActionExecuted = false;
[SetUp]
public void Setup()
{
LogManager.Adapter = new ConsoleOutLoggerFactoryAdapter(new NameValueCollection());
exceptionHandlerAdvice = new ExceptionHandlerAdvice();
originalAdapter = LogManager.Adapter;
loggerFactoryAdapter = new CaptureOutputLoggerFactoryAdapter();
LogManager.Adapter = loggerFactoryAdapter;
exceptionHandlerAdvice = new ExceptionHandlerAdvice();
}
public void TearDown()
{
loggerFactoryAdapter.AdviceLogger.LogMessages.Clear();
//reset so other tests can produce some output if needed.
LogManager.Adapter = originalAdapter;
}
[Test]
public void ExecuteSpelAction()
{
string executeHandlerText =
"on exception name ArithmeticException execute Spring.Aspects.Exceptions.ExceptionHandlerAspectIntegrationTests.Executed(true)";
ITestObject to = CreateTestObjectProxy(executeHandlerText);
try
{
to.Exceptional(new ArithmeticException());
}
catch (ArithmeticException)
{
Assert.IsTrue(spelActionExecuted);
}
}
public static void Executed(bool val)
{
spelActionExecuted = val;
}
[Test]
public void LoggingTest()
{
CaptureOutputLoggerFactoryAdapter loggerFactoryAdapter = new CaptureOutputLoggerFactoryAdapter();
LogManager.Adapter = loggerFactoryAdapter;
LogExceptionHandler logHandler = new LogExceptionHandler();
string testText = @"#log.Debug('Hello World, exception message = ' + #e.Message + ', target method = ' + #method.Name)";
logHandler.LogName = "adviceHandler";
string testText = @"'Hello World, exception message = ' + #e.Message + ', target method = ' + #method.Name";
logHandler.SourceExceptionNames.Add("ArithmeticException");
logHandler.ActionExpressionText = testText;
exceptionHandlerAdvice.ExceptionHandlers.Add(logHandler);
exceptionHandlerAdvice.AfterPropertiesSet();
ProxyFactory pf = new ProxyFactory(new TestObject());
pf.AddAdvice(exceptionHandlerAdvice);
ITestObject to = (ITestObject) pf.GetProxy();
@@ -71,7 +112,17 @@ namespace Spring.Aspects.Exceptions
Assert.Fail("Should have thrown exception when only logging");
} catch (ArithmeticException)
{
//TODO need to create adapter implementation to replay logged text.
bool found = false;
foreach (string message in loggerFactoryAdapter.AdviceLogger.LogMessages)
{
if (message.IndexOf("Hello World") >= 0)
{
found = true;
}
}
Assert.IsTrue(found, "did not find logging output");
}
}
@@ -81,7 +132,15 @@ namespace Spring.Aspects.Exceptions
{
string logHandlerText = "on exception name ArithmeticException log 'My Message, Method Name ' + #method.Name";
ExecuteLoggingHandler(logHandlerText);
ExecuteLoggingHandler(logHandlerText, "My Message");
}
[Test]
public void LoggingTestWithStringExplicitHandler()
{
string logHandlerText = "on exception name ArithmeticException log 'My Message, Method Name ' + #method.Name";
ExecuteLoggingHandler(logHandlerText, "My Message");
}
[Test]
@@ -89,18 +148,26 @@ namespace Spring.Aspects.Exceptions
{
string logHandlerText = "on exception (#e is T(System.ArithmeticException)) log 'My Message, Method Name ' + #method.Name";
ExecuteLoggingHandler(logHandlerText);
ExecuteLoggingHandler(logHandlerText, "My Message");
}
[Test]
public void LoggingTestWithConstraintExpressionWithExceptionHandler()
public void LoggingTestWithConstraintExpressionWithExceptionHandlerInList()
{
LogExceptionHandler exHandler = new LogExceptionHandler();
exHandler.ConstraintExpressionText = "#e is T(System.ArithmeticException)";
exHandler.LogName = "Cms.Session.ExceptionHandler";
exHandler.LogName = "adviceHandler";
exHandler.ActionExpressionText = "#log.Fatal('Request Timeout occured', #e)";
ExecuteLoggingHandler(exHandler);
ExecuteLoggingHandlerInList(exHandler, "Request Timeout");
}
[Test]
public void LoggingTestWithConstraintExpressionWithKeyedExceptionHandler()
{
LogExceptionHandler exHandler = new LogExceptionHandler();
ExecuteLoggingHandlerWithKeyedLogHandler(exHandler,
@"on exception (#e is T(System.ArithmeticException)) log 'Request Timeout occured'", "Request Timeout");
}
[Test]
@@ -109,7 +176,7 @@ namespace Spring.Aspects.Exceptions
{
string logHandlerText = "on foobar name ArithmeticException log 'My Message, Method Name ' + #method.Name";
ExecuteLoggingHandler(logHandlerText);
ExecuteLoggingHandler(logHandlerText, "My Message");
}
[Test]
@@ -117,11 +184,8 @@ namespace Spring.Aspects.Exceptions
{
string logHandlerText = "on exception (#e is System.FooBar) log 'My Message, Method Name ' + #method.Name";
ExecuteLoggingHandler(logHandlerText);
//No exception is expected.
ExecuteLoggingHandler(logHandlerText, "[WARN] Was not able to evaluate constraint expression [#e is System.FooBar]");
//TODO need to make sure log statement was executed.
}
[Test]
@@ -129,14 +193,11 @@ namespace Spring.Aspects.Exceptions
{
string logHandlerText = "on exception (1+1) log 'My Message, Method Name ' + #method.Name";
ExecuteLoggingHandler(logHandlerText);
ExecuteLoggingHandler(logHandlerText, "[WARN] Was not able to unbox constraint expression to boolean [1+1]");
//No exception is expected.
//TODO need to make sure log statement was executed.
}
private void ExecuteLoggingHandler(string logHandlerText)
private void ExecuteLoggingHandler(string logHandlerText, string searchString)
{
ITestObject to = CreateTestObjectProxy(logHandlerText);
@@ -146,13 +207,13 @@ namespace Spring.Aspects.Exceptions
}
catch (ArithmeticException)
{
//TODO assert logging occured.
AssertSearchString(searchString);
}
}
private void ExecuteLoggingHandler(IExceptionHandler handler)
private void ExecuteLoggingHandlerInList(IExceptionHandler handler, string searchString)
{
ITestObject to = CreateTestObjectProxy(handler);
ITestObject to = CreateTestObjectProxyInList(handler);
try
{
@@ -160,11 +221,23 @@ namespace Spring.Aspects.Exceptions
}
catch (ArithmeticException)
{
//TODO assert logging occured.
AssertSearchString(searchString);
}
}
private void ExecuteLoggingHandlerWithKeyedLogHandler(IExceptionHandler handler, string handlerText, string searchString)
{
ITestObject to = CreateTestObjectProxyWithKeyedHandler(handler, handlerText);
try
{
to.Exceptional(new ArithmeticException());
}
catch (ArithmeticException)
{
AssertSearchString(searchString);
}
}
[Test]
public void TranslationWithString()
@@ -390,6 +463,19 @@ namespace Spring.Aspects.Exceptions
return CreateProxy();
}
private ITestObject CreateTestObjectProxyInList(IExceptionHandler exceptionHander)
{
exceptionHandlerAdvice.ExceptionHandlers.Add(exceptionHander);
return CreateProxy();
}
private ITestObject CreateTestObjectProxyWithKeyedHandler(IExceptionHandler exceptionHander, string handlerText)
{
exceptionHandlerAdvice.ExceptionHandlerDictionary.Add("log", exceptionHander);
exceptionHandlerAdvice.ExceptionHandlers.Add(handlerText);
return CreateProxy();
}
private ITestObject CreateProxy()
{
exceptionHandlerAdvice.AfterPropertiesSet();
@@ -397,6 +483,19 @@ namespace Spring.Aspects.Exceptions
pf.AddAdvice(exceptionHandlerAdvice);
return (ITestObject)pf.GetProxy();
}
private void AssertSearchString(string searchString)
{
bool found = false;
foreach (string message in loggerFactoryAdapter.AdviceLogger.LogMessages)
{
if (message.IndexOf(searchString) >= 0)
{
found = true;
}
}
Assert.IsTrue(found, "did not find logging output [" + searchString + "] Logging values = "
+ StringUtils.CollectionToCommaDelimitedString(loggerFactoryAdapter.AdviceLogger.LogMessages));
}
}
}

View File

@@ -251,6 +251,8 @@
<Compile Include="Aspects\Cache\CacheParameterAdviceTests.cs" />
<Compile Include="Aspects\Cache\CacheAspectIntegrationTests.cs" />
<Compile Include="Aspects\Cache\InvalidateCacheAdviceTests.cs" />
<Compile Include="Aspects\Exception\CaptureOutputLogger.cs" />
<Compile Include="Aspects\Exception\CaptureOutputLoggerFactoryAdapter.cs" />
<Compile Include="Aspects\Exception\ExceptionHandlerAspectIntegrationTests.cs" />
<Compile Include="Aspects\Logging\SimpleLoggingAdviceTests.cs" />
<Compile Include="Aspects\Logging\TestableSimpleLoggingAdvice.cs" />

View File

@@ -23,8 +23,15 @@ limitations under the License.
<common>
<logging>
<!--
<factoryAdapter type="Common.Logging.Simple.ConsoleOutLoggerFactoryAdapter, Common.Logging">
<arg key="level" value="DEBUG" />
</factoryAdapter>
-->
<factoryAdapter type="Common.Logging.Simple.NoOpLoggerFactoryAdapter, Common.Logging">
</factoryAdapter>
</factoryAdapter>
</logging>
</common>
</configuration>