Serilog - 使用 AppSettings Config 如何配置子记录器并仅包含某些命名空间?

Serilog - With AppSettings Config how do I configure Sub Loggers & Include Certain Namespaces only?


我正在尝试在 CMS 中设置 Serilog,该 CMS 具有一些我们定义为 CMS 的默认日志记录配置设置,但是允许使用 CMS 的开发人员使用 Serilog AppSettings Nuget 包扩展和配置自己的日志记录要求 - https://github.com/serilog/serilog-settings-appsettings

我有一些这样的工作并且能够在外部配置文件中配置其他接收器我遇到并需要帮助的问题是如何让开发人员配置文件接收器以生成仅包含他们的 txt logile命名空间?

使用 C# class 我知道我可以创建一个子记录器,然后像这样使用过滤器 .Filter.ByIncludingOnly(Matching.FromSource("DevelopersNamespace")) 但使用 Serilog Analyzer VS Extension - https://github.com/Suchiman/SerilogAnalyzer 它无法生成示例 XML AppSettings 配置。

这是我在 C# 中的记录器配置的副本

Serilog.Debugging.SelfLog.Enable(msg => System.Diagnostics.Debug.WriteLine(msg));

//Set this environment variable - so that it can be used in external config file
//add key="serilog:write-to:RollingFile.pathFormat" value="%BASEDIR%\logs\log-{Date}.txt" />
Environment.SetEnvironmentVariable("BASEDIR", AppDomain.CurrentDomain.BaseDirectory, EnvironmentVariableTarget.Process);

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug() //Set to highest level of logging (as any sinks may want to restrict it to Errors only)
    .Enrich.WithProcessId()
    .Enrich.WithProcessName()
    .Enrich.WithThreadId()
    .Enrich.WithProperty("AppDomainId", AppDomain.CurrentDomain.Id)
    .Enrich.WithProperty("AppDomainAppId", HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty))
    .Enrich.With<Log4NetLevelMapperEnricher>()

    //Main .txt logfile - in similar format to older Log4Net output
    //Ends with ..txt as Date is inserted before file extension substring
    .WriteTo.File($@"{AppDomain.CurrentDomain.BaseDirectory}\App_Data\Logs\UmbracoTraceLog.{Environment.MachineName}..txt",
        rollingInterval: RollingInterval.Day,
        restrictedToMinimumLevel: LogEventLevel.Debug,
        retainedFileCountLimit: null, //Setting to null means we keep all files - default is 31 days
        outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss,fff} [P{ProcessId}/D{AppDomainId}/T{ThreadId}] {Log4NetLevel}  {SourceContext} - {Message:lj}{NewLine}{Exception}")

    //.clef format (Compact log event format, that can be imported into local SEQ & will make searching/filtering logs easier)
    //Ends with ..txt as Date is inserted before file extension substring
    .WriteTo.File(new CompactJsonFormatter(), $@"{AppDomain.CurrentDomain.BaseDirectory}\App_Data\Logs\UmbracoTraceLog.{Environment.MachineName}..json", 
        rollingInterval: RollingInterval.Day, //Create a new JSON file every day
        retainedFileCountLimit: null, //Setting to null means we keep all files - default is 31 days
        restrictedToMinimumLevel: LogEventLevel.Debug)

    //Read any custom user configuration of logging from serilog config file
    .ReadFrom.AppSettings(filePath: AppDomain.CurrentDomain.BaseDirectory + @"\config\serilog.config")
    .CreateLogger();

这是 AppSettings 配置文件的示例,用户可以使用该文件修改自己的接收器。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
        <!-- Controls log levels for all sinks (Set this higher than child sinks) -->
        <add key="serilog:minimum-level" value="Verbose" />

        <!-- Write to a user log file -->
        <add key="serilog:using:File" value="Serilog.Sinks.File" />
        <add key="serilog:write-to:File.path" value="%BASEDIR%\logs\warren-log.txt" /><!-- Can we do a relative path to website ? -->
        <add key="serilog:write-to:File.restrictedToMinimumLevel" value="Debug" /> 
        <add key="serilog:write-to:File.retainedFileCountLimit" value="32" /> <!-- Number of log files to keep (or remove value to keep all files) -->
        <add key="serilog:write-to:File.rollingInterval" value="Day" /> <!-- Create a new log file every Minute/Hour/Day/Month/Year/infinite -->

        <!-- TODO: How do I filter the file sink for customer to their own namespace ?? -->

    </appSettings>
</configuration>

我愿意接受关于如何实现这一目标的想法和建议,目标是允许开发人员配置他们自己的接收器,并根据需要选择过滤到他们自己的命名空间(因为我怀疑用户会想写他们自己的接收器代码)

对于任何有兴趣或以后会遇到这个问题的人 post 这就是我解决它的方法。

我使用了两个配置文件,一个用于配置主日志记录管道,一个用于子记录器的用户配置,如果需要,他们可以在不影响主日志记录管道的情况下使用过滤。

Serilog.Debugging.SelfLog.Enable(msg => System.Diagnostics.Debug.WriteLine(msg));

//Set this environment variable - so that it can be used in external config file
//add key="serilog:write-to:RollingFile.pathFormat" value="%BASEDIR%\logs\log.txt" />
Environment.SetEnvironmentVariable("BASEDIR", AppDomain.CurrentDomain.BaseDirectory, EnvironmentVariableTarget.Process);

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Verbose() //Set to highest level of logging (as any sinks may want to restrict it to Errors only)
    .Enrich.WithProcessId()
    .Enrich.WithProcessName()
    .Enrich.WithThreadId()
    .Enrich.WithProperty("AppDomainId", AppDomain.CurrentDomain.Id)
    .Enrich.WithProperty("AppDomainAppId", HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty))
    .Enrich.With<Log4NetLevelMapperEnricher>()

    //Main .txt logfile - in similar format to older Log4Net output
    //Ends with ..txt as Date is inserted before file extension substring
    .WriteTo.File($@"{AppDomain.CurrentDomain.BaseDirectory}\App_Data\Logs\UmbracoTraceLog.{Environment.MachineName}..txt",
        rollingInterval: RollingInterval.Day,
        restrictedToMinimumLevel: LogEventLevel.Verbose,
        retainedFileCountLimit: null, //Setting to null means we keep all files - default is 31 days
        outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss,fff} [P{ProcessId}/D{AppDomainId}/T{ThreadId}] {Log4NetLevel}  {SourceContext} - {Message:lj}{NewLine}{Exception}")

    //.clef format (Compact log event format, that can be imported into local SEQ & will make searching/filtering logs easier)
    //Ends with ..txt as Date is inserted before file extension substring
    .WriteTo.File(new CompactJsonFormatter(), $@"{AppDomain.CurrentDomain.BaseDirectory}\App_Data\Logs\UmbracoTraceLog.{Environment.MachineName}..json", 
        rollingInterval: RollingInterval.Day, //Create a new JSON file every day
        retainedFileCountLimit: null, //Setting to null means we keep all files - default is 31 days
        restrictedToMinimumLevel: LogEventLevel.Verbose)

    //Read from main serilog.config file
    .ReadFrom.AppSettings(filePath: AppDomain.CurrentDomain.BaseDirectory + @"\config\serilog.config")

    //A nested logger - where any user configured sinks via config can not effect the main 'umbraco' logger above
    .WriteTo.Logger(cfg =>
        cfg.ReadFrom.AppSettings(filePath: AppDomain.CurrentDomain.BaseDirectory + @"\config\serilog.user.config"))
    .CreateLogger();

下面是两个配置文件的示例:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>

        <!-- Used to toggle the loge levels for the main Umbraco log files -->
        <!-- Found at /app_data/logs/ -->
        <!-- NOTE: Changing this will also flow down into serilog.user.config -->
        <add key="serilog:minimum-level" value="Verbose" />

        <!-- To write to new log locations (aka Sinks) such as your own .txt files, ELMAH.io, Elastic, SEQ -->
        <!-- Please use the serilog.user.config file to configure your own logging needs -->

    </appSettings>
</configuration>

这是配置文件,用户可以在其中使用自己的命名空间进行过滤:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>

        <!-- Controls log levels for all user-definied child sub-logger sinks configured here (Set this higher than child sinks) -->
        <add key="serilog:minimum-level" value="Verbose" />

        <!-- For Different Namespaces - Set different logging levels -->
        <add key="serilog:minimum-level:override:Microsoft" value="Warning" />
        <add key="serilog:minimum-level:override:Microsoft.AspNetCore.Mvc" value="Error" />
        <add key="serilog:minimum-level:override:YourNameSpace" value="Information" />

        <!-- All logs definied via user.config will contain this property (won't be in main Umbraco logs) -->
        <add key="serilog:enrich:with-property:websiteName" value="Warrens Website" />

        <!-- Write to a user log file -->
        <add key="serilog:using:File" value="Serilog.Sinks.File" />
        <add key="serilog:write-to:File.path" value="%BASEDIR%\logs\warren-log.txt" />
        <add key="serilog:write-to:File.restrictedToMinimumLevel" value="Debug" /> <!-- I will be ignored as Debug as the user logging pipleine has it min set to Information, so only Info will flow through me -->
        <add key="serilog:write-to:File.retainedFileCountLimit" value="32" /> <!-- Number of log files to keep (or remove value to keep all files) -->
        <add key="serilog:write-to:File.rollingInterval" value="Day" /> <!-- Create a new log file every Minute/Hour/Day/Month/Year/infinite -->

        <!-- Filters all above sink's to use this expression -->
        <!-- Common use case is to include SourceType starting with your own namespace -->
        <add key="serilog:using:FilterExpressions" value="Serilog.Filters.Expressions" />
        <add key="serilog:filter:ByIncluding.expression" value="StartsWith(SourceContext, 'MyNamespace')" />

    </appSettings>
</configuration>