在C#项目里使用Serilog输出JSON格式日志并对接ELK日志分析系统,能够有效实现日志的结构化存储和高效检索,大幅降低线上问题的排查成本。ELK系统由Elasticsearch、Logstash、Kibana三部分组成,其中Elasticsearch负责日志存储和检索,Logstash负责日志过滤和转发,Kibana负责日志可视化展示,而Serilog的作用就是生成符合ELK解析要求的JSON格式日志。
Serilog基础环境配置
首先需要在C#项目中安装Serilog相关的NuGet包,核心包包括Serilog和Serilog.Sinks.Console,如果需要输出到文件还需要安装Serilog.Sinks.File,若需要直接输出JSON格式可以安装Serilog.Formatting.Compact包,该包提供了紧凑的JSON格式化器,生成的JSON日志体积更小,更适合ELK系统解析。
安装完成后,需要在程序入口处初始化Serilog日志实例,基础配置代码如下:
using Serilog;
class Program
{
static void Main(string[] args)
{
// 初始化Serilog配置
Log.Logger = new LoggerConfiguration()
// 设置最小日志级别为Information
.MinimumLevel.Information()
// 输出到控制台,使用紧凑JSON格式化器
.WriteTo.Console(new Serilog.Formatting.Compact.CompactJsonFormatter())
// 输出到文件,文件路径为logs/log.json,按天滚动,使用紧凑JSON格式化器
.WriteTo.File(
new Serilog.Formatting.Compact.CompactJsonFormatter(),
"logs/log.json",
rollingInterval: RollingInterval.Day
)
.CreateLogger();
// 测试输出日志
Log.Information("程序启动成功,当前时间:{StartTime}", DateTime.Now);
Log.Warning("这是一条警告级别的日志,示例参数:{Param}", 123);
Log.Error("这是一条错误级别的日志,错误信息:{ErrorMsg}", "数据库连接失败");
// 关闭日志
Log.CloseAndFlush();
}
}
自定义JSON日志字段
默认的Serilog JSON输出已经包含了时间戳、日志级别、消息等基础字段,实际项目中通常需要添加自定义字段,比如应用名称、环境标识、请求ID等,方便在ELK系统中过滤和检索日志。可以通过Enrich配置添加全局自定义属性,也可以在输出单条日志时添加临时属性。
添加自定义字段的示例代码如下:
using Serilog;
using Serilog.Context;
class Program
{
static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
// 添加全局自定义属性:应用名称、运行环境
.Enrich.WithProperty("AppName", "UserCenterService")
.Enrich.WithProperty("Env", "Production")
.WriteTo.Console(new Serilog.Formatting.Compact.CompactJsonFormatter())
.WriteTo.File(
new Serilog.Formatting.Compact.CompactJsonFormatter(),
"logs/log.json",
rollingInterval: RollingInterval.Day
)
.CreateLogger();
// 使用日志上下文添加临时自定义字段:请求ID
using (LogContext.PushProperty("RequestId", Guid.NewGuid().ToString()))
{
Log.Information("处理用户登录请求,用户ID:{UserId}", 1001);
}
Log.CloseAndFlush();
}
}
上述代码输出的JSON日志会包含AppName、Env、RequestId三个自定义字段,ELK系统采集后可以直接通过这些字段进行筛选,比如筛选所有生产环境下UserCenterService应用的日志。
对接ELK日志分析系统
生成JSON格式日志后,需要对接ELK系统完成日志的采集、存储和展示,常见的对接方式有两种:一种是通过Filebeat采集日志文件,转发到Logstash处理后再存入Elasticsearch;另一种是直接通过Serilog输出日志到Elasticsearch,不需要经过Logstash中间处理。
方式一:Filebeat+Logstash对接
首先需要在部署应用的服务器上安装Filebeat,修改Filebeat的配置文件filebeat.yml,指定日志文件的路径,将日志发送到Logstash:
filebeat.inputs:
- type: log
enabled: true
paths:
- /app/logs/*.json
json.keys_under_root: true
json.overwrite_keys: true
output.logstash:
hosts: ["127.0.0.1:5044"]
然后配置Logstash的管道配置文件,接收Filebeat的日志,做简单的过滤后输出到Elasticsearch:
input {
beats {
port => 5044
}
}
filter {
// 解析JSON格式日志
json {
source => "message"
}
// 转换时间戳字段为Elasticsearch支持的时间格式
date {
match => ["@timestamp", "yyyy-MM-ddTHH:mm:ss.ffffffZ"]
target => "@timestamp"
}
}
output {
elasticsearch {
hosts => ["127.0.0.1:9200"]
index => "csharp-logs-%{+yyyy.MM.dd}"
}
}
方式二:Serilog直接输出到Elasticsearch
如果需要简化架构,可以直接安装Serilog.Sinks.Elasticsearch包,让Serilog直接将JSON日志输出到Elasticsearch,不需要Filebeat和Logstash。配置代码如下:
using Serilog;
class Program
{
static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Elasticsearch(new Serilog.Sinks.Elasticsearch.ElasticsearchSinkOptions(new Uri("http://127.0.0.1:9200"))
{
AutoRegisterTemplate = true,
IndexFormat = "csharp-logs-{0:yyyy.MM.dd}",
// 使用紧凑JSON格式化器
CustomFormatter = new Serilog.Formatting.Compact.CompactJsonFormatter()
})
.CreateLogger();
Log.Information("直接输出日志到Elasticsearch测试");
Log.CloseAndFlush();
}
}
常见问题排查
- 如果ELK中看不到日志,首先检查Serilog输出的日志文件是否存在,路径是否正确
- 检查Filebeat和Logstash的连接是否正常,Logstash的监听端口是否和Filebeat配置的一致
- 检查JSON日志格式是否正确,是否有语法错误,可以通过在线JSON校验工具验证日志内容
- 如果自定义字段没有显示,检查Logstash的JSON解析配置是否正确,是否开启了json.keys_under_root配置
通过上述配置,就可以完成C#项目使用Serilog输出JSON日志对接ELK系统的全流程,后续可以在Kibana中创建索引模式,检索和可视化分析所有日志数据,快速定位线上问题。