Serilog 发送日志到 Logstash 应该用哪个 Sink?
推荐 Serilog.Sinks.Network 的 TCP sink 配合 JSON 格式化器发送到 Logstash 的 tcp 输入;也可用 HTTP sink 或先写文件再由采集器转发。本文给出配置示例。
推荐方案:用 Serilog.Sinks.Network 包的 TCP sink,搭配 JSON 格式化器,把结构化日志直接发到 Logstash 的 tcp 输入端口——网络直发省去文件中转,TCP 比 UDP 可靠;如果对投递可靠性要求高,更稳妥的做法是写本地 JSON 文件,由采集代理转发。
方案一:TCP 直发
安装包:
dotnet add package Serilog.Sinks.Network
dotnet add package Serilog.Sinks.Console
配置 Serilog:
using Serilog;
using Serilog.Sinks.Network;
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.TCPSink("tcp://logstash-host:5044", new Serilog.Formatting.Json.JsonFormatter())
.CreateLogger();
Log.Information("用户 {UserId} 登录成功", 42);
Logstash 侧对应输入:
input {
tcp {
port => 5044
codec => json_lines
}
}
JsonFormatter 保留结构化字段(如 Properties.UserId);Logstash 还需配置输出和匹配的字段映射,JSON 格式本身不保证已写入 ES。5044 在此是 TCP input,不能同时作为同地址的 Beats input。
方案二:写文件 + 采集转发(生产更稳)
.WriteTo.File(new Serilog.Formatting.Json.JsonFormatter(),
"logs/app-.json", rollingInterval: RollingInterval.Day)
需安装 Serilog.Sinks.File。应用写本地文件,采集代理(如 Filebeat)负责转发与重试;文件保留期、磁盘容量、轮转及主机故障仍影响可靠性,不能保证不丢日志。
常见坑
- UDP 会丢包:内网低负载可用,跨网络务必 TCP。
- 换行分隔:Logstash 用
json_linescodec 时,每条 JSON 必须以换行结尾,示例 JsonFormatter 会输出换行;自定义 formatter 需自行保证分隔。 - 断连缓冲:所核对的 Network sink 实现已有内存队列及重连,队列满时会丢弃旧事件;具体行为应核对所用版本,额外 Async 包装不等于持久缓冲。
若后端使用观测云,也可以保留 Serilog 的文件输出,由 DataKit读取日志。先针对实际 formatter 配置字段解析,并发送一条含异常堆栈的样本,确认没有被拆成多条记录,再考虑替换现有 Sink。
常见问题(FAQ)
Q:Serilog 有专门的 Logstash sink 吗?
A:社区有 Serilog.Sinks.Logstash(HTTP/UDP),需核对具体包版本的维护情况、协议和重试语义;Network TCP + json_lines 是另一种选择。
Q:Logstash 端收到的是一行多个 JSON 粘在一起怎么办?
A:TCP 是字节流,应让每条 JSON 以换行分隔,再匹配 json_lines;若发送端没有分隔,仅换 codec 不能补出事件边界。
Q:想保留 Serilog 的消息模板属性怎么办?
A:用 JsonFormatter(或 CompactJsonFormatter 输出 CLEF 格式),JsonFormatter 将属性放在 Properties 对象内;CompactJsonFormatter 将普通属性放在顶层,需按实际格式解析。