.Net-Serilog

2023-05-16

目录

1. 快速开始

1.1 控制台应用

1.2 在ASP.NET Core应用中使用Serilog

1.2.1 安装nuget包

1.2.2 修改Program.cs代码

1.2.3 删除appsettings.json里的Logging节点

1.2.4 两步初始化

2. 基本配置

2.1 Sink

2.2 输出模板

2.3 日志等级

2.3.1 日志等级判断

2.3.2 动态日志等级

2.4 不同级别的日志输出到不同的地方

2.5 Enrichers

2.6 根据Enricher的值进行过滤

2.7 Sub-logger

2.8 从配置文件读取配置

3. 结构化数据

4. 消息模板

5. 自定义序列化json


1. 快速开始

1.1 控制台应用

这里以控制台应用为例,首先安装以下三个nuget包:

Serilog
Serilog.Sinks.Console
Serilog.Sinks.File

(可以用命令行方式安装或通过VS安装,随意)
第二个nuget包,用来将日志输出到控制台。第三个用来将日志输出到文件。

然后,修改代码如下:

static void Main()
{
    Log.Logger = new LoggerConfiguration()
        .MinimumLevel.Debug()//最小日志等级
        .WriteTo.Console()//日志打印到控制台
        .WriteTo.File("logs/myapp.txt", rollingInterval: RollingInterval.Day)//日志打印到文件上
        .CreateLogger();
    Log.Information("Hello, world!");
    int a = 10, b = 0;
    try
    {
        Log.Debug("Dividing {A} by {B}", a, b);
        Console.WriteLine(a / b);
    }
    catch (Exception ex)
    {
        Log.Error(ex, "Something went wrong");
    }
    finally
    {
        Log.CloseAndFlush();
    }
}

1.2 在ASP.NET Core应用中使用Serilog

1.2.1 安装nuget包

首先安装Serilog.AspNetCore

1.2.2 修改Program.cs代码

    public static int Main(string[] args)
    {
        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
            .Enrich.FromLogContext()
            .WriteTo.Console()
            .CreateLogger();

        try
        {
            Log.Information("Starting web host");
            CreateHostBuilder(args).Build().Run();
            return 0;
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, "Host terminated unexpectedly");
            return 1;
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }
    
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseSerilog() // 添加Serilog
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });

1.2.3 删除appsettings.json里的Logging节点

删除之后,即可使用

1.2.4 两步初始化

在上面的例子中我们在程序启动时就初始化了Serilog,这样做好处是可以捕获到Host配置的异常,但没法使用appsettings.json和依赖注入。

所以为了能够使用配置文件和依赖注入,Serilog支持第二次初始化(两步初始化)。通过在UseSerilog里配置回调实现:

首先,将CreateLogger改为CreateBootstrapLogger:

public static int Main(string[] args)
    {
        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
            .Enrich.FromLogContext()
            .WriteTo.Console()
            .CreateBootstrapLogger(); // 修改此项

然后,在UserSerilog里配置回调:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseSerilog((context, services, configuration) => configuration
            .ReadFrom.Configuration(context.Configuration)
            .ReadFrom.Services(services)
            .Enrich.FromLogContext()
            .WriteTo.Console())
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

2. 基本配置

2.1 Sink

上面的实例代码的WriteTo.Console()和WriteTo.File(),就是不同的sinker。用来控制把日志写入到哪里。除了控制台和文件系统,你还可以通过安装不同的nuget包把日志写入各种存储系统,如数据库、消息队列、AWS、Azure、邮箱、HTTP等很多地方。

详见Provided-Sinks

2.2 输出模板

.WriteTo.File("log.txt",
        outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")

默认的输出模板就是上面的样子,Timestamp和Level都是内置属性。{Message:lj}表示将消息序列化成json字符串,string类型除外(j表示json,l表示except for string literals)。

{Level:u3}表示将日志等级的输出显示为3个大写字符,如DBG、INF、ERR等。{Level:w3}表示三个字符的小写。

同理,可以增加一个{Properties:j}用来显示额外的上下文信息。

2.3 日志等级


同大多数的日志框架一样,分为Verbose、Debug、Information、Warning、Error和Fatal六个等级。在之前的例子中我们可以看到,使用.MinimumLevel.Debug()配置了最低等级,小于此等级的日志不会被打印出来。

默认日志等级:如果没有配置MinimumLevel的话,默认等级为Information。

2.3.1 日志等级判断


可以通过Log.IsEnabled(LogEventLevel.Debug)来判断某个等级是否启用。

2.3.2 动态日志等级

var levelSwitch = new LoggingLevelSwitch();
levelSwitch.MinimumLevel = LogEventLevel.Warning;
var log = new LoggerConfiguration()
  .MinimumLevel.ControlledBy(levelSwitch)
  .WriteTo.ColoredConsole()
  .CreateLogger();

当需要切换日志等级时,直接修改:

levelSwitch.MinimumLevel = LogEventLevel.Verbose;
log.Verbose("This will now be logged");

2.4 不同级别的日志输出到不同的地方

我们可以通过配置不同sinker的restrictedToMinimumLevel的属性,来让不同级别的日志落到不同的sinker上。

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.File("log.txt")
    .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Information)
    .CreateLogger();

以上代码会将所有的level的日志输出到log.txt上,但只有Information及以上级别的日志会输出到Console上。

看到这里,也许你会问:MinimumLevel.Debug()和sinker里的restrictedToMinimumLevel有什么区别?

其实MinimumLevel.Debug()只是来负责控制哪些级别的日志可以触发WriteTo操作,而restrictedToMinimumLevel只是用来根据级别过滤这些日志。如果MinimumLevel设置为Information,即使restrictedToMinimumLevel设置为Debug,最终也不会看到Debug级别的日志。
 

2.5 Enrichers

输出模板里我们介绍过{Timestamp:yyyy}{Level}都属于Enricher,只不过这些都是框架内置的。我们也可以定义自己的Enricher来打印自定义的内容,如下就是一个需要打印出线程Id的Enricher:

class ThreadIdEnricher : ILogEventEnricher
{
    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(
                "ThreadId", Thread.CurrentThread.ManagedThreadId));
    }
}

使用,通过{ThreadId}

Log.Logger = new LoggerConfiguration()
    .Enrich.With(new ThreadIdEnricher())
    .WriteTo.Console(
        outputTemplate: "{Timestamp:HH:mm} [{Level}] ({ThreadId}) {Message}{NewLine}{Exception}")
    .CreateLogger();

如果你想打印的ThreadId是固定的,就不用定义ThreadIdEnricher类,可以直接这么写:

Log.Logger = new LoggerConfiguration()
    .Enrich.WithProperty("ThreadId","123")
    .WriteTo.Console(
        outputTemplate: "{Timestamp:HH:mm} [{Level}] ({ThreadId}) {Message}{NewLine}{Exception}")
    .CreateLogger();

框架支持的Enricher有以下几种:

名称 nuget包
WithMachineName()WithEnvironmentUserName()Serilog.Enrichers.Environment
WithProcessId()Serilog.Enrichers.Process
WithThreadId()Serilog.Enrichers.Thread
WithHttpRequestId()Serilog.Web.Classic
WithExceptionDetails()Serilog.Exceptions
WithDemystifiedStackTraces()Serilog.Enrichers.Demystify
WithCorrelationId()Serilog.Enrichers.CorrelationId
WithClientIp()WithClientAgent()Serilog.Enrichers.ClientInfo
WithXllPath()Serilog.Enrichers.ExcelDna
WithSensitiveDataMasking()Serilog.Enrichers.Sensitive
FromGlobalLogContext()Serilog.Enrichers.GlobalLogContext

详见

2.6 根据Enricher的值进行过滤

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .Filter.ByExcluding(Matching.WithProperty<int>("Count", p => p < 10))
    .CreateLogger();

Count的值小于10时,不会打印日志。

2.7 Sub-logger

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .WriteTo.Logger(lc => lc
        .Filter.ByIncludingOnly(...)
        .WriteTo.File("log.txt"))
    .CreateLogger();

2.8 从配置文件读取配置

可以通过 Serilog.Settings.AppSettings 或 Serilog.Settings.Configuration 两个nuget包实现。

3. 结构化数据

  1. 对于int、bool、string、Guid、Uri等简单的数据类型,Serilog可以转为字符串输出。
  2. 对于List、Dictionary等集合类型,会自动序列化为json字符串。
  3. 对于复杂对象,可以使用@操作符将对象序列化为json字符串,否则会直接调用ToString()方法。如有以下代码:
var person = new Person { Name = "aa", FirstName = "bb", Id = 5 };
Log.Information("Processing {Person}", person);
Log.Information("Processing {@Person}", person);

输出为

[16:22:33 INF] Processing CalcStringDuplicated.Person
[16:22:33 INF] Processing {"Name": "aa", "FirstName": "bb", "Id": 5, "$type": "Person"}

如何自定义数据的输出结构?

可以使用ByTransforming,比如下述代码我们只需要把Person对象的NameId属性输出:

Log.Logger = new LoggerConfiguration()
    .Destructure.ByTransforming<Person>(
        r => new { Name = r.Name, Id = r.Id })
    .WriteTo...

输出为

[16:27:39 INF] Processing CalcStringDuplicated.Person
[16:27:39 INF] Processing {"Name": "aa", "Id": 5}

强制将object转为string

上面说到对于List等集合类型,Serilog会自动序列化为json字符串,如果不想这么做,可以使用$操作符

var c =new List<int>{10};
Log.Information("value = {$c}", c);

输出为

[16:35:13 INF] value = System.Collections.Generic.List`1[System.Int32]

4. 消息模板

Serilog建议使用消息模板将日志展示出来,而不是直接展示消息。

// 不推荐
Log.Information("The time is " + DateTime.Now);
// 推荐做法
Log.Information("The time is {Now}", DateTime.Now);

这个模板的语法类似于string.format(). 大括号里的属性命名规则同普通属性一致,建议使用Pascal命名,但你也可以随便写。

属性和后面参数对象是根据先后位置对应的,而不是根据名称。

以下代码:

var person = new Person { Name = "aa", FirstName = "bb", Id = 5 };
var person2 = new Person { Name = "aa2", FirstName = "22", Id = 5 };
Log.Information("Processing {@Person2}---{@Person}",person, person2);
Log.Information("Processing {@person}---{@person2}", person, person2);
Log.Information("Processing {@person2}---{@person}", person, person2);
Log.Information("Processing {@a}---{@b}", person, person2);

输出结果都一样:

[17:21:58 INF] Processing {"Name": "aa", "Id": 5}---{"Name": "aa2", "Id": 5}
[17:21:58 INF] Processing {"Name": "aa", "Id": 5}---{"Name": "aa2", "Id": 5}
[17:21:58 INF] Processing {"Name": "aa", "Id": 5}---{"Name": "aa2", "Id": 5}
[17:21:58 INF] Processing {"Name": "aa", "Id": 5}---{"Name": "aa2", "Id": 5}

消息模板也支持类似string.format{0}{1}。如:

Log.Information("Processing {@1}---{@0}", person, person2);

输出为:

[17:24:07 INF] Processing {"Name": "aa2", "Id": 5}---{"Name": "aa", "Id": 5}

消息模板详见

5. 自定义序列化json

Serilog自带了三个json序列化程序:

  • Serilog.Formatting.Json.JsonFormatter- 这是Serilog软件包中附带的历史默认值。它生成日志事件的完整呈现,并支持一些配置选项。
  • Serilog.Formatting.Compact.CompactJsonFormatter- 存在于Serilog.Formatting.Compactnuget包,提供了更节省空间的 JSON 格式化程序。
  • Serilog.Formatting.Compact.RenderedCompactJsonFormatter- 同样附带在Serilog.Formatting.Compact包中,这个格式化程序将消息模板预渲染成文本。
     

自定义formater:

class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime Created { get; set; }
}

class CustomDateFormatter : IFormatProvider
{
    readonly IFormatProvider basedOn;
    readonly string shortDatePattern;
    public CustomDateFormatter(string shortDatePattern, IFormatProvider basedOn)
    {
        this.shortDatePattern = shortDatePattern;
        this.basedOn = basedOn;
    }
    public object GetFormat(Type formatType)
    {
        if (formatType == typeof(DateTimeFormatInfo))
        {
            var basedOnFormatInfo = (DateTimeFormatInfo)basedOn.GetFormat(formatType);
            var dateFormatInfo = (DateTimeFormatInfo)basedOnFormatInfo.Clone();
            dateFormatInfo.ShortDatePattern = this.shortDatePattern;
            return dateFormatInfo;
        }
        return this.basedOn.GetFormat(formatType);
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var formatter = new CustomDateFormatter("dd-MMM-yyyy", new CultureInfo("en-AU"));
        Log.Logger = new LoggerConfiguration() 
            .WriteTo.Console(formatProvider: new CultureInfo("en-AU")) // Console 1
            .WriteTo.Console(formatProvider: formatter)                // Console 2
            .CreateLogger();

        var exampleUser = new User { Id = 1, Name = "Adam", Created = DateTime.Now };
        Log.Information("Created {@User} on {Created}", exampleUser, DateTime.Now);

        Log.CloseAndFlush();
    }
}

输出

[13:57:12 INF] Created {"Id": 1, "Name": "Adam", "Created": "2020-09-01T13:56:59.7803740-05:00", "$type": "User"} on 1/09/2020 1:57:12 PM
[13:57:12 INF] Created {"Id": 1, "Name": "Adam", "Created": "2020-09-01T13:56:59.7803740-05:00", "$type": "User"} on 01-Sep-2020 1:57:12 PM

转载自 Serilog 2.10 中文文档_catshitone的专栏-CSDN博客_serilog中文文档

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

.Net-Serilog 的相关文章

  • UdpClient 在多个侦听器上接收数据

    我有一个实现自动发现机制的应用程序 但我遇到了 UdpClient 问题 只要应用程序的单个实例打开 它就可以正常工作 然而 当第二个实例打开时 只有第一个实例接收单播数据包 有趣的是 实现相同机制的类似应用程序似乎没有这个问题 有什么建议
  • 向 Serilog 添加自定义属性

    我在应用程序中将 Serilog 与 MS SQL Server 接收器一起使用 假设我已经定义了以下类 public class Person public string FirstName get set public string L
  • 使用“Openxml writer”合并 Excel 中的单元格

    我想合并单元格是excel 通过使用 DOM 方法 我可以轻松做到这一点 但由于我的 Excel 文件太大 当我尝试获取工作表时 它会抛出内存不足异常 所以我必须使用SAX方法来读取excel文件 但我不知道如何用这种方法合并单元格 查了很
  • ASP.NET Core 应用程序生命周期

    目前是否有关于生命周期的 ASP NET Core 文档 我希望能够在正确的点融入生命周期 它与现有的 ASP NET MVC 5 生命周期相似吗 http www asp azureedge net v 2016 09 01 001 me
  • NUnit 测试项目的 log4net 配置部分

    我正在使用名为 AssemblyTest nunit 的项目运行 NUnit 该测试调用另一个使用 log4net 程序集的程序集 这是使用 nunit 版本 2 4 3 和 net 2 0 框架 在 TestFixtureSetup 中
  • 为什么在第一次调用类方法之前不调用静态构造函数

    根据乔恩 斯基特的文章C 和 beforefieldinit http csharpindepth com Articles General Beforefieldinit aspx和讨论C 中何时调用静态构造函数 https stacko
  • 在线找到具有两个不同参数的多个匹配绑定

    我在同一个功能文件中写了两行 何时 When user action1 key1 with value1 for atttributeType Value in Filename1 SectionId1 Then abc When user
  • 基于 Microsoft Word 模板生成 PDF 文档 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我需要一个 Word 文档 它是某种模板 收集用户输入以填充该模板中的特定字段 然后生成一个 PDF
  • 以编程方式获取命名管道的系统名称

    我正在使用 WCF NetNamedPipeBinding 编写进程间通信 我的目标是让服务在 net pipe localhost service 上运行 所以我运行最简单的主机 host new ServiceHost contract
  • .NET - MVC 应用程序外部的 Razor - 删除 @inherits 和提供 @model 时出现问题

    抱歉问了这么长的问题 我把它分成了三个问题 可以分别阅读 如果你能帮我解决一个问题 请帮我 我有一个 Razor 引擎的自定义实现 所有作品和模板均已编译并可以使用 手头有一些实现涉及具有通用的基类Model允许强类型视图 模板 的属性 此
  • Swashbuckle 在 ASP.NET Core 中失败并出现 NotSupportedException 异常

    我跟着这个关于如何在我的 asp net core 2 2 项目中添加 swashbuckle 当我运行该项目时 我收到以下错误 处理请求时发生未处理的异常 NotSupportedException HTTP 方法 GET 和路径 id
  • Visual Studio 扩展找不到所需的程序集

    我为 Visual Studio 2013 编写了一个扩展 因为该死的组合框错误 https stackoverflow com questions 7800032 cancel combobox selection in wpf with
  • 从 .resx 文件组获取所有可用区域性

    我需要以编程方式列出 resx 文件组中的可用区域性 但 ResourceManager 类似乎没有帮助 我可能有 Labels resx Labels fr FR resx Labels ro RO resx 等等 但是 我如何在运行时找
  • 如何通过 COM 将长数组从 VB6 传递到 C#

    我需要将 int 或 long 数组 无关紧要 从 VB6 应用程序传递到 C COM Visible 类 我尝试在 C 中声明接口 如下所示 void Subscribe MarshalAs UnmanagedType SafeArray
  • 如何使用 vb.net 将数据插入 Access 表?

    我想在 Access 数据库中插入一个新行 我正在考虑做类似的事情 oConnection new Connection connectionstring oTable oCennection table Orders oRow oTabl
  • C# 创建包含多个文件的 ZIP 存档

    我正在尝试创建包含多个文本文件的 ZIP 存档 如下所示 Dictionary
  • 在 Javascript 中本地化字符串

    我目前正在使用 resx文件来管理我的 NET 服务器端资源 我正在处理的应用程序还允许开发人员将 JavaScript 插入各种事件处理程序中以进行客户端验证等 对我来说本地化 JavaScript 消息和字符串的最佳方法是什么 理想情况
  • 如何将pdf页面设置设置为打印属性对话框?

    大家好 我想知道如何设置 pdf 页面设置到打印属性对话框 例如 如果我的 PDF 页面设置为横向 则布局会自动显示横向而不是纵向 如果我的 PDF 页面设置为纵向 则布局会自动显示纵向 我在这个主题上做了很多研发 但没有找到任何满意的链接
  • 是什么原因导致 Linq 错误:此方法无法转换为存储表达式?

    我有一堆具有相同 select 语句的 Linq to Entity 方法 所以我想我会很聪明 并将其分离到它自己的方法中以减少冗余 但是当我尝试运行代码时 我得到了以下内容错误 该方法不能转化为 商店表达式 这是我创建的方法 public
  • 如何将 CSV 文件读入 .NET 数据表

    如何将 CSV 文件加载到System Data DataTable 根据CSV文件创建数据表 常规 ADO net 功能是否允许这样做 我一直在使用OleDb提供者 但是 如果您正在读取具有数值的行 但希望将它们视为文本 则会出现问题 但

随机推荐