Как бы вы изменили этот вонючий код? (Ведение журнала, копирование и вставка, .Net 3.5) - PullRequest
11 голосов
/ 08 мая 2009

У меня есть такой код:

Logger logger = new Logger();
System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
logger.LogInformation("Calling SomeObject.SomeMethod at " + DateTime.Now.ToString());
stopWatch.Start();
// This is the method I'm interested in.
SomeResponse response = someObject.SomeMethod(someParam);
stopWatch.Stop();
logger.LogInformation("SomeObject.SomeMethod returned at " + DateTime.Now.ToString());
logger.LogInformation("SomeObject.SomeMethod took " + stopWatch.ElapsedMilliseconds + " milliseconds.");

Мне нужно обернуть подобный код гораздо большим количеством объектов и их методов, чтобы выполнить профилирование производительности. Мне не разрешено использовать сторонние плагины или программное обеспечение и т. Д.

Я бы предпочел не писать этот код вокруг всех этих методов, вызывать весь этот код регистрации. Как бы вы реорганизовали это, чтобы устранить некоторые мои усилия по написанию кода?

Если мне не очень ясно, задавайте вопросы в комментариях, и я постараюсь уточнить.

Спасибо за любую помощь !!

Ответы [ 9 ]

19 голосов
/ 08 мая 2009

Можно выполнить рефакторинг кода для принятия экземпляра указателя метода (он же System.Action ).

public void CallWithLogTiming (Action theAction)
{
  Logger logger = new Logger();
  System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
  logger.LogInformation("Calling SomeObject.SomeMethod at " + DateTime.Now.ToString());
  stopWatch.Start();

// This is the method I'm interested in.
  theAction();

  stopWatch.Stop();
  logger.LogInformation("SomeObject.SomeMethod returned at " + DateTime.Now.ToString());
  logger.LogInformation("SomeObject.SomeMethod took " + stopWatch.ElapsedMilliseconds + " milliseconds.");
}

Тогда вы можете вызвать его, создав лямбда-выражение. Поскольку myResponse является захваченной переменной, она будет заполнена при запуске этого действия, а myResponse будет доступен для использования позже в этой области.

SomeResponse myResponse = null;
CallWithLogTiming( () => myResponse = someObject.SomeMethod(someParam) );
6 голосов
/ 08 мая 2009

Для простоты, вы можете использовать дженерики, вот так (вне головы):

public T MyLogMethod<T,S>(Func<S, T> someFunction, S someParameter) {}

Func (S, T) где S - тип параметра метода, а T - тип возвращаемого значения.

5 голосов
/ 08 мая 2009

Я думаю, что я реализую класс таймера, который можно использовать так:

void test()
{
  foo();
  //timer for the following statements
  using (new MyTimer("Some method"))
  {
    bar();
  }
  baz();
}

Класс MyTimer реализован следующим образом:

  • Содержит экземпляр секундомера и строку сообщения, которая идентифицирует таймер
  • Конструктор запускает секундомер и запоминает строку сообщения
  • Метод Dispose останавливает секундомер и регистрирует строку сообщения и истекшее время
2 голосов
/ 08 мая 2009

Вы можете сделать синтаксис немного чище с помощью нескольких простых методов расширения в Logger, это не требует дополнительных сборок, чем то, что у вас уже есть, и вы можете подключить его сразу. И это можно использовать повторно, если вы собираетесь делать это несколько раз по всему коду.

public static class LoggerExtentions
{
    public static void StartTimerLogInformation(this Logger logger, Stopwatch stopWatch, string method)
    {
        stopWatch.Reset();
        stopWatch.Start();
        logger.LogInformation(string.Format("Calling {0} at {1}", method, DateTime.Now.ToString()));
    }        

    public static void StopTimerLogInformation(this Logger logger, Stopwatch stopWatch, string method)
    {
        stopWatch.Stop();
        logger.LogInformation(string.Format("{0} returned at {1}", method, DateTime.Now.ToString()));
        logger.LogInformation(string.Format("{0} took {1} milliseconds", method, stopWatch.ElapsedMilliseconds));
        stopWatch.Reset();
    }
}

тогда вы можете просто использовать этот код вместо того кода, который у вас был в исходном посте

Logger logger = new Logger();
Stopwatch stopWatch = new Stopwatch();
logger.StartTimerLogInformation(stopWatch, "SomeObject.SomeMethod");
SomeResponse response = someObject.SomeMethod(someParam);
logger.StopTimerLogInformation(stopWatch, "SomeObject.SomeMethod");
2 голосов
/ 08 мая 2009

Всегда есть библиотека PostSharp , которая позволяет вам создавать аспектно-ориентированный код. Это позволяет вам вести журнал и секундомер как атрибут, что круто. Он вставит в ваш метод пре- и почтовый код в качестве шага после компиляции.

Кроме того, вы можете рассмотреть некоторые статические методы таймера / логгера, такие как этот, чтобы обернуть код, который вы хотите time / log:

Timer.LogExecutionTime("SomeObject.SomeMethod", () =>
{
    Logger.LogBeforeAndAfter("SomeObject.SomeMethod", () =>
    {
        SomeResponse response = someObject.SomeMethod(someParam);
    }
});
2 голосов
/ 08 мая 2009

Определенно кандидат в АОП. Мы используем PostSharp для такого рода вещей. http://www.postsharp.org/

1 голос
/ 08 мая 2009

Если вы напишете такой класс (я использую Java; могут быть некоторые вещи, которые не переводятся точно):

public class myProfiler {
    final String method;
    final Logger logger = new Logger();
    final System.Diagnostics.Stopwatch stopWatch = new Stopwatch();
    public myProfiler(method) {this.method = method};
    public void Start() {
        logger.LogInformation("Calling " + method + " at " + DateTime.Now.ToString());
        stopWatch.Start();      
    }
    public void Stop() {
        stopWatch.Stop();
        logger.LogInformation(method + " returned at " + DateTime.Now.ToString());
        logger.LogInformation(method + " took " + stopWatch.ElapsedMilliseconds + " milliseconds.");
    }
}

Затем вы сократили код, необходимый для каждого метода, до

myProfiler profiler = new myProfiler("SomeObject.SomeMethod");
profiler.Start();
...
profiler.Stop();
0 голосов
/ 18 февраля 2014

Что мне нравится делать в этих случаях, так это реализовывать мои таймеры с использованием шаблона Disposable; Вам будет гарантирована правильная очистка и регистрация в случае ошибки:

    public class Timer : IDisposable
    {
        Logger logger = new Logger();
        Stopwatch stopWatch = new Stopwatch();

        public Timer()
        {
            calledFunc = CalledFunc;
            logger.LogInformation("Calling SomeObject.SomeMethod at " +
                DateTime.Now.ToString());
            stopWatch.Start();
        }

        // Dispose() calls Dispose(true)
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        // NOTE: Leave out the finalizer altogether if this class doesn't 
        // own unmanaged resources itself, but leave the other methods
        // exactly as they are. 
        ~Timer()
        {
            // Finalizer calls Dispose(false)
            Dispose(false);
        }
        // The bulk of the clean-up code is implemented in Dispose(bool)
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // free managed resources
                stopWatch.Stop();
                logger.LogInformation("SomeObject.SomeMethod returned at " +
                    DateTime.Now.ToString());
                logger.LogInformation("SomeObject.SomeMethod took " +
                    stopWatch.ElapsedMilliseconds + " milliseconds.");
            }
            // free native resources if there are any.
        }
    }

Затем вы используете таймер следующим образом:

using (var t = new Timer())
{
   // your code
}

Конечно, вы можете передавать аргументы таймеру (имя метода, регистратор и т. Д.), Чтобы настроить то, что происходит в настройке, и утилизировать

0 голосов
/ 08 мая 2009

Как насчет наследования класса таймера для всех ваших объектов?

...