.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 的相关文章

  • win32 API 和 .NET 框架之间的选择

    我必须开发一个适用于 Windows 的应用程序 该应用程序将能够通过网络摄像头识别手势来控制鼠标 我将使用 vc 2008 进行开发 但我很困惑是使用 NET 框架还是核心 win32 API 性能对于我的应用程序非常重要 根据 Ivor
  • 将小数格式化为两位或整数

    对于 10 我想要 10 而不是 10 00 对于 10 11 我想要 10 11 没有代码可以实现吗 即通过指定格式字符串类似于 0 N2 decimal num 10 11M Console WriteLine num ToString
  • ASP.NET - 在 GridView 中显示图像和 pdf

    我想在 asp GridView 中显示 图像 列 这个想法是提供图像的缩略图以及实际尺寸图像的链接 对于某些行 这也可以是 PDF 文档 我想要 PDF 的链接 PDF 或图像存储在 SQL 数据库中 现在我在处理程序 ashx 文件中出
  • 字符串与 StringBuilder

    我理解之间的区别String and StringBuilder StringBuilder是可变的 但是两者之间有很大的性能差异吗 我正在开发的程序有很多大小写驱动的字符串附加 500 正在使用StringBuilder更好的选择 是的
  • 用于 mvc3 日期格式和日期验证的文本框

    我决定开始使用 MVC 3 并且在尝试将我的一个 Web 应用程序重做为 MVC3 时遇到了这个问题 我的项目是这样设置的 public class Project public int ProjectID get set Required
  • 发生错误。", ExceptionMessage: "提供的 'HttpContent' 实例无效

    尝试将文件添加到 http 休息调用时出现此错误 responseJson 消息 发生错误 ExceptionMessage 提供了无效的 HttpContent 实例 它确实 正在使用 多部分 参数名称 内容 异常类型 System Ar
  • 使用32位应用程序获取syswow64目录

    我正在尝试在系统目录中查找文件 问题是当使用 Environment SystemDirectory 在 x64 计算机上 我仍然获得 System32 目录 而不是 Systemwow64 目录 我需要在 x86 机器上获取 System
  • 将参数从 Web 表单传递到 Crystal 报表

    我有一份报告 我想将其显示在网络表单上 没有参数的报告运行良好 带参数的报告让我很头疼 这是我在 BindReport 方法中编写的代码 该代码在表单的页面加载事件上调用 ReportDocument rpt new ReportDocum
  • 在 C# 中将 ulong 映射到 long ?

    我正在尝试将 ulong 映射到 long 反之亦然 将 uint 映射到 int 反之亦然 如下所示 为了将值保存在具有签名类型的 MS SQL 数据库中仅限整数和大整数 我这样做是因为我必须检查 在数据库中 一个数字 uint ulon
  • 父窗体中的居中消息框[重复]

    这个问题在这里已经有答案了 有没有一种简单的方法可以在 net 2 0中将MessageBox居中于父窗体中 我在 C 中确实需要这个并发现中心消息框 C http bytes com topic c sharp answers 26712
  • 使用 LINQ to SQL 的 .NET 架构的最佳设计实践(DAL 必要吗?我们真的可以使用 POCO吗?要采用的设计模式吗?)

    我避免在 net arch n 层架构上编写看起来像是另一个线程的内容 但请耐心等待 希望我和其他人一样 在选择用于企业应用程序的架构时 考虑到当今的趋势和新兴技术 仍然没有 100 满意或不清楚应采取的最佳方法 我想我正在寻求大众社区对方
  • 如果未返回,则在一段时间后终止线程

    我有一个线程从网络或串行端口获取一些数据 如果 5 秒内没有收到数据 则线程必须终止 或返回 false 换句话说 如果线程运行时间超过 5 秒 则必须停止 我用 C 编写 但任何 NET 语言都可以 有两种方法 1 封装超时 从网络或串行
  • 中继命令和无参数执行方法

    我目前正在学习 WPF 和 MVVM 我想我已经了解了大部分内容及其工作原理 但我在使用 RelayCommand 或 DelegateCommand 时遇到了一些我不明白的问题 我认为这与代表的工作方式有关 请注意 下面的代码目前仅处于测
  • 何时使用 IComparable 与何时使用IComparer

    我正在尝试找出我需要实现哪些接口 他们本质上都做同样的事情 我什么时候会使用其中一种而不是另一种 好吧 他们不是quite与IComparer
  • StreamReader 消耗的字节数

    有没有办法知道 StreamReader 使用了流的多少字节 我有一个项目 我们需要读取一个文件 该文件具有文本标题 后跟二进制数据的开头 我最初尝试读取该文件是这样的 private int dataOffset void ReadHea
  • 查询 dns 别名

    我找到了一些code http msdn microsoft com en us library system net dns gethostbyaddress VS 71 aspx来自 msdn 站点 下面包含代码 看起来它将返回给定服务
  • 将 C# 代码转换为 F#(if 语句)

    我想知道如何转换此代码逐行从 C 到 F 我不想使用任何类型的 F 习惯用法或类似的东西 我想了解如何直接映射C 的构造到 F 这是 C 代码 requires l Length gt 0 int GetMinimumValue List
  • JavaScript:根据文化格式化数字/货币,例如 .NET String.Format()?

    这似乎是一个有人在 2009 年就已经解决的问题 但我找不到一个像样的 JavaScript 库 它需要这样的数字 12345 58 并根据特定区域性 即 nl nl 或 ISO 货币代码正确设置其格式 dojo 表现出了希望 但它默认不包
  • .NET 迭代器包装抛出 API

    我有一个带有 API 的类 它允许我请求对象 直到它抛出一个IndexOutOfBoundsException 我想将它包装到一个迭代器中 以便能够编写更清晰的代码 但是 我需要捕获异常以停止迭代 static IEnumerable It
  • 为什么文件更新时“如果较新则复制”不复制文件?

    我在 Visual Studio Express 中有一个解决方案 如下所示 The LogicSchemaC 中的类 将在运行时解析指定的 XML 文件 以下是在main的方法Program cs LogicSchema ls new L

随机推荐