SPRNET-1057 - Add new 'execute' action for ExceptionTranslationAdvice
SPRNET-1056 - Provide easier configuration of exception handlers in ExceptionTranslationAdvice.
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user