在使用Logstash做日志收集时,一个常见场景是通过syslog 插件来收集网络设备或安全设备的日志。
但是当你去搜索 logstash syslog plugin 时,可能会发现搜索引擎或者 ChatGPT 给出的答案却是udp 插件tcp 插件

这时就会有疑问:

  • syslog 插件和 tcp/udp 插件到底有什么差异?
  • 为什么官方的 syslog 插件性能很差?
  • 实际生产环境该用哪个插件?

本文尝试分析 syslog 插件与 tcp/udp 插件的差异,并给出最佳实践。


syslog 插件的实现

Logstash 提供了一个syslog插件,它支持同时监听 TCP 和 UDP
只需要配置端口即可,无需指定协议。

部分代码实现如下:

def udp_listener(output_queue)
    @logger.info("Starting syslog udp listener", :address => "#{@host}:#{@port}")

    @udp.close if @udp
    @udp = UDPSocket.new (IPAddr.new(@host).ipv6? rescue nil) ? Socket::AF_INET6 : Socket::AF_INET
    @udp.do_not_reverse_lookup = true
    @udp.bind(@host, @port)

    while !stop?
      payload, client = @udp.recvfrom(65507)
      metric.increment(:messages_received)
      decode(client[3], output_queue, payload)
    end
  ensure
    close_udp
  end # def udp_listener
  
  ...
  ...
  
   def tcp_listener(output_queue)
    @logger.info("Starting syslog tcp listener", :address => "#{@host}:#{@port}")
    @tcp = TCPServer.new(@host, @port)
    @tcp.do_not_reverse_lookup = true

    while !stop?
      socket = @tcp.accept
      @tcp_sockets << socket
      metric.increment(:connections)

      Thread.new(output_queue, socket) do |output_queue, socket|
        tcp_receiver(output_queue, socket)
      end
    end
  ensure
    close_tcp
  end # def tcp_listener

可以看到:

  • UDP :通过 recvfrom 接收数据
  • TCP :每个连接都起一个线程处理

也就是说,syslog 插件的 TCP 处理是基于多线程模型 。 这在并发连接数多的情况下,性能会急剧下降。


udp 插件的实现

再看 udp 插件的实现:

def udp_listener(output_queue)
    @logger.info("Starting UDP listener", :address => "#{@host}:#{@port}")
    ...
    # some code
    ...
    while !stop?
      next if IO.select([@udp], [], [], 0.5).nil?
      # collect datagram messages and add to inputworker queue
      @queue_size.times do
        begin
          payload, client = @udp.recvfrom_nonblock(@buffer_size)
          break if payload.empty?
          push_data(payload, client)
        rescue IO::EAGAINWaitReadable
          break
        end
      end
    end
  ensure
    if @udp
      @udp.close_read rescue ignore_close_and_log($!)
      @udp.close_write rescue ignore_close_and_log($!)
    end
  end

这里采用的是 IO 复用 (select/非阻塞 IO) ,而不是线程 per connection。 因此在高并发 UDP 场景下性能更好。


tcp 插件的实现

TCP 插件则是基于 Netty 封装的高性能事件驱动框架:

java_import 'org.logstash.tcp.InputLoop'

Netty 能够高效处理海量连接,相比 syslog 插件的多线程实现,性能提升巨大。


syslog 插件的额外开销

除了 I/O 模型上的差异,syslog 插件还会对日志进行 协议解析 ,比如调用 grok 规则对 syslog message 做拆解。而 tcp/udp 插件只是单纯地收取消息,不做协议解析。 因此:

  • syslog 插件: 收集 + 解析 (功能更多,但性能差),但是现在的syslog很多都不会遵守相关的协议规定。
  • tcp/udp 插件: 只收集 (需要后续手动解析,但性能好)

结论与推荐实践

  • 性能优先 :建议使用 tcpudp 插件来收集 syslog 日志,再通过 grokdissect 插件解析。
  • 简单配置 :如果设备较少、日志量不大,可以直接用 syslog 插件。
  • 生产环境最佳实践
  1. udp/tcp 插件收集日志
  2. 在 pipeline 中使用 grok/dissect 做 syslog 格式解析
  3. 输出到 Elasticsearch / Kafka 等存储系统