Syslog 日志拆分

问题: Rsyslog 收取到的日志被拆分成多条#

日志为 JSON 格式,观察到的现象是完整的 JSON 数据有 6k 左右。

发送系统将日志截断成 1k 长的多段,每段当作一个 syslog 消息发送过来。

导致接收系统无法正确处理发送过来的日志。

Syslog 协议#

https://www.rfc-editor.org/rfc/rfc5424

Syslog协议没有明确规定每个消息的大小,在6.1章节中说接收方应可以接收 2048 字节长的消息,但没有规定消息长度上限。

当消息长度超过接收方可处理的长度时,接收方可以对消息进行截断。

超长的消息如何分片发送和合并协议中没有规范,也就是没有标准方式可以遵循。

原因#

经查,发送消息的系统使用的 org.graylog2.syslog4j 库来发送 syslog。

该库代码中设置的默认消息长度为 1024 字节,超长的消息会分片为多个 1024 长的消息进行发送。

分片时如果某一片消息后面还有分片,则会在消息内容的最后加入字符串 “…”,

如果某一片消息前面有分片(即不是第一片消息),则会在消息内容的开始处加入字符串 “…”。

消息的最大长度以及分片时的前后标记分别可以通过配置项来进行设置,下面是其源码:

public byte[] getSplitMessageBeginText() {
    return this.splitMessageBeginText;
}

public void setSplitMessageBeginText(byte[] splitMessageBeginText) {
    this.splitMessageBeginText = splitMessageBeginText;
}

public void setSplitMessageBeginText(String splitMessageBeginText) throws SyslogRuntimeException {
    this.splitMessageBeginText = SyslogUtility.getBytes(this, splitMessageBeginText);
}

public byte[] getSplitMessageEndText() {
    return this.splitMessageEndText;
}

public void setSplitMessageEndText(byte[] splitMessageEndText) {
    this.splitMessageEndText = splitMessageEndText;
}

public void setSplitMessageEndText(String splitMessageEndText) throws SyslogRuntimeException {
    this.splitMessageEndText = SyslogUtility.getBytes(this, splitMessageEndText);
}

public int getMaxMessageLength() {
    return this.maxMessageLength;
}

public void setMaxMessageLength(int maxMessageLength) {
    this.maxMessageLength = maxMessageLength;
}

解决方案#

最终通过修改发送方的最大消息长度配置解决了问题:

import org.graylog2.syslog4j.Syslog;

public class ExampleSyslogTest {
   @Test
   public void appendPriority() throws Exception {
      syslog = Syslog.getInstance("tcp");
      syslog.getConfig().setMaxMessageLength(1024 * 16);
   }
}

更多思考#

消息大小总是会有一个限制,当消息长度超过一定限制时,分片就无可避免了。

下面这个 Stackoverflow 的问题中,给出了在 Rsyslog 中将多条日志整合在一起的方法:

https://stackoverflow.com/questions/56177336/rsyslog-config-to-merge-multiple-lines-coming-via-imuxsock

comments powered by Disqus