Создано при помощи нейросети Kandinsky

Библиотека SimpleExecutionLogger для логирования исполнения методов

User Rating: 5 / 5

Изображение к статье создано при помощи нейросети Kandinsky

Всем привет, друзья. 🎉

Я разработал небольшую библиотеку SimpleExecutionLogger на языке C#, предназначенную для логирования методов в программах, разработанных на платформе .NET 7.0. Библиотека позволяет добавить к вашему коду логирование методов и шагов их исполнения. С библиотекой SimpleExecutionLogger вы можете отследить, сколько времени заняло выполнение интересующих вас методов и получить текстовый лог, содержащий стек вызовов тех методов, для которых было включено логирование.

Для того, чтобы начать логировать исполнение методов в целевом интересующем классе, достаточно создать экземпляр логгера в нём. Затем нужно добавить вызовы специальных методов, доступных у логгера для начала логирования входа в интересующий метод и выхода из него. Вы также можете добавить настраиваемые шаги внутри метода с описанием того, что происходит на каждом шаге, и данная информация также попадёт в финальный лог.

Чтобы лучше понимать и читать стек вызовов для логируемых методов, логгер предусматривает возможность отступов для вложенных методов, которые вызываются из родительского метода: таким образом при чтении итогового лога можно понять, какие участки кода занимают больше всего времени и произвести требуемую оптимизацию кода.

Логирование рекомендуется осуществлять на этапе разработки и отладки вашего приложения для поиска узких или проблемных мест, которые могут быть неоптимальными по времени выполнения. На финальной стадии работы над кодом рекомендуется убрать логирование методов и экземпляр логгера из отлаживаемого класса, когда оптимизация кода уже завершена, поскольку логгер и процесс логирования также накладывают дополнительные накладные расходы во время выполнения программы.

🚀 Установка библиотеки и подключение к проекту

Библиотека SimpleExecutionLogger опубликована в виде NuGet пакета, который доступен по следующей ссылке: https://www.nuget.org/packages/SimpleExecutionLogger/

Чтобы начать использовать библиотеку, необходимо установить пакет библиотеки и подключить его к вашему проекту. Ниже приведены команды для установки пакета через .NET CLI и Package Manager.

.NET CLI 
dotnet add package SimpleExecutionLogger --version 1.0.0
Package Manager
PM> NuGet\Install-Package SimpleExecutionLogger -Version 1.0.0

Если вы работаете в среде Microsoft Visual Studio и хотите установить пакет без использования средств командной строки непосредственно в среде разработки, то выполните следующие шаги:

  1. В главном меню Microsoft Visual Studio выберите пункт меню Средства → Диспетчер пакетов NuGet → Управление пакетами NuGet для решения...
  2. В открывшемся окне "NuGet - решение" на вкладке "Обзор" введите в строку поиска SimpleExecutionLogger для того, чтобы найти пакет библиотеки в каталоге доступных пакетов NuGet
  3. После того, как пакет SimpleExecutionLogger найден, выберите его в результатах поиска и в правой части окна проставьте флажок напротив имени вашего проекта
  4. Далее, напротив выпадающего списка с текущей последней версией пакета для библиотеки (Последняя стабильная 1.0.0) нажмите кнопку "Установить".

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

🔗 Репозиторий проекта на GitHub

Репозиторий проекта с исходным кодом библиотеки и документацией располагается по адресу: https://github.com/AllineedRu/SimpleExecutionLogger

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

© Лицензия

Библиотека распространяется под лицензией MIT License

❓ Как использовать библиотеку

Допустим, у вас есть консольное приложение, и в нём есть основной класс MyAmazingClass, содержащий главный метод public static void Main() в качестве точки входа. И этот класс вам нужно логировать и инструментировать, т.е. нужно понимать, сколько времени выполняются методы Main, MyMethod1, MyMethod2 и OtherMethodN:

public class MyAmazingClass {

	// Конструктор класса MyAmazingClass
	public MyAmazingClass() {
		// ... некоторый код для инициализации класса ...
	}

	// Главная точка входа для логики вашего класса
	public static void Main() {
		Console.WriteLine("Привет из класса MyAmazingClass!");
		Console.WriteLine();

		// вызываем метод MyMethod1
		MyMethod1();
	}

	private void MyMethod1() {
		// ... какой-то код ...

		// теперь мы вызываем метод MyMethod2 из метода MyMethod1
		MyMethod2();
	}

	private void MyMethod2() {
		// ... какой-то код ...

		// наконец, мы вызываем OtherMethodN из метода MyMethod2
		OtherMethodN();
	}

	// ... другие методы класса MyAmazingClass ...

	private void OtherMethodN() {
		// ... другие инструкции ... 
	}
}

 

Здесь как раз вам сможет помочь библиотека SimpleExecutionLogger. Прежде всего необходимо импортировать пространство имён SimpleExecutionLogger при помощи директивы using:

using SimpleExecutionLogger;

public class MyAmazingClass {
	// ...
}

 

Теперь вы можете создать переменную логгера в классе MyAmazingClass (эта переменная должна иметь тип ExecutionLogger) и указать желаемое имя в виде строки для вашего логгера в вызове конструктора при создании экземпляра класса ExecutionLogger:

using SimpleExecutionLogger;

public class MyAmazingClass {

	// Статический экземпляр логгера для класса MyAmazingClass
	private static ExecutionLogger logger = new ExecutionLogger("Мой чудесный логгер");

	// Конструктор класса
	public MyAmazingClass() {
		// ... некоторый код для инициализации класса ...
	}

	// ...
}

 

Вот и все! Теперь у вас есть экземпляр логгера для класса MyAmazingClass, и вы можете добавить логирование в те методы вашего класса, которые вы хотите анализировать, логировать и отслеживать. Добавим логирование в наши методы Main, MyMethod1, MyMethod2 и OtherMethodN:

public class MyAmazingClass {
	// Статический экземпляр логгера для класса MyAmazingClass
	private static ExecutionLogger logger = new ExecutionLogger("Мой чудесный логгер");

	// Конструктор класса
	public MyAmazingClass() {
		// ... некоторый код для инициализации класса ...
	}

	// Главная точка входа для логики вашего класса
	public static void Main() {
		logger.StartMethod();

		Console.WriteLine("Привет из класса MyAmazingClass!");
		Console.WriteLine();

		// вызываем метод MyMethod1
		MyMethod1();

		logger.EndMethod();
	}

	private static void MyMethod1() {
		logger.StartMethod();
		
		// ... здесь идут какие-то инструкции ...

		// теперь мы вызываем метод MyMethod2 из метода MyMethod1
		MyMethod2();

		logger.EndMethod();
	}

	private static void MyMethod2() {
		logger.StartMethod();
		
		// ... здесь идут какие-то инструкции ...

		// наконец, мы вызываем OtherMethodN из метода MyMethod2
		OtherMethodN();

		logger.EndMethod();
	}

	// ... другие методы класса MyAmazingClass ...

	private static void OtherMethodN() {
		logger.StartMethod();

		// ... другие инструкции ... 

		logger.EndMethod();
	}
}

 

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

Но подождите... Как мы теперь можем получить доступ к логу и просмотреть его?

Это сделать легко. Всё, что вам нужно сделать, это просто выбрать правильное место для доступа к собранным логам. В нашем примере хорошим местом для получения собранных логов являются последние строки метода Main после вызова logger.EndMethod():

	// ...

	public static void Main() {
		logger.StartMethod();

		Console.WriteLine("Привет из класса MyAmazingClass!");
		Console.WriteLine();

		// вызываем метод MyMethod1
		MyMethod1();

		logger.EndMethod();

		// Получить логи, собранные экземпляром логгера 'logger'
		string log = logger.GetLog();

		// Распечатать логи на экране консоли
		Console.WriteLine("Собранные логи:");
		Console.WriteLine(log);
	}

	// ...

 

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

Привет из класса MyAmazingClass!

Собранные логи:
[Мой чудесный логгер]  >> Method 'Main' start at 09.12.2023 2:32:35
        [Мой чудесный логгер]  >> Method 'MyMethod1' start at 09.12.2023 2:32:35
                [Мой чудесный логгер]  >> Method 'MyMethod2' start at 09.12.2023 2:32:35
                        [Мой чудесный логгер]  >> Method 'OtherMethodN' start at 09.12.2023 2:32:35
                        [Мой чудесный логгер]  << Method 'OtherMethodN' end at 09.12.2023 2:32:35, duration: 0 ms
                [Мой чудесный логгер]  << Method 'MyMethod2' end at 09.12.2023 2:32:35, duration: 0 ms
        [Мой чудесный логгер]  << Method 'MyMethod1' end at 09.12.2023 2:32:35, duration: 0 ms
[Мой чудесный логгер]  << Method 'Main' end at 09.12.2023 2:32:35, duration: 15 ms

С помощью нашего экземпляра логгера мы теперь получили полную информацию о методах, которые были вызваны нашим приложением! Обратите внимание, что для каждого метода у нас также есть соответствующая метка времени, когда этот метод был запущен, и метка времени, когда он был завершен. Также была собрана продолжительность каждого зарегистрированного в логгере метода. Например, мы видим, что наш метод Main выполняется 15 миллисекунд. Остальные методы MyMethod1, MyMethod2, OtherMethodN были слишком быстрыми, поскольку в нашем коротком примере они пусты.

Вы также можете заметить, что всё логирование производится на английском языке (кроме названия логгера "Мой логгер", которое мы указали при его создании в конструкторе класса ExecutionLogger).

Ниже, в разделе "Конфигурация", я покажу, как можно перевести стандартные части фраз, выводимых логгером, на русский язык.

🛠️ Конфигурация

Класс ExecutionLogger имеет несколько общедоступных (public) свойств, которые можно дополнительно настроить для вашего экземпляра логгера перед началом процесса логирования:

Свойство Тип данных Описание Значение по умолчанию
TabulationPrefix string Строка, определяющая символ табуляции или пользовательскую строку, используемую для отступов вложенных методов "\t"
MethodStartedLogPrefix string Строка-префикс, добавляется в лог при входе в метод (в начале выполнения метода) " >> Method "
MethodEndedLogPrefix string Строка-префикс, добавляется в лог при выходе из метода (в конце выполнения метода, при возврате из метода) " << Method "
MethodStepLogPrefix string Строка-префикс, добавляется в лог при логировании определенного шага в рамках выполняющегося метода " Method "
LoggerNameFormat string Формат печати имени экземпляра логгера. Пользовательская строка, определяющая формат, должна содержать только один элемент форматирования {0} "[{0}] "
EnableLoggerName bool Флаг, определяющий, следует ли включить печать имени экземпляра логгера во время логирования. Установите в значение false, чтобы скрыть имя логгера в записях лога true
StepNameFormat string Формат печати названия шага метода. Пользовательская строка, определяющая формат, должна содержать только один элемент форматирования {0} "[Step: {0}]: "
StartAtString string Пользовательская строка, определяющая фразу в логе для вывода точки времени начала выполнения метода " start at "
EndAtString string Пользовательская строка, определяющая фразу в логе для вывода точки времени завершения выполнения метода " end at "
AtString string Пользовательская строка, определяющая фразу в логе для вывода точки времени начала выполнения шага внутри метода " at "
MethodNameQuoteString string Строка, используемая для обёртывания имени логируемого метода слева и справа. "'"
StepNameSeparator string Строка-разделитель между именем логируемого метода и именем логируемого шага внутри этого метода. ": "
MethodExecutionDurationFormat string Строка, определяющая формат отображения продолжительности выполнения зарегистрированного метода, должна содержать только один элемент форматирования {0} ", duration: {0} ms"
MethodStepDurationFormat string Строка, задающая формат отображения времени, прошедшего от начала выполнения метода до шага внутри этого метода, а также разницу во времени между этим шагом и предыдущим шагом, если такой предыдущий шаг имеется. Должна содержать только два элемента форматирования {0} и {1}. ", elapsed from start: {0} ms, delta={1} ms"

 

Если вы хотите изменить некоторые из этих доступных свойств на свои собственные значения перед началом процесса логирования, вы можете установить нужные значения для вашего экземпляра логгера перед первым вызовом метода  StartMethod для экземпляра логгера. Например, вы можете разместить свою пользовательскую конфигурацию в статическом конструкторе вашего класса, если ваш экземпляр логгера определен в качестве статической переменной внутри вашего логируемого класса:

using SimpleExecutionLogger;

public class MyAmazingClass {
    // Статический экземпляр логгера для класса MyAmazingClass
    private static ExecutionLogger logger = new ExecutionLogger("Мой чудесный логгер");

    static MyAmazingClass() {
        logger.TabulationPrefix = "  ";
        logger.MethodStartedLogPrefix = " Вход в метод ";
        logger.MethodEndedLogPrefix = " Выход из метода ";
        logger.MethodStepLogPrefix = " Метод ";
        logger.LoggerNameFormat = "(Логгер: '{0}')";
        logger.StartAtString = " в ";
        logger.EndAtString = " в ";
        logger.AtString = ", время - ";
        logger.StepNameFormat = "[Шаг: {0}] ";
        logger.MethodExecutionDurationFormat = ", длительность: {0} мс";
        logger.MethodStepDurationFormat = ", прошло от старта {0} мс, дельта = {1} мс";
    }

    // ... остальной код и методы класса MyAmazingClass ...
}

 

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

Привет из класса MyAmazingClass!

Собранные логи:
(Логгер: 'Мой чудесный логгер') Вход в метод 'Main' в 10.12.2023 12:21:16
  (Логгер: 'Мой чудесный логгер') Вход в метод 'MyMethod1' в 10.12.2023 12:21:16
    (Логгер: 'Мой чудесный логгер') Вход в метод 'MyMethod2' в 10.12.2023 12:21:16
      (Логгер: 'Мой чудесный логгер') Вход в метод 'OtherMethodN' в 10.12.2023 12:21:16
      (Логгер: 'Мой чудесный логгер') Выход из метода 'OtherMethodN' в 10.12.2023 12:21:16, длительность: 0 мс
    (Логгер: 'Мой чудесный логгер') Выход из метода 'MyMethod2' в 10.12.2023 12:21:16, длительность: 0 мс
  (Логгер: 'Мой чудесный логгер') Выход из метода 'MyMethod1' в 10.12.2023 12:21:16, длительность: 0 мс
(Логгер: 'Мой чудесный логгер') Выход из метода 'Main' в 10.12.2023 12:21:16, длительность: 16 мс

 

Если ваш экземпляр логгера определен как нестатическое поле в логируемом классе, хорошим местом для настройки пользовательских значений доступных свойств является конструктор вашего логируемого класса. Допустим, у нас есть еще один класс MyAnotherAmazingClass, где экземпляр логгера теперь определен как нестатическая переменная класса. В этом случае мы добавляем конфигурацию для пользовательских свойств в нестатический конструктор этого логируемого класса:

public class MyAnotherAmazingClass {
	// Нестатический экземпляр логгера для этого класса
	private ExecutionLogger logger = new ExecutionLogger("Другой чудесный логгер");

	// Нестатический конструктор класса
	public MyAnotherAmazingClass() {
		logger.TabulationPrefix = "  ";
		logger.MethodStartedLogPrefix = " Вход в метод ";
		logger.MethodEndedLogPrefix = " Выход из метода ";
		logger.MethodStepLogPrefix = " Метод ";
		logger.LoggerNameFormat = "(Логгер: '{0}')";
		logger.StartAtString = " в ";
		logger.EndAtString = " в ";
		logger.AtString = ", время - ";
		logger.StepNameFormat = "[Шаг: {0}] ";
		logger.MethodExecutionDurationFormat = ", длительность: {0} мс";
		logger.MethodStepDurationFormat = ", прошло от старта {0} мс, дельта = {1} мс";
	}

	// ... остальной код и методы класса MyAnotherAmazingClass ...
}

 

📕 API

Помимо StartMethod(), EndMethod() и GetLog(), в классе ExecutionLogger есть еще несколько общедоступных (public) методов, которые могут быть полезны для управления процессом логирования. Ниже приведен список других доступных методов класса ExecutionLogger:

Метод Описание
void ClearLog() Выполняет очистку внутреннего строкового буфера, содержащего текст полного лога
void ClearLoggedMethodsStack() Очищает стек, содержащий логируемые методы. Также сбрасывает текущий уровень метода на значение 0 (уровень отступов/табуляции для вложенных методов, вызываемых из "родительского" метода)
void ClearAll() Выполняет полную очистку текущего экземпляра логгера: очищает текущий лог и стек исполняемых методов
void LogMethodStep(string stepDescription) Добавляет в журнал указанный шаг (описание выполненной операции) в рамках выполнения текущего метода
void LogMethodStep(string? stepName, string stepDescription) Добавляет в журнал указанный шаг (описание выполненной операции) в рамках выполнения текущего метода

 

Чтобы лучше понять, как можно использовать эти дополнительные методы, давайте рассмотрим несколько примеров.

💡 Примеры использования API

✔️ Метод LogMethodStep

Вы можете добавить некоторые дополнительные логические шаги в свои логируемые методы на случай, если эти шаги также необходимо будет добавить в окончательный лог. Для этого используются методы void LogMethodStep(string stepDescription) и void LogMethodStep(string? stepName, string stepDescription). Давайте рассмотрим следующий пример на языке C# и немного улучшим наш предыдущий вариант класса MyAmazingClass: 

using SimpleExecutionLogger;

public class MyAmazingClass {
    // Статический экземпляр логгера для класса MyAmazingClass
    private static ExecutionLogger logger = new ExecutionLogger("Мой чудесный логгер");

    static MyAmazingClass() {
        logger.TabulationPrefix = "  ";
        logger.MethodStartedLogPrefix = " Вход в метод ";
        logger.MethodEndedLogPrefix = " Выход из метода ";
        logger.MethodStepLogPrefix = " Метод ";
        logger.LoggerNameFormat = "(Логгер: '{0}')";
        logger.StartAtString = " в ";
        logger.EndAtString = " в ";
        logger.AtString = ", время - ";
        logger.StepNameFormat = "[Шаг: {0}] ";
        logger.MethodExecutionDurationFormat = ", длительность: {0} мс";
        logger.MethodStepDurationFormat = ", прошло от старта {0} мс, дельта = {1} мс";
    }

    // Конструктор класса
    public MyAmazingClass() {
        // ... некоторый код для инициализации класса ...
    }

    public static void Main() {
        logger.StartMethod();

        Console.WriteLine("Привет из класса MyAmazingClass!");
        Console.WriteLine();

        // вызываем метод MyMethod1 с логированием соответствующего шага
        logger.LogMethodStep("Вызываем метод 'MyMethod1'...");
        MyMethod1();

        logger.EndMethod();

        string log = logger.GetLog();

        Console.WriteLine("Собранные логи:");
        Console.WriteLine(log);
    }

    private static void MyMethod1() {
        logger.StartMethod();

        logger.LogMethodStep("Входим в цикл с переменной-счётчиком 'i'...");
        for (int i = 0; i < 10; i++) {
            logger.LogMethodStep(i.ToString(), "Значение i = " + i);
            Thread.Sleep(10);
        }

        // теперь мы вызываем метод MyMethod2 из метода MyMethod1
        MyMethod2();

        logger.EndMethod();
    }

    private static void MyMethod2() {
        logger.StartMethod();

        // ... здесь идут какие-то инструкции ...

        // наконец, мы вызываем OtherMethodN из метода MyMethod2
        OtherMethodN();

        logger.EndMethod();
    }

    // ... другие методы класса MyAmazingClass ...

    private static void OtherMethodN() {
        logger.StartMethod();

        // ... другие инструкции ... 

        logger.EndMethod();
    }
}

 

В этом примере мы добавили вызов метода LogMethodStep в наш метод Main:

        // вызываем метод MyMethod1 с логированием соответствующего шага
        logger.LogMethodStep("Вызываем метод 'MyMethod1'...");
        MyMethod1();

 

Затем мы добавили простой цикл for в метод MyMethod1, а внутри этого цикла мы добавили логирование шагов для каждой итерации цикла, передав выражение i.ToString() в качестве имени шага и выражение "Значение i =" + i в качестве описание шага:

    private static void MyMethod1() {
        logger.StartMethod();

        logger.LogMethodStep("Входим в цикл с переменной-счётчиком 'i'...");
        for (int i = 0; i < 10; i++) {
            logger.LogMethodStep(i.ToString(), "Значение i = " + i);
            Thread.Sleep(10);
        }

        // теперь мы вызываем метод MyMethod2 из метода MyMethod1
        MyMethod2();

        logger.EndMethod();
    }

 

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

Привет из класса MyAmazingClass!

Собранные логи:
(Логгер: 'Мой чудесный логгер') Вход в метод 'Main' в 10.12.2023 13:07:36
  (Логгер: 'Мой чудесный логгер') Метод 'Main': Вызываем метод 'MyMethod1'..., время - 10.12.2023 13:07:36, прошло от старта 14 мс, дельта = 0 мс
  (Логгер: 'Мой чудесный логгер') Вход в метод 'MyMethod1' в 10.12.2023 13:07:36
    (Логгер: 'Мой чудесный логгер') Метод 'MyMethod1': Входим в цикл с переменной-счётчиком 'i'..., время - 10.12.2023 13:07:36, прошло от старта 0 мс, дельта = 0 мс
    (Логгер: 'Мой чудесный логгер') Метод 'MyMethod1': [Шаг: 0] Значение i = 0, время - 10.12.2023 13:07:36, прошло от старта 0 мс, дельта = 0 мс
    (Логгер: 'Мой чудесный логгер') Метод 'MyMethod1': [Шаг: 1] Значение i = 1, время - 10.12.2023 13:07:36, прошло от старта 16 мс, дельта = 16 мс
    (Логгер: 'Мой чудесный логгер') Метод 'MyMethod1': [Шаг: 2] Значение i = 2, время - 10.12.2023 13:07:36, прошло от старта 32 мс, дельта = 15 мс
    (Логгер: 'Мой чудесный логгер') Метод 'MyMethod1': [Шаг: 3] Значение i = 3, время - 10.12.2023 13:07:36, прошло от старта 48 мс, дельта = 16 мс
    (Логгер: 'Мой чудесный логгер') Метод 'MyMethod1': [Шаг: 4] Значение i = 4, время - 10.12.2023 13:07:36, прошло от старта 64 мс, дельта = 16 мс
    (Логгер: 'Мой чудесный логгер') Метод 'MyMethod1': [Шаг: 5] Значение i = 5, время - 10.12.2023 13:07:36, прошло от старта 80 мс, дельта = 16 мс
    (Логгер: 'Мой чудесный логгер') Метод 'MyMethod1': [Шаг: 6] Значение i = 6, время - 10.12.2023 13:07:36, прошло от старта 95 мс, дельта = 16 мс
    (Логгер: 'Мой чудесный логгер') Метод 'MyMethod1': [Шаг: 7] Значение i = 7, время - 10.12.2023 13:07:36, прошло от старта 110 мс, дельта = 15 мс
    (Логгер: 'Мой чудесный логгер') Метод 'MyMethod1': [Шаг: 8] Значение i = 8, время - 10.12.2023 13:07:36, прошло от старта 125 мс, дельта = 15 мс
    (Логгер: 'Мой чудесный логгер') Метод 'MyMethod1': [Шаг: 9] Значение i = 9, время - 10.12.2023 13:07:36, прошло от старта 141 мс, дельта = 16 мс
    (Логгер: 'Мой чудесный логгер') Вход в метод 'MyMethod2' в 10.12.2023 13:07:36
      (Логгер: 'Мой чудесный логгер') Вход в метод 'OtherMethodN' в 10.12.2023 13:07:36
      (Логгер: 'Мой чудесный логгер') Выход из метода 'OtherMethodN' в 10.12.2023 13:07:36, длительность: 0 мс
    (Логгер: 'Мой чудесный логгер') Выход из метода 'MyMethod2' в 10.12.2023 13:07:36, длительность: 0 мс
  (Логгер: 'Мой чудесный логгер') Выход из метода 'MyMethod1' в 10.12.2023 13:07:36, длительность: 157 мс
(Логгер: 'Мой чудесный логгер') Выход из метода 'Main' в 10.12.2023 13:07:36, длительность: 177 мс

Заметьте, что благодаря тому, что мы добавили логирование отдельных шагов внутри методов, в логе мы имеем информацию о каждом таком шаге, а также указание длительности этого шага, в миллисекундах. 

✔️ Метод ClearLog

Класс ExecutionLogger имеет внутренний строковый буфер для хранения полного текста лога. Если вы считаете, что этот внутренний буфер перегружен (например, в какой-то момент исполнения вашей программы), вы можете сбросить этот буфер, вызвав метод ClearLog. Он очистит этот внутренний буфер, но учтите, что после вызова этого метода вы уже не сможете получить доступ к записям в логе, собранным до момента очистки:

	// ... некоторый код, где мы уже распечатали собранный лог
	// и больше нам не нужен строковый буфер для сбора логов ...

	// очищаем внутренний строковый буфер, хранящий полный текст лога, 
	// для экземпляра логгера:	
	logger.ClearLog();
 
✔️ Метод ClearLoggedMethodsStack

Этот метод используется для очистки текущего внутреннего уровня отступа для стека методов (т.е. сброса его в значение 0) и очистки внутреннего стека, содержащего информацию о последовательности вызовов методов (стека методов).

Info icon by Icons8НА ЗАМЕТКУ
Обратите внимание, что вызов метода ClearLoggedMethodsStack не очищает внутренний строковой буфер, который хранит полный лог. Он производит только очистку стека вызовов методов и сброс уровня отступов для методов в значение 0.

	// сбросить внутренний стек вызова методов для экземпляра логгера,
	// а также уровень отступа для стека методов в значение 0:
	logger.ClearLoggedMethodsStack();
 
✔️ Метод ClearAll

Метод ClearAll просто вызывает два рассмотренных выше метода: ClearLog и ClearLoggedMethodsStack, поэтому вызывайте для экземпляра вашего логгера, когда хотите очистить как внутренний строковый буфер, так и стек методов:

	// мы вызываем ClearAll() в момент, когда мы хотим полностью сбросить состояние
	// экземпляра логгера:
	logger.ClearAll();

 

Ниже я также приведу простые примеры использования библиотеки SimpleExecutionLogger в программах, написанных на языках Visual Basic и F# для платформы .NET 7.0.

💡 Пример использования библиотеки в программе на Visual Basic (.NET 7.0)

Рассмотрим пример простой программы (консольное приложение), использующей библиотеку SimpleExecutionLogger и написанной на языке Visual Basic:

Imports SimpleExecutionLogger

Module Program
    Dim logger As ExecutionLogger = New ExecutionLogger("Логгер")

    Sub New()
        logger.TabulationPrefix = "  "
        logger.MethodStartedLogPrefix = " Вход в метод "
        logger.MethodEndedLogPrefix = " Выход из метода "
        logger.MethodStepLogPrefix = " Метод "
        logger.LoggerNameFormat = "(Логгер: '{0}')"
        logger.StartAtString = " в "
        logger.EndAtString = " в "
        logger.AtString = ", время - "
        logger.StepNameFormat = "[Шаг: {0}] "
        logger.MethodExecutionDurationFormat = ", длительность: {0} мс"
        logger.MethodStepDurationFormat = ", прошло от старта {0} мс, дельта = {1} мс"
    End Sub

    Sub Main(args As String())
        logger.StartMethod()

        Console.WriteLine("Привет из программы на Visual Basic!")
        MySub1()

        logger.EndMethod()

        Console.WriteLine(logger.GetLog())
    End Sub

    Sub MySub1()
        logger.StartMethod()

        logger.LogMethodStep("Вход в цикл по i...")
        For i = 1 To 10
            logger.LogMethodStep(i.ToString(), "Переменная i = " & CStr(i))
            Threading.Thread.Sleep(10)
        Next

        logger.EndMethod()
    End Sub
End Module

Если запустим программу, то мы увидим информацию о логируемых методах и шагах. В моём случае получился следующий лог:

Привет из программы на Visual Basic!
(Логгер: 'Логгер') Вход в метод 'Main' в 10.12.2023 14:11:38
  (Логгер: 'Логгер') Вход в метод 'MySub1' в 10.12.2023 14:11:38
    (Логгер: 'Логгер') Метод 'MySub1': Вход в цикл по i..., время - 10.12.2023 14:11:38, прошло от старта 0 мс, дельта = 0 мс
    (Логгер: 'Логгер') Метод 'MySub1': [Шаг: 1] Переменная i = 1, время - 10.12.2023 14:11:38, прошло от старта 0 мс, дельта = 0 мс
    (Логгер: 'Логгер') Метод 'MySub1': [Шаг: 2] Переменная i = 2, время - 10.12.2023 14:11:38, прошло от старта 24 мс, дельта = 24 мс
    (Логгер: 'Логгер') Метод 'MySub1': [Шаг: 3] Переменная i = 3, время - 10.12.2023 14:11:38, прошло от старта 40 мс, дельта = 16 мс
    (Логгер: 'Логгер') Метод 'MySub1': [Шаг: 4] Переменная i = 4, время - 10.12.2023 14:11:38, прошло от старта 56 мс, дельта = 16 мс
    (Логгер: 'Логгер') Метод 'MySub1': [Шаг: 5] Переменная i = 5, время - 10.12.2023 14:11:38, прошло от старта 71 мс, дельта = 15 мс
    (Логгер: 'Логгер') Метод 'MySub1': [Шаг: 6] Переменная i = 6, время - 10.12.2023 14:11:38, прошло от старта 87 мс, дельта = 16 мс
    (Логгер: 'Логгер') Метод 'MySub1': [Шаг: 7] Переменная i = 7, время - 10.12.2023 14:11:38, прошло от старта 103 мс, дельта = 16 мс
    (Логгер: 'Логгер') Метод 'MySub1': [Шаг: 8] Переменная i = 8, время - 10.12.2023 14:11:38, прошло от старта 118 мс, дельта = 15 мс
    (Логгер: 'Логгер') Метод 'MySub1': [Шаг: 9] Переменная i = 9, время - 10.12.2023 14:11:38, прошло от старта 134 мс, дельта = 16 мс
    (Логгер: 'Логгер') Метод 'MySub1': [Шаг: 10] Переменная i = 10, время - 10.12.2023 14:11:38, прошло от старта 149 мс, дельта = 15 мс
  (Логгер: 'Логгер') Выход из метода 'MySub1' в 10.12.2023 14:11:38, длительность: 164 мс
(Логгер: 'Логгер') Выход из метода 'Main' в 10.12.2023 14:11:38, длительность: 188 мс

 

💡 Пример использования библиотеки в программе на F# (.NET 7.0)

Рассмотрим пример простой программы (консольное приложение), использующей библиотеку SimpleExecutionLogger и написанной на языке F#:

open System
open SimpleExecutionLogger

let logger = new ExecutionLogger("Логгер")

logger.TabulationPrefix <- "  "
logger.MethodStartedLogPrefix <- " Вход в метод "
logger.MethodEndedLogPrefix <- " Выход из метода "
logger.MethodStepLogPrefix <- " Метод "
logger.LoggerNameFormat <- "(Логгер: '{0}')"
logger.StartAtString <- " в "
logger.EndAtString <- " в "
logger.AtString <- ", время - "
logger.StepNameFormat <- "[Шаг: {0}] "
logger.MethodExecutionDurationFormat <- ", длительность: {0} мс"
logger.MethodStepDurationFormat <- ", прошло от старта {0} мс, дельта = {1} мс"

printfn "Привет из программы F#, использующей библиотеку SimpleExecutionLogger!"

logger.StartMethod();

let simpleCycle =
    logger.StartMethod()
    logger.LogMethodStep("simpleCycle: входим в цикл по i от 1 до 10")
    for i in 1..10 do
        logger.LogMethodStep(i.ToString(), "Значение i = " + i.ToString())
        Threading.Thread.Sleep(10)
    logger.LogMethodStep("simpleCycle: выходим")
    logger.EndMethod()

simpleCycle

logger.EndMethod()

let log = logger.GetLog()
printf "%s" log

Если запустим программу, то мы увидим информацию о логируемых методах и шагах. В моём случае получился следующий лог:

Привет из программы F#, использующей библиотеку SimpleExecutionLogger!
(Логгер: 'Логгер') Вход в метод 'main@' в 10.12.2023 14:21:20
  (Логгер: 'Логгер') Вход в метод 'main@' в 10.12.2023 14:21:20
    (Логгер: 'Логгер') Метод 'main@': simpleCycle: входим в цикл по i от 1 до 10, время - 10.12.2023 14:21:20, прошло от старта 0 мс, дельта = 0 мс
    (Логгер: 'Логгер') Метод 'main@': [Шаг: 1] Значение i = 1, время - 10.12.2023 14:21:20, прошло от старта 1 мс, дельта = 1 мс
    (Логгер: 'Логгер') Метод 'main@': [Шаг: 2] Значение i = 2, время - 10.12.2023 14:21:20, прошло от старта 21 мс, дельта = 20 мс
    (Логгер: 'Логгер') Метод 'main@': [Шаг: 3] Значение i = 3, время - 10.12.2023 14:21:20, прошло от старта 37 мс, дельта = 16 мс
    (Логгер: 'Логгер') Метод 'main@': [Шаг: 4] Значение i = 4, время - 10.12.2023 14:21:20, прошло от старта 52 мс, дельта = 15 мс
    (Логгер: 'Логгер') Метод 'main@': [Шаг: 5] Значение i = 5, время - 10.12.2023 14:21:20, прошло от старта 68 мс, дельта = 16 мс
    (Логгер: 'Логгер') Метод 'main@': [Шаг: 6] Значение i = 6, время - 10.12.2023 14:21:20, прошло от старта 84 мс, дельта = 16 мс
    (Логгер: 'Логгер') Метод 'main@': [Шаг: 7] Значение i = 7, время - 10.12.2023 14:21:20, прошло от старта 100 мс, дельта = 16 мс
    (Логгер: 'Логгер') Метод 'main@': [Шаг: 8] Значение i = 8, время - 10.12.2023 14:21:20, прошло от старта 116 мс, дельта = 16 мс
    (Логгер: 'Логгер') Метод 'main@': [Шаг: 9] Значение i = 9, время - 10.12.2023 14:21:20, прошло от старта 131 мс, дельта = 15 мс
    (Логгер: 'Логгер') Метод 'main@': [Шаг: 10] Значение i = 10, время - 10.12.2023 14:21:20, прошло от старта 146 мс, дельта = 15 мс
    (Логгер: 'Логгер') Метод 'main@': simpleCycle: выходим, время - 10.12.2023 14:21:20, прошло от старта 162 мс, дельта = 16 мс
  (Логгер: 'Логгер') Выход из метода 'main@' в 10.12.2023 14:21:20, длительность: 163 мс
(Логгер: 'Логгер') Выход из метода 'main@' в 10.12.2023 14:21:20, длительность: 169 мс

 

Яндекс.Метрика