Created
September 4, 2018 20:50
-
-
Save explorer14/c105be970cdf7cd34d5f283eeae276f2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ProfilingProxy : DispatchProxy | |
{ | |
private T decorated; | |
private bool excludeProperties; | |
private string[] methodsToExcludeFromProfiling; | |
private IHttpContextAccessor httpContextAccessor; | |
public static T CreateProxy(T decorated, | |
IHttpContextAccessor httpContextAccessor, | |
ProfilingConfiguration config = null) | |
{ | |
// call the base class static method to create proxy instance, set the | |
// passed in params and return it | |
object proxy = Create(); | |
((ProfilingProxy)proxy).SetParameters(decorated, | |
config, | |
httpContextAccessor); | |
return (T)proxy; | |
} | |
protected override object Invoke( | |
MethodInfo targetMethod, object[] args) | |
{ | |
object result = default(object); | |
result = InvokeInternal(targetMethod, | |
args, | |
!ShouldNotProfile(targetMethod)); | |
return result; | |
} | |
// See if the current method qualifies for not being profiled i.e. in the | |
// exclusion list or a property with property exclusion enabled. | |
private bool ShouldNotProfile(MethodInfo targetMethod) | |
{ | |
return (methodsToExcludeFromProfiling != null && | |
methodsToExcludeFromProfiling | |
.Contains(targetMethod.Name)) || | |
(targetMethod.IsProperty() && | |
excludeProperties); | |
} | |
private object InvokeInternal( | |
MethodInfo targetMethod, | |
object[] args, bool doProfiling) | |
{ | |
object result; | |
Stopwatch stopwatch = null; | |
if (doProfiling) | |
{ | |
stopwatch = Stopwatch.StartNew(); | |
} | |
result = targetMethod.Invoke(this.decorated, args); | |
// see if this method invocation was an async one | |
var resultAsTask = result as Task; | |
if (resultAsTask != null) | |
{ | |
// if yes, then attach a continuation | |
// when the original task completes | |
resultAsTask.ContinueWith(task => | |
{ | |
var property = task.GetType() | |
.GetTypeInfo() | |
.GetProperties() | |
.FirstOrDefault(p => p.Name == "Result"); | |
if (property != null) | |
{ | |
// get the task result | |
result = property.GetValue(task); | |
if (doProfiling) | |
{ | |
stopwatch.Stop(); | |
Log.Logger.Information( | |
$"ASYNC: Method {this.decorated.GetType().FullName}:{targetMethod.Name} executed in " + | |
$"{stopwatch.Elapsed.ToString()} " + | |
$"RequestId {this.httpContextAccessor.HttpContext.TraceIdentifier}{Environment.NewLine}"); | |
} | |
} | |
}); | |
} | |
else | |
{ | |
if (doProfiling) | |
{ | |
stopwatch.Stop(); | |
Log.Logger.Information( | |
$"Method {this.decorated.GetType().FullName}:{targetMethod.Name} executed in " + | |
$"{stopwatch.Elapsed.ToString()} " + | |
$"RequestId {this.httpContextAccessor.HttpContext.TraceIdentifier}"); | |
} | |
} | |
return result; | |
} | |
private void SetParameters(T decorated, | |
ProfilingConfiguration configuration, | |
IHttpContextAccessor httpContextAccessor) | |
{ | |
this.decorated = decorated; | |
this.excludeProperties = | |
configuration?.ExcludeProperties ?? true; | |
this.methodsToExcludeFromProfiling = | |
configuration?.MethodNamesToExclude; | |
this.httpContextAccessor = httpContextAccessor; | |
} | |
} | |
/// | |
/// A simple configuration data structure to pass to the to let | |
/// it know which methods to exclude from profiling and whether or not to exclude properties from | |
/// being profiled. The default of the ExcludeProperties will be treated as "true" by the | |
/// ProfilingProxy internally | |
/// | |
public sealed class ProfilingConfiguration | |
{ | |
public string[] MethodNamesToExclude { get; set; } | |
public bool ExcludeProperties { get; set; } = true; | |
} | |
internal static class MethodInfoExtensions | |
{ | |
internal static bool IsProperty(this MethodInfo methodInfo) | |
=> methodInfo.Name.StartsWith("get_") || | |
methodInfo.Name.StartsWith("set_"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment