Pull to refresh

Автоматизация логирования входов в функции

Reading time 2 min
Views 4.1K
У нас в компании с незапамятных времен существует гласно-негласное правило о логировании входа в каждую функцию. И ладно бы это ограничивалось простой строчкой Logger.LogEntering() в их начале (хотя, наверное, тоже надоело бы), так еще и наш «замечательный» доморощенный логгер получать названия функций из которых он вызван не умеет, и как следствие, эта единственная строчка разрасталась до эпического Logger.Log(«Classname.FunctionName — Entering») or something like that.

Неудивительно, что под воздействием недавних топиков о Mono.Cecil и родилась задача автоматизации процесса.



Для того, чтобы продукт представлял не только внутрикорпоративную ценность, но и был общественно полезным, решено было поддержкой «внутреннего» логгера не ограничиваться, а также поддержать сторонние log4net и nlog (благо, и для внутренних нужд мы плавно переходим на log4net).

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

Вступление затянулось, и пора переходить от слов к делу.
Реализация — это msbuild-task, который запускается на этапе пост-компиляции и производит инъекцию вызовов Logger.Debug(«Entering») в начало каждой функции. Вернее, не каждой, а только помеченные атрибутом [Log]. Или во все функции классов, помеченные этим атрибутом. Ну или всё-таки в начало каждой функции модуля, если атрибут [Log] затесался в AssemblyInfo. Если же какие-то функции логировать ну никак не хочется, то их можно пометить атрибутом [NoLog].

Чтобы эта msbuild-задача выполнялась, в .csproj\.vbproj необходимо добавить её вызов. Например, так:

<Import Project="c:\path\to\Logging.NLog.targets" />

Ну или чуть-чуть изменив, если вы предпочитаете log4net.

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

    //имя класса - Logger - имеет значение. Можно менять, но тогда надо его менять и в .targets-файле
    public class Logger
    {
        public static NLog.Logger Log = NLog.LogManager.GetLogger("NlogConsoleApp");
    }


На этом и всё. Фильтрация результирующего потока логов возлагается целиком и полностью на логирующую библиотеку. LoggingMagic достаточно легко расширяется, имена атрибутов и класса с логгером меняются в параметрах msbuild task'a.

Серьезным недостатком является отсутствие поддержки методики logger-per-class (которая пропагандируется, к примеру, nlog'ом), логирование возможно либо через статичное поле, либо через статичную функцию. В комментариях было бы любопытно узнать, каким способом логирования пользуетесь вы :) Логирование per-class возможно будет добавлено при настойчивых требованиях сообщества :)

Итог получился урезанным подобием Log4Postsharp, но без привязки к проприетарным компонентам и без необходимости линковки с чем-либо сторонним и поставки этого при инсталяциях (библиотеки loggingmagic дальше Buildserver'a никуда не уходят).

Исходные коды всего этого безобразия выложены на codeplex

P.S. надеюсь, что к дискуссии о нужности-ненужности собственно логирования входов всё не сведется, потому что это чаще всего определяется предметной областью. В нашем конкретном случае это порой единственный способ сбора детальной информации при обнаружении проблем на клиентских инсталляциях.
Tags:
Hubs:
+20
Comments 19
Comments Comments 19

Articles