class TcpSyslog

TcpSyslog is used are a dead-simple replacement for syslog ruby libs. None of them is able to send logs to a remote server, and even less in TCP.

Example:

For rails (2.X) :

config.logger = TcpSyslog.new(host => 'localhost')

For more info about Syslog protocol, please refer to the RFC: www.faqs.org/rfcs/rfc3164.html

Parts taken frm SyslogLogger gem and ActiveSupport

Constants

LEVEL_LOGGER_MAP

Maps Logger log level values to syslog log levels.

LOGGER_LEVEL_MAP

Maps Logger log levels to their values so we can silence.

LOGGER_MAP

Maps Logger warning types to syslog(3) warning types.

Attributes

auto_flushing[R]

Log level for Logger compatibility.

facility[R]

Log level for Logger compatibility.

host[R]

Log level for Logger compatibility.

port[R]

Log level for Logger compatibility.

progname[R]

Log level for Logger compatibility.

Public Class Methods

new(options = {}) click to toggle source

Usage :

  • options : A hash with the following options

** host : defaults to 'localhost' ** port : defaults to '514' ** facility : defaults to user ** progname : defaults to 'rails' ** auto_flushing : number of messages to buffer before flushing ** ssl : defaults to nil *** +cert : “/path/to/cert” *** +key : “/path/to/key” *** +ca_file : “/path/to/ca_file”

# File lib/tcp_syslog.rb, line 77
def initialize(options = {})
  @level = LOGGER_LEVEL_MAP[options[:level]] || Logger::DEBUG
  @host = options[:host] || 'localhost'
  @port = options[:port] || '514'
  @facility = options[:facility] || Syslog::LOG_USER
  @progname = options[:progname] || 'rails'
  @buffer = Hash.new { |h,k| h[k] = [] }
  @socket = {}
  @auto_flushing = options[:auto_flushing] || 1
  @local_ip = local_ip
  @remove_ansi_colors = options[:remove_ansi_colors] || true
  @ssl = options[:ssl]
  return if defined? SYSLOG
  self.class.const_set :SYSLOG, true
end

Public Instance Methods

<<(message) click to toggle source

In Logger, this dumps the raw message; the closest equivalent would be Logger::UNKNOWN

# File lib/tcp_syslog.rb, line 110
def <<(message)
  add(Logger::UNKNOWN, message)
end
add(severity, message, progname = nil, &block) click to toggle source

Almost duplicates Logger#add.

# File lib/tcp_syslog.rb, line 99
def add(severity, message, progname = nil, &block)
  severity ||= Logger::UNKNOWN
  return if @level > severity
  message = clean(message || block.call)
  buffer << {:severity => severity, :body => clean(message)}
  auto_flush
  message
end
close() click to toggle source
# File lib/tcp_syslog.rb, line 114
def close
  flush
  socket.close
  @socket[Thread.current] = nil
end
flush() click to toggle source

Flush buffered logs to Syslog

# File lib/tcp_syslog.rb, line 121
def flush
  buffer.each do |message|
    log(message[:severity], message[:body])
  end
  clear_buffer
end
socket() click to toggle source
# File lib/tcp_syslog.rb, line 128
def socket
  @socket[Thread.current] ||= new_socket
end
ssl?() click to toggle source
# File lib/tcp_syslog.rb, line 132
def ssl?
  !@ssl.nil?
end

Protected Instance Methods

clean(message) click to toggle source

Clean up messages so they're nice and pretty.

# File lib/tcp_syslog.rb, line 159
def clean(message)
  message = message.to_s.dup
  message.strip!
  message.gsub!(/%/, '%%') # syslog(3) freaks on % (printf)
  message.gsub!(/\e\[[^m]*m/, '') if @remove_ansi_colors # remove useless ansi color codes
  return message
end
local_ip() click to toggle source

Returns current ip (taken from coderrr.wordpress.com/2008/05/28/get-your-local-ip-address/)

# File lib/tcp_syslog.rb, line 169
def local_ip
  orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true  # turn off reverse DNS resolution temporarily

  UDPSocket.open do |s|
    s.connect '64.233.187.99', 1
    s.addr.last
  end
ensure
  Socket.do_not_reverse_lookup = orig
end
log(severity, msg) click to toggle source

Log the message to syslog This method is private, use the add method instead

# File lib/tcp_syslog.rb, line 182
def log(severity, msg)
  begin
    # Newline characters are not allowed in MSG part
    msg.split("\n").each do |line|
      write_on_socket(severity, line)
    end
  rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EPIPE, Timeout::Error, OpenSSL::SSL::SSLError => e
    # can't log anything, stop trying
    @socket[Thread.current] = nil
  end
end
new_socket() click to toggle source
# File lib/tcp_syslog.rb, line 138
def new_socket
  ssl? ? ssl_socket : tcp_socket
end
ssl_socket() click to toggle source
# File lib/tcp_syslog.rb, line 146
def ssl_socket
  ssl_context = OpenSSL::SSL::SSLContext.new
  ssl_context.cert = OpenSSL::X509::Certificate.new(File.open(@ssl[:cert]))
  ssl_context.key = OpenSSL::PKey::RSA.new(File.open(@ssl[:key]))
  ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
  ssl_context.ca_file = @ssl[:ca_file]
  ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, ssl_context)
  ssl_socket.sync_close = true
  ssl_socket.connect
  ssl_socket
end
tcp_socket() click to toggle source
# File lib/tcp_syslog.rb, line 142
def tcp_socket
  TCPSocket.new(@host, @port)
end
write_on_socket(severity, msg) click to toggle source

actually write on the tcp socket

# File lib/tcp_syslog.rb, line 195
def write_on_socket(severity, msg)
  SystemTimer.timeout_after(1) do
    # Build syslog packet
    packet = "<#{@facility + LEVEL_LOGGER_MAP[severity]}>#{Time.now.strftime("%b %e %H:%M:%S")} #{@local_ip} [#{@progname}]: #{msg}\n"
    # Max size of a packet cannot be  greater than 1024 bytes
    if packet.size > 1024 # FIXME1.9: use bytesize, fix following 2 lines
      socket.write(packet[0..1022] + "\n")
      write_on_socket severity, packet[1023..-1]
    else
      socket.write(packet)
    end
  end
end