Skip to content

Commit da1bd2f

Browse files
author
TheTribe
committed
Merge pull request #99 from jbatte47/feature/issue-96
Issue #96 - code complete
2 parents 8edc9de + 69d4e91 commit da1bd2f

File tree

5 files changed

+166
-23
lines changed

5 files changed

+166
-23
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#region FreeBSD
2+
3+
// Copyright (c) 2013, John Batte
4+
// All rights reserved.
5+
//
6+
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
7+
//
8+
// * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
9+
//
10+
// * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
11+
// documentation and/or other materials provided with the distribution.
12+
//
13+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
14+
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
15+
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
16+
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
17+
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
18+
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19+
20+
#endregion
21+
22+
using System;
23+
24+
using Castle.DynamicProxy;
25+
26+
using Patterns.ExceptionHandling;
27+
28+
namespace Patterns.Interception
29+
{
30+
/// <summary>
31+
/// Provides an interceptor that allows its interception logic to be
32+
/// injected; no-op fall-backs are used when no logic has been specified
33+
/// for an interception step.
34+
/// </summary>
35+
public class DelegateInterceptor : IInterceptor
36+
{
37+
/// <summary>
38+
/// Initializes a new instance of the <see cref="DelegateInterceptor" /> class.
39+
/// </summary>
40+
/// <param name="after">
41+
/// The action to execute after proceeding. This action will not run
42+
/// if the invocation throws an exception.
43+
/// </param>
44+
/// <param name="before">The action to execute before proceeding.</param>
45+
/// <param name="condition">
46+
/// The interception condition. If this condition returns <c>false</c>,
47+
/// the invocation proceeds with no further interception.
48+
/// </param>
49+
/// <param name="finally">
50+
/// The action to execute at the end of the interception, regardless of
51+
/// whether or not an exception was thrown.
52+
/// </param>
53+
/// <param name="onError">The function to execute when an error occurs.</param>
54+
public DelegateInterceptor(Action<IInvocation> after = null, Action<IInvocation> before = null,
55+
Func<IInvocation, bool> condition = null, Action<IInvocation> @finally = null,
56+
Func<IInvocation, Exception, ExceptionState> onError = null)
57+
{
58+
Initialize(after, before, condition, @finally, onError);
59+
}
60+
61+
protected virtual Action<IInvocation> After { get; set; }
62+
63+
protected virtual Action<IInvocation> Before { get; set; }
64+
65+
protected virtual Func<IInvocation, bool> Condition { get; set; }
66+
67+
protected virtual Action<IInvocation> Finally { get; set; }
68+
69+
protected virtual Func<IInvocation, Exception, ExceptionState> OnError { get; set; }
70+
71+
/// <summary>
72+
/// Intercepts the specified invocation.
73+
/// </summary>
74+
/// <param name="invocation">The invocation.</param>
75+
public virtual void Intercept(IInvocation invocation)
76+
{
77+
if (Condition != null && !Condition(invocation))
78+
{
79+
invocation.Proceed();
80+
return;
81+
}
82+
83+
try
84+
{
85+
if (Before != null) Before(invocation);
86+
87+
invocation.Proceed();
88+
89+
if (After != null) After(invocation);
90+
}
91+
catch (Exception error)
92+
{
93+
Func<IInvocation, Exception, ExceptionState> errorHandler = OnError
94+
?? ((thisCall, thisBug) => new ExceptionState(thisBug, false));
95+
ExceptionState state = errorHandler(invocation, error);
96+
97+
if (state.IsHandled) return;
98+
99+
if (!ReferenceEquals(error, state.Exception) && state.Exception != null) throw state.Exception;
100+
101+
throw;
102+
}
103+
finally
104+
{
105+
if (Finally != null) Finally(invocation);
106+
}
107+
}
108+
109+
private void Initialize(Action<IInvocation> after, Action<IInvocation> before, Func<IInvocation, bool> condition,
110+
Action<IInvocation> @finally, Func<IInvocation, Exception, ExceptionState> onError)
111+
{
112+
Condition = condition;
113+
Before = before;
114+
After = after;
115+
Finally = @finally;
116+
OnError = onError;
117+
}
118+
}
119+
}

src/Patterns/Logging/LoggingInterceptor.cs

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
using Common.Logging;
2929

3030
using Patterns.Collections.Strategies;
31+
using Patterns.ExceptionHandling;
32+
using Patterns.Interception;
3133

3234
namespace Patterns.Logging
3335
{
@@ -36,7 +38,7 @@ namespace Patterns.Logging
3638
/// uses <see cref="ILog" /> to log Trace, Debug, Info, and Error
3739
/// events throughout the execution of intercepted invocations.
3840
/// </summary>
39-
public class LoggingInterceptor : IInterceptor
41+
public class LoggingInterceptor : DelegateInterceptor
4042
{
4143
private const string _nullArgument = "[NULL]";
4244
private const string _argumentListFormat = "({0})";
@@ -53,54 +55,74 @@ private static readonly FuncStrategies<Type, object, object> _displayStrategies
5355

5456
private static readonly JavaScriptSerializer _jsonSerializer = new JavaScriptSerializer();
5557

58+
/// <summary>
59+
/// Initializes a new instance of the <see cref="LoggingInterceptor"/> class.
60+
/// </summary>
61+
/// <param name="config">The config.</param>
62+
/// <param name="logFactory">The log factory.</param>
63+
public LoggingInterceptor(ILoggingConfig config, Func<Type, ILog> logFactory) : this(config, logFactory, null) {}
64+
5665
/// <summary>
5766
/// Initializes a new instance of the <see cref="LoggingInterceptor" /> class.
5867
/// </summary>
5968
/// <param name="config">The config.</param>
6069
/// <param name="logFactory">The log factory.</param>
61-
public LoggingInterceptor(ILoggingConfig config, Func<Type, ILog> logFactory)
70+
/// <param name="condition">The intercept condition.</param>
71+
public LoggingInterceptor(ILoggingConfig config, Func<Type, ILog> logFactory, Func<IInvocation, bool> condition)
6272
{
63-
_config = config;
6473
_logFactory = logFactory;
74+
_config = config;
75+
Initialize(condition);
6576
}
6677

67-
/// <summary>
68-
/// Intercepts the specified invocation.
69-
/// </summary>
70-
/// <param name="invocation">The invocation.</param>
71-
public void Intercept(IInvocation invocation)
78+
private void Initialize(Func<IInvocation, bool> condition)
79+
{
80+
Condition = condition;
81+
Before = LogBefore;
82+
After = LogAfter;
83+
OnError = LogError;
84+
Finally = LogFinally;
85+
}
86+
87+
protected virtual void LogBefore(IInvocation invocation)
7288
{
7389
ILog log = _logFactory(invocation.TargetType);
7490
log.Trace(handler => handler(LoggingResources.MethodStartTraceFormat, invocation.TargetType, invocation.Method.Name));
7591
log.Debug(handler => handler(LoggingResources.MethodArgsDebugFormat, invocation.Method.Name, GetMethodArguments(invocation)));
92+
}
7693

77-
try
78-
{
79-
invocation.Proceed();
80-
log.Info(handler => handler(LoggingResources.MethodInfoFormat, invocation.Method.Name, LoggingResources.MethodInfoPass));
81-
}
82-
catch (Exception error)
83-
{
84-
log.Info(handler => handler(LoggingResources.MethodInfoFormat, invocation.Method.Name, LoggingResources.MethodInfoFail));
85-
log.Error(handler => handler(LoggingResources.ExceptionErrorFormat, invocation.Method.Name, error.ToFullString()));
86-
if (!_config.TrapExceptions) throw;
87-
}
88-
94+
protected virtual void LogAfter(IInvocation invocation)
95+
{
96+
ILog log = _logFactory(invocation.TargetType);
97+
log.Info(handler => handler(LoggingResources.MethodInfoFormat, invocation.Method.Name, LoggingResources.MethodInfoPass));
8998
log.Trace(handler => handler(LoggingResources.MethodStopTraceFormat, invocation.TargetType, invocation.Method.Name));
99+
}
90100

101+
protected virtual ExceptionState LogError(IInvocation invocation, Exception error)
102+
{
103+
ILog log = _logFactory(invocation.TargetType);
104+
log.Info(handler => handler(LoggingResources.MethodInfoFormat, invocation.Method.Name, LoggingResources.MethodInfoFail));
105+
log.Error(handler => handler(LoggingResources.ExceptionErrorFormat, invocation.Method.Name, error.ToFullString()));
106+
return new ExceptionState(error, _config.TrapExceptions);
107+
}
108+
109+
protected virtual void LogFinally(IInvocation invocation)
110+
{
91111
if (invocation.ReturnValue == null) return;
92112

113+
ILog log = _logFactory(invocation.TargetType);
114+
93115
object value = ConvertValueForDisplay(invocation.ReturnValue);
94116
log.Debug(handler => handler(LoggingResources.MethodReturnDebugFormat, invocation.Method.Name, value));
95117
}
96118

97-
private static string GetMethodArguments(IInvocation invocation)
119+
protected static string GetMethodArguments(IInvocation invocation)
98120
{
99121
object[] arguments = invocation.Arguments.Select(ConvertValueForDisplay).ToArray();
100122
return string.Format(_argumentListFormat, string.Join(_argumentListSeparator, arguments));
101123
}
102124

103-
private static object ConvertValueForDisplay(object value)
125+
protected static object ConvertValueForDisplay(object value)
104126
{
105127
if (value == null) return _nullArgument;
106128

src/Patterns/Patterns.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
<Compile Include="Configuration\IConfigurationSource.cs" />
7272
<Compile Include="ExceptionHandling\ExceptionState.cs" />
7373
<Compile Include="ExceptionHandling\Try.cs" />
74+
<Compile Include="Interception\DelegateInterceptor.cs" />
7475
<Compile Include="Logging\ILoggingConfig.cs" />
7576
<Compile Include="Logging\LoggingConfig.cs" />
7677
<Compile Include="Logging\LoggingInterceptor.cs" />

src/_specs/Steps/Logging/MockVerificationSteps.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public void VerifyLogBrokenExecution()
7777
public void VerifyLogTrappedErrorExecution()
7878
{
7979
Mock<ILog> mockLog = _moq.Container.Mock<ILog>();
80-
mockLog.Verify(log => log.Trace(It.IsAny<Action<FormatMessageHandler>>()), Times.Exactly(2));
80+
mockLog.Verify(log => log.Trace(It.IsAny<Action<FormatMessageHandler>>()), Times.Exactly(1));
8181
mockLog.Verify(log => log.Debug(It.IsAny<Action<FormatMessageHandler>>()), Times.Exactly(1));
8282
mockLog.Verify(log => log.Info(It.IsAny<Action<FormatMessageHandler>>()), Times.Exactly(1));
8383
mockLog.Verify(log => log.Error(It.IsAny<Action<FormatMessageHandler>>()), Times.Exactly(1));
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2+
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ImplicitlyCapturedClosure/@EntryIndexedValue">DO_NOT_SHOW</s:String>
23
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseIsOperator_002E2/@EntryIndexedValue">DO_NOT_SHOW</s:String>
34
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue">&lt;data&gt;&lt;IncludeFilters /&gt;&lt;ExcludeFilters /&gt;&lt;/data&gt;</s:String>
45
<s:String x:Key="/Default/FilterSettingsManager/AttributeFilterXml/@EntryValue">&lt;data /&gt;</s:String></wpf:ResourceDictionary>

0 commit comments

Comments
 (0)