Провайдеры логгирования

Данное руководство устарело. Актуальное руководство: Руководство по Entity Framework Core 7

Последнее обновление: 15.11.2020

Провайдеры логгирования собственно представляют те объекты, которые отвечают за логику логгирования. И в данном случае мы можем либо воспользоваться встроенными провайдерами, либо создать свой.

Создание своего провайдера логгирования

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

Для создания своего провайдера добавим в проект новый класс MyLoggerProvider:

using System;
using Microsoft.Extensions.Logging;
using System.IO;

namespace HelloApp
{
    public class MyLoggerProvider : ILoggerProvider
    {
        public ILogger CreateLogger(string categoryName)
        {
            return new MyLogger();
        }

        public void Dispose() { }

        private class MyLogger : ILogger
        {
            public IDisposable BeginScope<TState>(TState state)
            {
                return null;
            }

            public bool IsEnabled(LogLevel logLevel)
            {
                return true;
            }

            public void Log<TState>(LogLevel logLevel, EventId eventId, 
					TState state, Exception exception, Func<TState, Exception, string> formatter)
            {
                File.AppendAllText("log.txt", formatter(state, exception));
                Console.WriteLine(formatter(state, exception));
            }
        }
    }
}

Класс провайдера логгирования должен реализовать интерфейс ILoggerProvider. В этом интерфейсе определены два метода:

  • CreateLogger: создает и возвращает объект логгера. Для создания логгера используется путь к файлу, который передается через конструктор

  • Dispose: управляет освобождение ресурсов. В данном случае пустая реализация

Сам логгер представлен объектом ILogger. Этот интерфейс определяет три метода:

  • BeginScope: этот метод возвращает объект IDisposable, который представляет некоторую область видимости для логгера. В данном случае нам этот метод не важен, поэтому возвращаем значение null

  • IsEnabled: возвращает значения true или false, которые указывает, доступен ли логгер для использования. Здесь можно задать различную логику. В частности, в этот метод передается объект LogLevel, и мы можем, к примеру, задействовать логгер в зависимости от значения этого объекта. Но в данном случае просто возвращаем true, то есть логгер доступен всегда.

  • Log: этот метод предназначен для выполнения логгирования. Он принимает пять параметров:

    • LogLevel: уровень детализации текущего сообщения

    • EventId: идентификатор события

    • TState: некоторый объект состояния, который хранит сообщение

    • Exception: информация об исключении

    • formatter: функция форматирования, которая с помощью двух предыдущих параметров позволяет получить собственно сообщение для логгирования

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

Допустим, у нас будут следующая модель и контекст данных:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}
public class ApplicationContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public ApplicationContext()
    {
        Database.EnsureDeleted();
        Database.EnsureCreated();
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=helloappdb;Trusted_Connection=True;");
    }
}

Локальное применение провайдера

Теперь применим логгирование для контекста данных:

using Microsoft.EntityFrameworkCore.Infrastructure;
using System;
using System.Linq;
using Microsoft.Extensions.Logging;

namespace HelloApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                db.GetService<ILoggerFactory>().AddProvider(new MyLoggerProvider());
                User user1 = new User { Name = "Tom", Age = 33 };
                User user2 = new User { Name = "Alice", Age = 26 };
                
                db.Users.Add(user1);
                db.Users.Add(user2);
                db.SaveChanges();

                var users = db.Users.ToList();
                Console.WriteLine("Данные после добавления:");
                foreach (User u in users)
                {
                    Console.WriteLine($"{u.Id}.{u.Name} - {u.Age}");
                }
            }
            Console.Read();
        }
    }
}

С помощью метода GetService<ILoggerFactory>() получаем встроенный сервис ILoggerFactory, которому через метод AddProvider() передаем наш провайдер MyLoggerProvider.

И при запуске приложения на консоль будет выведена детальная информация по всем операциям:

Логгирование в Entity Framework Core

Глобальная установка логгирования в контексте данных

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

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;


public class ApplicationContext : DbContext
{
	public DbSet<User> Users { get; set; }
	public ApplicationContext()
	{
		Database.EnsureDeleted();
		Database.EnsureCreated();
	}
	protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
	{
		optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=helloappdb;Trusted_Connection=True;");
		optionsBuilder.UseLoggerFactory(MyLoggerFactory);
	}
	// устанавливаем фабрику логгера
	public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder =>
	{
		builder.AddProvider(new MyLoggerProvider());	// указываем наш провайдер логгирования
	});
}

Чтобы установить провайдер логгирования, нам нужна фабрика логгера - объект ILoggerFactory. Для этого определяем статическую переменную MyLoggerFactory, которой присваивается результат метода LoggerFactory.Create. Данный метод устанавливает для использования ранее созданный провайдер MyLoggerProvider.

Затем в методе OnConfiguring устанавливаем данную фабрику логгера:

optionsBuilder.UseLoggerFactory(MyLoggerFactory);

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

using (ApplicationContext db = new ApplicationContext())
{
	//db.GetService<ILoggerFactory>().AddProvider(new MyLoggerProvider());  // этот вызов больше не надо использовать
	
	// операции с данными
}

Настройка логгирования

Логгирование позволяет получить разнообразную информацию, связанную с операциями с данными, но не вся эта информация может быть нам нужна. В этом случае мы можем воспользоваться категориями логгирования, которые были рассмотрены в прошлой теме и которые представлены классом DbLoggerCategory. Например, мы хотим получать только данные о генерируемом и исполняемом SQL-коде. Для этого применим категорию Database.Command и изменим определение переменной MyLoggerFactory:

public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder =>
{
	builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name)
		.AddProvider(new MyLoggerProvider());
});

Для установки категории применяется метод AddFilter. Он принимает делегат, который получает категорию и уровень сообщений лога. Уровень сообщений лога в данном случае не используется. В теле лямбда-выражения мы сравниванием категорию со значением DbLoggerCategory.Database.Command.Name. Если эта операция сравнивания возвращает true, то сообщение попадает в лог.

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

public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder =>
{
	builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name 
						&& level == LogLevel.Information)
		.AddProvider(new MyLoggerProvider());
});

Использование встроенного провайдера

Выше мы определили и использовали свой собственный провайдер. Но в реальности мы можем использовать и провайдер, который предоставляется Microsoft. Для этого нам надо через пакетный менеджер Nuget установить пакет Microsoft.Extensions.Logging.Console. После добавления этого пакета в проект мы сможем вызывать встроенный консольный логгер посредством метода AddConsole():

public class ApplicationContext : DbContext
{
	public DbSet<User> Users { get; set; }
	public ApplicationContext()
	{
		Database.EnsureDeleted();
		Database.EnsureCreated();
	}
	protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
	{
		optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=helloappdb;Trusted_Connection=True;");
		optionsBuilder.UseLoggerFactory(MyLoggerFactory);
	}
	public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder =>
	{
		builder.AddConsole();
		// или так с более детальной настройкой
		//builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name
		//            && level == LogLevel.Information)
		//       .AddConsole();
	});
}

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

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850