Создание приемника файла serilog позже во время выполнения - как?

Я использую Serilog на стороне сервера для всех своих служб .NETCore, использующих консоль, файл и приемники Graylog. Мне также нравится использовать его в моих толстых клиентах Windows (приложениях WPF).

С последним у меня проблема, так как я не понимаю, как я могу добавить еще один приемник файлов ПОСЛЕ того, как пользователь успешно вошел в систему. Мне нужен журнал приложения, хранящийся в глобальной папке AppData (Environment.SpecialFolder.CommonApplicationData), и другой журнал пользователя в подпапке пользователя, например что-то вроде этого:

var appName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
AppDataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), appName);
Directory.CreateDirectory(AppDataDir);
AppLogDir = Path.Combine(AppDataDir, $@"Logs");
Directory.CreateDirectory(AppLogDir);

Log.Logger = new LoggerConfiguration()
    .ReadFrom.AppSettings()
    .Enrich.WithExceptionDetails()
    .Enrich.FromLogContext()
    .Enrich.WithProcessName()
    .Enrich.WithThreadId()
    .Enrich.WithAssemblyName()
    .WriteTo.Console(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] " +
                                     "ThreadId: [{ThreadId}] {Message:lj}{NewLine}{Exception}")
    .WriteTo.File(Path.Combine(AppLogDir, $"{appName}-.log"), rollOnFileSizeLimit: true,
        fileSizeLimitBytes: 1048576, retainedFileCountLimit: 30,
        outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] " +
                        "[ThreadId: {ThreadId}] {Message:lj}{NewLine}{Exception}")
    .CreateLogger();

Затем в моем LoginViewModel после успешного входа в систему и спустя долгое время после настройки первоначального регистратора приложений я знаю имя пользователя и могу создать папку для этого пользователя и поместить туда файл журнала:

var userLogDir = Path.Combine(AppDataDir, userName); // <-- userName comes from login token
Directory.CreateDirectory(userLogDir);

А теперь возникает сложный вопрос: как добавить приемник файлов в мою существующую конфигурацию регистратора ??? Мне нужно что-то вроде:

var loggerConfig = Log.GetLoggerConfiguration();  //<--- This is missing, at least I could not find it!
loggerConfig.WriteTo.File(Path.Combine(userLogDir, $"{appName}-{userName}-.log"), rollOnFileSizeLimit: true,
        fileSizeLimitBytes: 10485760, retainedFileCountLimit: 30,
        outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] " +
                        "[ThreadId: {ThreadId}] {Message:lj}{NewLine}{Exception}")

Есть идеи, как это можно сделать с помощью Serilog?


person ThommyB    schedule 23.08.2018    source источник


Ответы (1)


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

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

Заменить существующий регистратор

Этот первый метод полностью заменяет регистратор на новый.

var logfilepath = GetLogFilePath(Assembly.GetEntryAssembly());
var loggerConfig = new LoggerConfiguration()
  .WriteTo.RollingFile(logfilepath);
  
Log.Logger= loggerConfig.CreateLogger();
  
Log.Debug("It works!");


//Somewhere later in the program
Log.Debug("Setting up database with {ConnectionString}", connectionString);


loggerConfig = loggerConfig.WriteTo.MSSqlServer(
    connectionString: "Server=localhost;Database=LogDb;Integrated Security=SSPI;",
    sinkOptions: new MSSqlServerSinkOptions { TableName = "LogEvents" });

Log.Logger = loggerConfig.CreateLogger(); //Replace the old logger with the new one

Создайте новый регистратор и отправьте все события в старый регистратор.

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

Log.Debug("Setting up database with {ConnectionString}", connectionString);

var loggerConfig = new LoggerConfiguration()
  .WriteTo.Sink((ILogEventSink)Log.Logger) //Here we're passing in the old logger
  .WriteTo.MSSqlServer(
    connectionString: "Server=localhost;Database=LogDb;Integrated Security=SSPI;",
    sinkOptions: new MSSqlServerSinkOptions { TableName = "LogEvents" });

Log.Logger = loggerConfig.CreateLogger();

Идти в ногу с конфигурацией

При такой настройке регистратора мы можем просто добавить конфигурацию в контейнер DI как таковой:

IServiceCollection

services.AddSingleton(loggerConfig);

Единство

container.RegisterInstance(loggerConfig);

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

Бонус

В качестве дополнительного бонуса я заметил еще одну вещь: мы также можем загружать приемники из библиотеки DLL во время выполнения (Serilog ›= 3.0). Как указано в этой проблеме, мы можем загружать конфигурацию из сборок во время выполнения время с помощью ConfigurationAssemblySource.

public static LoggerConfiguration Configuration(
    this LoggerSettingsConfiguration settingConfiguration,
    IConfiguration configuration,
    ConfigurationAssemblySource configurationAssemblySource)
{ ... }
person DCCoder    schedule 16.05.2021