Я хочу регистрировать время, прошедшее с моих API. Я вижу здесь два разных подхода:

  1. Используя stopwatch. Создайте новый секундомер сразу после входа в API, а затем вызовите stop() непосредственно перед выходом из него (напечатайте прошедшее время в самом журнале).

  2. Сделайте две распечатки журнала, одну сразу после входа в API, а другую перед выходом из него. Истекшее время будет «сохранено» как разница во времени между двумя временными метками журнала.

Какой подход вы считаете лучшим?

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

2
Gianluca Ghettini 22 Фев 2016 в 12:26

2 ответа

Лучший ответ

Я бы выбрал первый вариант. Создание Stopwatch очень дешево. А с хорошей оболочкой требуемый код в каждом методе API может быть таким простым, как:

public int MyApiMethod()
{
    using (new ExecutionTimeLogger())
    {
        // All API functionality goes inside this using block.
        var theResultValue = 23;
        return theResultValue;
    }
}

Класс ExecutionTimeLogger будет выглядеть примерно так:

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using log4net;

public class ExecutionTimeLogger : IDisposable
{
    private readonly ILog log = LogManager.GetLogger("ExecutionTimes");
    private readonly string methodName;
    private readonly Stopwatch stopwatch;

    public ExecutionTimeLogger([CallerMemberName] string methodName = "")
    {
        this.methodName = methodName;
        stopwatch = Stopwatch.StartNew();
    }

    public void Dispose()
    {
        log.Debug(methodName + "() took " + stopwatch.ElapsedMilliseconds + " ms.");
        GC.SuppressFinalize(this);
    }
}

В зависимости от реализации вашего регистратора вывод может выглядеть примерно так:

15: 04: 23.4477 | DEBUG | ExecutionTimes | MyApiMethod () занял 42 мс.

Обратите внимание, что вывод журнала также будет сгенерирован, когда метод API выдал исключение внутри using, потому что экземпляр ExecutionTimeLogger будет удален в любом случае.

Параметр methodName будет автоматически заполнен компилятором, поскольку он имеет атрибут [CallerMemberName] атрибут. Вам не нужно передавать его каждый раз при создании ExecutionTimeLogger.

Строка GC.SuppressFinalize(this) сообщает сборщику мусора, что вызов финализатора экземпляра ExecutionTimeLogger не нужно планировать, потому что мы знаем, что он никогда не создавал неуправляемые ресурсы.


Если вы используете Unity в качестве платформы DI, вы также можете написать UnityContainerExtension, который обертывает каждый метод, имеющий определенный настраиваемый атрибут (например, LogExecutionTimeAttribute), с требуемым кодом измерения и регистрации. Однако это намного сложнее.

13
Good Night Nerd Pride 22 Фев 2016 в 14:30

Я предпочитаю первый подход (с использованием stopwatch).
если ваши методы вызываются синхронизацией, вам не нужно создавать новый объект Stopwatch для каждого вызова API. вы можете определить глобальный экземпляр Stopwatch и после вызова Stop() вызвать Reset() , или Restart(), чтобы начать отсчет с начала.

1
Mohy66 22 Фев 2016 в 09:56