class LogStash::Inputs::Syslog

Read syslog messages as events over the network.

This input is a good choice if you already use syslog today. It is also a good choice if you want to receive logs from appliances and network devices where you cannot run your own log collector.

Of course, 'syslog' is a very muddy term. This input only supports RFC3164 syslog with some small modifications. The date format is allowed to be RFC3164 style or ISO8601. Otherwise the rest of the RFC3164 must be obeyed. If you do not use RFC3164, do not use this input.

Note: this input will start listeners on both TCP and UDP

Public Class Methods

new(params) click to toggle source
Calls superclass method LogStash::Inputs::Base::new
# File lib/logstash/inputs/syslog.rb, line 48
def initialize(params)
  super
  @shutdown_requested = false
  BasicSocket.do_not_reverse_lookup = true
end

Public Instance Methods

register() click to toggle source
# File lib/logstash/inputs/syslog.rb, line 55
def register
  @grok_filter = LogStash::Filters::Grok.new(
    "overwrite" => "message",
    "match" => { "message" => "<%{POSINT:priority}>%{SYSLOGLINE}" },
  )

  @date_filter = LogStash::Filters::Date.new(
    "match" => [ "timestamp", "MMM  d HH:mm:ss", "MMM dd HH:mm:ss", "ISO8601"]
  )

  @grok_filter.register
  @date_filter.register

  @tcp_clients = ThreadSafe::Array.new
end
run(output_queue) click to toggle source
# File lib/logstash/inputs/syslog.rb, line 72
def run(output_queue)
  # udp server
  udp_thr = Thread.new do
    begin
      udp_listener(output_queue)
    rescue => e
      break if @shutdown_requested
      @logger.warn("syslog udp listener died",
                   :address => "#{@host}:#{@port}", :exception => e,
                   :backtrace => e.backtrace)
      sleep(5)
      retry
    end # begin
  end # Thread.new

  # tcp server
  tcp_thr = Thread.new do
    begin
      tcp_listener(output_queue)
    rescue => e
      break if @shutdown_requested
      @logger.warn("syslog tcp listener died",
                   :address => "#{@host}:#{@port}", :exception => e,
                   :backtrace => e.backtrace)
      sleep(5)
      retry
    end # begin
  end # Thread.new

  # If we exit and we're the only input, the agent will think no inputs
  # are running and initiate a shutdown.
  udp_thr.join
  tcp_thr.join
end
syslog_relay(event) click to toggle source

Following RFC3164 where sane, we'll try to parse a received message as if you were relaying a syslog message to it. If the message cannot be recognized (see @grok_filter), we'll treat it like the whole event is correct and try to fill the missing pieces (host, priority, etc)

# File lib/logstash/inputs/syslog.rb, line 197
def syslog_relay(event)
  @grok_filter.filter(event)

  if event["tags"].nil? || !event["tags"].include?("_grokparsefailure")
    # Per RFC3164, priority = (facility * 8) + severity
    #                       = (facility << 3) & (severity)
    priority = event["priority"].first.to_i rescue 13
    severity = priority & 7   # 7 is 111 (3 bits)
    facility = priority >> 3
    event["priority"] = priority
    event["severity"] = severity
    event["facility"] = facility

    event["timestamp"] = event["timestamp8601"] if event.include?("timestamp8601")
    @date_filter.filter(event)
  else
    @logger.info? && @logger.info("NOT SYSLOG", :message => event["message"])

    # RFC3164 says unknown messages get pri=13
    priority = 13
    event["priority"] = 13
    event["severity"] = 5   # 13 & 7 == 5
    event["facility"] = 1   # 13 >> 3 == 1
  end

  # Apply severity and facility metadata if
  # use_labels => true
  if @use_labels
    facility_number = event["facility"]
    severity_number = event["severity"]

    if @facility_labels[facility_number]
      event["facility_label"] = @facility_labels[facility_number]
    end

    if @severity_labels[severity_number]
      event["severity_label"] = @severity_labels[severity_number]
    end
  end
end
teardown() click to toggle source
# File lib/logstash/inputs/syslog.rb, line 165
def teardown
  @shutdown_requested = true
  close_udp
  close_tcp
  finished
end

Private Instance Methods

close_tcp() click to toggle source
# File lib/logstash/inputs/syslog.rb, line 182
def close_tcp
  # If we somehow have this left open, close it.
  @tcp_clients.each do |client|
    client.close rescue nil
  end
  @tcp.close if @tcp rescue nil
  @tcp = nil
end
close_udp() click to toggle source
# File lib/logstash/inputs/syslog.rb, line 173
def close_udp
  if @udp
    @udp.close_read rescue nil
    @udp.close_write rescue nil
  end
  @udp = nil
end
tcp_listener(output_queue) click to toggle source
# File lib/logstash/inputs/syslog.rb, line 133
def tcp_listener(output_queue)
  @logger.info("Starting syslog tcp listener", :address => "#{@host}:#{@port}")
  @tcp = TCPServer.new(@host, @port)
  @tcp_clients = []

  loop do
    client = @tcp.accept
    @tcp_clients << client
    Thread.new(client) do |client|
      ip, port = client.peeraddr[3], client.peeraddr[1]
      @logger.info("new connection", :client => "#{ip}:#{port}")
      LogStash::Util::set_thread_name("input|syslog|tcp|#{ip}:#{port}}")
      begin
        client.each do |line|
          @codec.decode(line) do |event|
            decorate(event)
            event["host"] = ip
            syslog_relay(event)
            output_queue << event
          end
        end
      rescue Errno::ECONNRESET
      ensure
        @tcp_clients.delete(client)
      end
    end # Thread.new
  end # loop do
ensure
  close_tcp
end
udp_listener(output_queue) click to toggle source
# File lib/logstash/inputs/syslog.rb, line 108
def udp_listener(output_queue)
  @logger.info("Starting syslog udp listener", :address => "#{@host}:#{@port}")

  if @udp
    @udp.close
  end

  @udp = UDPSocket.new(Socket::AF_INET)
  @udp.bind(@host, @port)

  loop do
    payload, client = @udp.recvfrom(9000)
    # Ruby uri sucks, so don't use it.
    @codec.decode(payload) do |event|
      decorate(event)
      event["host"] = client[3]
      syslog_relay(event)
      output_queue << event
    end
  end
ensure
  close_udp
end