-->

понедельник, 1 февраля 2016 г.

Логируем метод вместе с аргументами при помощи аспектов и DI

Очередной совет из серии "хорошо за 5 минут" - логируем вызов метода вместе со списком и значениями входных параметров!

Пусть будет для примера старинный ASMX клиент:
public partial class LegacyClient : System.ServiceModel.ClientBase<Legacy>, Legacy
у которого большое количество методов, переплетенных в причудливые макаронины вместе с остальным кодом. Писать свою обертку долго и больно, а логировать вызовы надо. Будем делать на аспектах с помощью DI-контейнера! В данном примере - Ninject. Конкретный контейнер не так важен. Важно, поддерживает ли он "перехватчики" - interceptors. На том, что такое Dependency Injection и DI-контейнеры - останавливаться не буду, все уже жевано-пережевано до меня.

Поехали, выделяем интерфейс с нужными методами (R# спешит на помощь):
public partial class LegacyClient : ILegacyClient {}
В конфиге контейнера указываем наш interceptor:
Kernel.Bind(typeof(ILegacyClient))
    .To<LegacyClient>()
    .Intercept()
    .With<LogRequestInterceptor>();
А вот и реализация interceptor'а:
using Ninject;
using Ninject.Extensions.Interception;

public class LogRequestInterceptor : IInterceptor 
{
    public void Intercept(IInvocation invocation)
    {
        MethodInfo method = invocation.Request.Method;

        var parameters = method.GetParameters();

        var builder = new StringBuilder();

        for (int index = 0; index < parameters.Length; index++)
        {
            object argument = invocation.Request.Arguments[index];

            ParameterInfo parameterInfo = parameters[index];

            if (!parameterInfo.IsOut)
            {
                //use any serialization you like
                string text = $"{parameterInfo.Name} = {argument.ToJson()}, ";

                builder.Append(text);
            }
        }

        string joinedParameters = builder.ToString();

        YourLogging(method.Name, joinedParameters); 

        //LegacyClient method call
        invocation.Proceed();
    }
}
Дополнительно к нашему "перехватчику" необходимо реализовать сериализацию аргументов метода (я предпочитаю JSON).

Получение объекта с дополнительными аспектами ничем не отличается от обычного resolve'а с помощью DI:
ILegacyClient client = Kernel.GetService<ILegacyClient>();
client.AnyMethodCall(...);
Самое важное в этом примере - мы не меняем интерфейс класса LegacyClient, поэтому вызывающий методы данного класса код остается нетронутым, и измениться не может в принципе. Необходимо лишь изменить способ создания объекта, что зачастую является куда менее трудоемкой задачей, чем написание прокси-класса. На этом всё, побольше удачи и поменьше рутины!




Комментариев нет:

Отправить комментарий