Я пытаюсь начать использовать аспектно-ориентированное программирование для повторяющихся задач. Я не уверен, как разделить проблемы. Я использую C#, а для АОП я использую Castle.DynamicProxy (используя функцию Autofac InterceptedBy), но я надеюсь, что ответ на этот вопрос может быть достаточно общим советом, который можно применить и к другим решениям АОП (возможно, вы сможете убедить мне переключиться на другое решение АОП).
Например, у меня есть что-то вроде следующего перехватчика, который перехватывает все вызовы методов класса. В настоящее время у него есть две проблемы: когда вызывается метод, (1) измерять, сколько времени занял вызов, и (2) регистрировать имя метода до и после его вызова.
public class TimeLoggingInterceptor : IInterceptor
{
private ILog m_Log;
public TimeLoggingInterceptor(ILog log)
{
m_Log = log;
}
public void Intercept(IInvocation invocation)
{
// Logging concerns
string fullMethodName = invocation.TargetType.Name + "." + invocation.MethodInvocationTarget.Name;
m_Log.Debug(fullMethodName + " started.");
// Timing concerns
DateTime beforeStamp = DateTime.UtcNow;
// Call method
invocation.Proceed();
// Timing concerns
DateTime afterStamp = DateTime.UtcNow;
TimeSpan callTime = afterStamp - beforeStamp;
// Logging concerns
m_Log.Debug(fullMethodName + " finished. Took " + callTime.TotalMilliseconds + "ms.");
}
}
У меня непреодолимое ощущение, что проблемы со временем (измерение того, сколько времени занял вызов метода) должны быть отделены от проблем с ведением журнала (запись в файл журнала), потому что... ну, это разные проблемы. Я думаю сделать что-то подобное, но не знаю, как подойти к упорядочению или куда поместить переменную callTime:
public class TimingInterceptor : IInterceptor
{
private static ThreadLocal<TimeSpan> callTime = new ThreadLocal<TimeSpan>();
public static TimeSpan CallTime
{
get
{
if (!callTime.IsValueCreated) throw new InvalidOperationException("callTime was never set");
return callTime.Value;
}
}
public void Intercept(IInvocation invocation)
{
// Timing concerns
DateTime beforeStamp = DateTime.UtcNow;
// Call method
invocation.Proceed();
// Timing concerns
DateTime afterStamp = DateTime.UtcNow;
callTime.Value = afterStamp - beforeStamp;
}
}
public class LoggingInterceptor : IInterceptor
{
private ILog m_Log;
public LoggingInterceptor(ILog log)
{
m_Log = log;
}
public void Intercept(IInvocation invocation)
{
// Logging concerns
string fullMethodName = invocation.TargetType.Name + "." + invocation.MethodInvocationTarget.Name;
m_Log.Debug(fullMethodName + " started.");
// Call method
invocation.Proceed();
// Logging concerns
m_Log.Debug(fullMethodName + " finished. Took " + TimingInterceptor.CallTime.TotalMilliseconds + "ms.");
}
}
По сути, я думаю, что здесь должно произойти то, что TimingInterceptor каким-то образом должен напрямую перехватывать методы, а затем LoggingInterceptor должен обходить это.
Какие подходы люди используют, чтобы убедиться, что эти проблемы будут происходить в правильном порядке? Должен ли я связывать перехватчики, используя метод перехвата LoggingInterceptor TimingInterceptor Intercept? Или мне добавить какой-то атрибут [InterceptOrder(1|2|3|...)]
в классы перехватчиков? Или я могу поставить что-то вроде [InterceptAfter(typeof(TimingInterceptor))]
на LoggingInterceptor?
И есть ли лучшая альтернатива использованию локальной переменной потока для отслеживания времени вызова? Да, я хочу, чтобы это было потокобезопасным. Я думаю, что сохранение этой переменной в стеке может быть предпочтительнее, но я не уверен, как LoggingInterceptor может получить дескриптор TimingInterceptor, не вводя слишком много связей (было бы неплохо иметь возможность отключить реализацию TimingInterceptor без перекомпиляции LoggingInterceptor) .