Serilog 日志库简单实践(二):控制台与调试 Sinks(.net8)

〇、前言

前文已经介绍过什么是 Serilog,以及其核心特点,详见:https://www.cnblogs.com/hnzhengfy/p/19167414/Serilog_basic

本文继续对各种类型的 Sink 进行简单的实践,主题是控制台与调试 Sink,供参考。

一、控制台与调试 Sinks 用法

1.1 Serilog.Sinks.Console:将日志优雅地输出到控制台

Serilog.Sinks.Console 不仅仅是一个简单的控制台输出工具,它是一个功能强大且灵活的日志接收器。支持纯文本(默认)、着色文本(区分日志级别)、JSON 格式(保留结构化数据)

通过可配置的 outputTemplate,它能够在保持可读性的同时完美保留日志的结构化信息 (通过 JSON 格式或内联 JSON 属性,保存事件 ID、自定义字段等)。这种"双重能力"使得开发者可以在本地清晰地看到日志,而运维和监控系统可以通过解析 JSON 部分,实现高效的日志搜索、分析和告警,极大地提升了日志的价值和可维护性。

另外,直接输出到控制台,无额外依赖,性能开销低

关键配置参数:

outputTemplate:自定义日志输出模板,支持占位符(如 {Timestamp}、{Level}、{Message} 等)。

theme:控制控制台颜色主题,可选值:

  • AnsiConsoleTheme.Literate(默认,带颜色)
  • AnsiConsoleTheme.None(无颜色)
  • 自定义主题(如 AnsiConsoleTheme.Code)。

formatter:指定日志格式化器,默认使用文本格式化器,可替换为 JsonFormatter 输出 JSON。

MinimumLevel:设置最小日志级别(如只输出 Warning 及以上级别)。

1.1.1 创建项目并安装依赖包

创建一个 .net8 版本的控制台应用程序或者 WebAPI 程序,然后安装必要的动态库。

复制代码
dotnet add package Serilog.Sinks.Console

1.1.2 修改 Program.cs

复制代码
using Serilog;

// 创建日志记录器,演示三种不同的 Console Sinks 配置
// 1. 纯文本格式(默认)
var plainTextLogger = new LoggerConfiguration()
    .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
    .CreateLogger();

// 2. 带颜色的控制台输出
var coloredLogger = new LoggerConfiguration()
    .WriteTo.Console(
        outputTemplate: "[{Timestamp:HH:mm:ss} {Level:w3}] {Message:lj} {Properties:j}{NewLine}{Exception}",
        theme: Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme.Literate)
    .CreateLogger();

// 3. JSON 格式输出(保留结构化数据)
var jsonLogger = new LoggerConfiguration()
    .WriteTo.Console(formatter: new Serilog.Formatting.Json.JsonFormatter())
    .CreateLogger();

// 设置全局日志器(可选)
Log.Logger = coloredLogger;

try
{
    // 使用不同 logger 输出示例日志
    Console.WriteLine("=== 纯文本格式 ===");
    plainTextLogger.Information("应用程序启动。");
    plainTextLogger.Warning("这是一个警告,用户 ID: {UserId}, 操作: {Action}", 123, "Login");

    Console.WriteLine("\n=== 彩色控制台输出 ===");
    coloredLogger.Information("用户 {User} 成功登录,IP: {IP}", "alice", "192.168.1.10");
    coloredLogger.Error(new InvalidOperationException("无效操作"), "处理请求时出错");

    Console.WriteLine("\n=== JSON 格式输出 ===");
    jsonLogger.Information("记录结构化事件,Url: {Url}, StatusCode: {StatusCode}", "/api/values", 200);
    jsonLogger.Debug("调试信息:耗时 {DurationMs} ms", 45.7);

    Console.WriteLine("\n=== 结构化日志的优势演示 ===");
    var user = new { Id = 456, Name = "Bob", Roles = new[] { "Admin", "User" } };
    coloredLogger.Information("新用户注册:{@User}", user); // {@User} 表示对象结构化输出

    Console.WriteLine("\n按任意键退出...");
    Console.ReadKey();
}
catch (Exception ex)
{
    Log.Fatal(ex, "应用程序发生未处理异常");
}
finally
{
    Log.CloseAndFlush(); // 确保日志写入完成
}

1.1.3 查看效果

如下图,可见不同类型的值,会有不同的颜色,查看 json 字符串的值也比较清晰:

1.2 通过 Serilog.Sinks.Console 加自定义 json 格式化工具类输出易读格式

注意性能影响,美化 JSON(尤其是 Formatting.Indented)会增加 CPU 开销,不建议在生产环境高频率日志中使用,适合开发/调试环境。

依赖 Newtonsoft.Json,虽然 .NET Core 有 System.Text.Json,但 JObject.ToString(Formatting.Indented) 更方便。

每条日志都需要是一个独立 JSON 对象,而非整个文件是 JSON 数组。

下面是一个简单实现:

先定义一个 json 格式化工具类 BeautifiedJsonFormatter.cs:

复制代码
using Newtonsoft.Json;
using Serilog.Events;
using Serilog.Formatting;
using Serilog.Formatting.Compact;
using System.Text;

namespace Test.WebAPI.Serilog.WriteToFile
{
    public class BeautifiedJsonFormatter : ITextFormatter
    {
        private readonly RenderedCompactJsonFormatter _innerFormatter = new RenderedCompactJsonFormatter();
        public void Format(LogEvent logEvent, TextWriter output)
        {
            // 先用 CompactJsonFormatter 写入 StringBuilder
            var sb = new StringBuilder();
            var stringWriter = new StringWriter(sb);
            _innerFormatter.Format(logEvent, stringWriter);
            // 解析为 JObject 并重新格式化为美化 JSON
            var json = sb.ToString().Trim(); // 去掉空白
            if (string.IsNullOrEmpty(json)) 
                return;            
            var obj = Newtonsoft.Json.Linq.JObject.Parse(json);
            var prettyJson = obj.ToString(Formatting.Indented); // 美化输出(带缩进)
            output.WriteLine(prettyJson); // 写入输出流并换行
        }
    }
}

测试一下:

复制代码
using Serilog;
using System.Text;
using Test.WebAPI.Serilog.WriteToFile;

// 创建一个自定义的 TextWriter,用于在写入控制台前美化 JSON
var writer = new StreamWriter(Console.OpenStandardOutput())
{
    AutoFlush = true,  
    //Encoding = Encoding.UTF8
};
// 配置 Serilog
Log.Logger = new LoggerConfiguration()
    .WriteTo.Console(
        formatter: new BeautifiedJsonFormatter(), // 使用自定义美化格式化器
        standardErrorFromLevel: null) // 所有级别都输出到 stdout
    .CreateLogger();
// 示例日志
Log.Information("Hello {Name}! Today is {Date:yyyy-MM-dd}.", "Alice", DateTime.Now);
Log.Warning("Something went wrong with user {@User}", new { Id = 123, Name = "Bob" });
Log.Error(new InvalidOperationException("Test exception"), "An error occurred");

Log.CloseAndFlush();

输出的格式化 json 如下图:

1.3 Serilog.Sinks.Debug:将日志写入到 Visual Studio 的"输出"窗口,主要用于开发和调试

Serilog.Sinks.Debug 是 Serilog 的一个官方接收器(Sink),它将日志事件写入到 Visual Studio 的"输出"窗口 (Output Window)中。这个功能主要用于开发和调试阶段 ,为开发者提供了一种高效、便捷的日志查看方式

核心特点:

开发调试友好: 无需额外配置或打开其他日志查看工具。日志实时显示在 VS 的"输出"窗口中,无需中断程序执行。
环境隔离: 仅在开发环境中启用,不会影响生产环境的性能。可以轻松地在配置中启用/禁用,无需修改代码。
结构化日志: 保持 Serilog 的结构化日志优势。以可读的格式显示日志,包含时间戳、日志级别、消息和属性。
**与 Serilog 生态无缝集成:**与其他 Serilog 接收器(如控制台、文件)可以同时使用。保持 Serilog 统一的 API 和配置风格。

在日常开发中,Serilog.Sinks.Debug 可以做到避免干扰,与 Console.WriteLine 不同,Serilog.Sinks.Debug 不会干扰控制台输出,不会使控制台输出混乱。

还可以方便日志级别控制,可以设置不同的日志级别(如 Debug、Information、Warning),只显示需要的调试信息。

还能方便的查看上下文信息,保留 Serilog 的结构化日志特性,包括参数化消息和上下文属性。

更主要的是可以专业调试,比简单的 Debug.WriteLine 更强大、更灵活,是专业开发的首选。

下边是一个简单的示例。

安装必要的包:

复制代码
dotnet add package Serilog.Sinks.Debug

修改 Program.cs:

复制代码
using Serilog;
using System;

namespace SerilogDebugExample
{
    class Program
    {
        static void Main(string[] args)
        {
            // 配置 Serilog,将日志写入到 Visual Studio 的输出窗口
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug() // 设置最小日志级别为 Debug
                .WriteTo.Debug() // 关键:将日志写入到 VS 输出窗口
                .CreateLogger();

            try
            {
                Log.Information("应用程序启动");
                // 模拟一些操作
                Log.Debug("正在处理用户请求");
                Log.Warning("库存不足,商品 {ProductId}", 12345);
                Log.Error(new Exception("模拟的异常"), "处理订单时发生错误,订单ID: {OrderId}", "ORD12345");
                Log.Information("应用程序正常关闭");
            }
            finally
            {
                Log.CloseAndFlush(); // 确保所有日志已写入
            }
        }
    }
}

然后,打开输出窗口(视图-->输出,快捷方式 Alt+2)。

然后运行项目,查看输出窗口:

相关推荐
智驭未来掌门人25 分钟前
利用FFmpeg快速实现一个RTSP推流服务器
后端
垚垚学技术_聚焦云原生38 分钟前
K8s Pod 完整生命周期详解
后端
JavaGuide42 分钟前
Github 史诗级故障,与此同时,Cursor 版「GitHub」正式上线!
前端·后端
不一样的少年_1 小时前
修了 Bug、做了重构,为什么老板还是觉得你没产出?
前端·后端·程序员
水深火乐1 小时前
安全地生成验证码、密码和 Token
后端
小强19881 小时前
SQL Server 慢查询怎么定位?一套从“卡”到“快”的完整排查流程
后端
水深火乐1 小时前
interface和any
后端
步行cgn1 小时前
MyBatis Error evaluating expression ‘ids‘. Return value (3) was not iterable 错误详
java·后端
神奇小汤圆1 小时前
Spring Boot + LangChain4j实现RAG——从零搭建企业级知识库问答系统
后端
H_Peak1 小时前
Python运算符与流程控制:if条件判断
后端