Class: SemanticLogger::Appender::Syslog

Inherits:
Base
  • Object
show all
Defined in:
lib/semantic_logger/appender/syslog.rb

Constant Summary collapse

DEFAULT_LEVEL_MAP =

Default mapping of ruby log levels to syslog log levels

::Syslog::LOG_EMERG - “System is unusable” ::Syslog::LOG_ALERT - “Action needs to be taken immediately” ::Syslog::LOG_CRIT - “A critical condition has occurred” ::Syslog::LOG_ERR - “An error occurred” ::Syslog::LOG_WARNING - “Warning of a possible problem” ::Syslog::LOG_NOTICE - “A normal but significant condition occurred” ::Syslog::LOG_INFO - “Informational message” ::Syslog::LOG_DEBUG - “Debugging information”

{
  fatal: ::Syslog::LOG_CRIT,
  error: ::Syslog::LOG_ERR,
  warn:  ::Syslog::LOG_WARNING,
  info:  ::Syslog::LOG_NOTICE,
  debug: ::Syslog::LOG_INFO,
  trace: ::Syslog::LOG_DEBUG
}

Instance Attribute Summary collapse

Attributes inherited from Base

#formatter

Attributes inherited from Base

#filter, #name

Instance Method Summary collapse

Methods inherited from Base

colorized_formatter, #level

Methods inherited from Base

#benchmark, default_level, default_level=, #level, #level=, #payload, #pop_tags, #push_tags, #silence, #tagged, #tags, #with_payload

Constructor Details

#initialize(params = {}, &block) ⇒ Syslog

For more information on the Syslog constants used below see ruby-doc.org/stdlib-2.0.0/libdoc/syslog/rdoc/Syslog.html Parameters

:ident [String]
  Identity of the program
  Default: 'ruby'

:options [Integer]
  Default: ::Syslog::LOG_PID | ::Syslog::LOG_CONS
  Any of the following (options can be logically OR'd together)
    ::Syslog::LOG_CONS
    ::Syslog::LOG_NDELAY
    ::Syslog::LOG_NOWAIT
    ::Syslog::LOG_ODELAY
    ::Syslog::LOG_PERROR
    ::Syslog::LOG_PID

:facility [Integer]
  Default: ::Syslog::LOG_USER
  Type of program (can be logically OR'd together)
    ::Syslog::LOG_AUTH
    ::Syslog::LOG_AUTHPRIV
    ::Syslog::LOG_CONSOLE
    ::Syslog::LOG_CRON
    ::Syslog::LOG_DAEMON
    ::Syslog::LOG_FTP
    ::Syslog::LOG_KERN
    ::Syslog::LOG_LRP
    ::Syslog::LOG_MAIL
    ::Syslog::LOG_NEWS
    ::Syslog::LOG_NTP
    ::Syslog::LOG_SECURITY
    ::Syslog::LOG_SYSLOG
    ::Syslog::LOG_USER
    ::Syslog::LOG_UUCP
    ::Syslog::LOG_LOCAL0
    ::Syslog::LOG_LOCAL1
    ::Syslog::LOG_LOCAL2
    ::Syslog::LOG_LOCAL3
    ::Syslog::LOG_LOCAL4
    ::Syslog::LOG_LOCAL5
    ::Syslog::LOG_LOCAL6
    ::Syslog::LOG_LOCAL7

:level [Symbol]
  Default: SemanticLogger's log level.
  The minimum level at which this appender will write logs. Any log messages below this level will be ignored.

:level_map [Hash]
  Supply a custom map of SemanticLogger levels to syslog levels.
  For example, passing in { warn: ::Syslog::LOG_NOTICE }
    would result in a log mapping that matches the default level map,
    except for :warn, which ends up with a LOG_NOTICE level instead of a
    LOG_WARNING one.
  Without overriding any parameters, the level map will be
    LEVEL_MAP = {
      fatal:   ::Syslog::LOG_CRIT,
      error:   ::Syslog::LOG_ERR,
      warn:    ::Syslog::LOG_WARNING,
      info:    ::Syslog::LOG_NOTICE,
      debug:   ::Syslog::LOG_INFO,
      trace:   ::Syslog::LOG_DEBUG
    }

:local_hostname [String]
  Default: Socket.gethostname || `hostname`.strip
  Hostname to provide to the remote syslog.

:server [String]
  Default: 'syslog://localhost'
  For writing logs to a remote syslog server
  URI of server: protocol://host:port
  Uses port 514 by default for TCP and UDP.
  local syslog example:          'syslog://localhost'
  TCP example with default port: 'tcp://logger'
  TCP example with custom port:  'tcp://logger:8514'
  UDP example with default port: 'udp://logger'
  UDP example with custom port:  'udp://logger:8514'
  When using the :syslog protocol, logs will always be sent to the localhost syslog

:tcp_client [Hash]
  Default: {}
  Only used with the TCP protocol.
  Specify custom parameters to pass into Net::TCPClient.new
  For a list of options see the net_tcp_client documentation:
    https://www.omniref.com/ruby/gems/net_tcp_client/1.0.0/symbols/Net::TCPClient/initialize


145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/semantic_logger/appender/syslog.rb', line 145

def initialize(params = {}, &block)
  params     = params.dup
  @ident     = params.delete(:ident) || 'ruby'
  @options   = params.delete(:options) || (::Syslog::LOG_PID | ::Syslog::LOG_CONS)
  @facility  = params.delete(:facility) || ::Syslog::LOG_USER
  filter     = params.delete(:filter)
  level      = params.delete(:level)
  level_map  = params.delete(:level_map)
  @level_map = DEFAULT_LEVEL_MAP.dup
  @level_map.update(level_map) if level_map
  @server   = params.delete(:server) || 'syslog://localhost'
  uri       = URI(@server)
  @host     = uri.host || 'localhost'
  @protocol = (uri.scheme || :syslog).to_sym
  raise "Unknown protocol #{@protocol}!" unless [:syslog, :tcp, :udp].include?(@protocol)
  @host               = 'localhost' if @protocol == :syslog
  @port               = URI(@server).port || 514
  @local_hostname     = params.delete(:local_hostname) || Socket.gethostname || `hostname`.strip
  @tcp_client_options = params.delete(:tcp_client)

  # Warn about any unknown configuration options.
  params.each_pair { |key, val| SemanticLogger::Logger.logger.warn "Ignoring unknown configuration option: #{key.inspect} => #{val.inspect}" }

  # The syslog_protocol gem is required when logging over TCP or UDP.
  if [:tcp, :udp].include?(@protocol)
    begin
      require 'syslog_protocol'
    rescue LoadError
      raise 'Missing gem: syslog_protocol. This gem is required when logging over TCP or UDP. To fix this error: gem install syslog_protocol'
    end

    # The net_tcp_client gem is required when logging over TCP.
    if protocol == :tcp
      @tcp_client_options          ||= {}
      @tcp_client_options[:server] = "#{@host}:#{@port}"
      begin
        require 'net/tcp_client'
      rescue LoadError
        raise 'Missing gem: net_tcp_client. This gem is required when logging over TCP. To fix this error: gem install net_tcp_client'
      end
    end
  end

  reopen

  super(level, filter, &block)
end

Instance Attribute Details

#facilityObject (readonly)

Returns the value of attribute facility.



38
39
40
# File 'lib/semantic_logger/appender/syslog.rb', line 38

def facility
  @facility
end

#hostObject (readonly)

Returns the value of attribute host.



38
39
40
# File 'lib/semantic_logger/appender/syslog.rb', line 38

def host
  @host
end

#local_hostnameObject (readonly)

Returns the value of attribute local_hostname.



38
39
40
# File 'lib/semantic_logger/appender/syslog.rb', line 38

def local_hostname
  @local_hostname
end

#portObject (readonly)

Returns the value of attribute port.



38
39
40
# File 'lib/semantic_logger/appender/syslog.rb', line 38

def port
  @port
end

#protocolObject (readonly)

Returns the value of attribute protocol.



38
39
40
# File 'lib/semantic_logger/appender/syslog.rb', line 38

def protocol
  @protocol
end

#remote_syslogObject (readonly)

Returns the value of attribute remote_syslog.



38
39
40
# File 'lib/semantic_logger/appender/syslog.rb', line 38

def remote_syslog
  @remote_syslog
end

#serverObject (readonly)

Returns the value of attribute server.



38
39
40
# File 'lib/semantic_logger/appender/syslog.rb', line 38

def server
  @server
end

Instance Method Details

#default_formatterObject

Custom log formatter for syslog



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/semantic_logger/appender/syslog.rb', line 236

def default_formatter
  Proc.new do |log|
    tags = log.tags.collect { |tag| "[#{tag}]" }.join(" ") + " " if log.tags && (log.tags.size > 0)

    message = log.message.to_s
    message << ' -- ' << log.payload.inspect if log.payload
    log.each_exception do |exception, i|
      if i == 0
        message << ' -- '
      else
        message << "\nCause: "
      end
      message << "#{exception.class}: #{exception.message}\n#{(exception.backtrace || []).join("\n")}"
    end

    duration_str = log.duration ? "(#{'%.1f' % log.duration}ms) " : ''

    "#{log.level.to_s[0..0].upcase} [#{$$}:#{log.thread_name}] #{tags}#{duration_str}#{log.name} -- #{message}"
  end
end

#flushObject

Flush is called by the semantic_logger during shutdown.



231
232
233
# File 'lib/semantic_logger/appender/syslog.rb', line 231

def flush
  @remote_syslog.flush if @remote_syslog && @remote_syslog.respond_to?(:flush)
end

#log(log) ⇒ Object

Write the log using the specified protocol and host.



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/semantic_logger/appender/syslog.rb', line 211

def log(log)
  # Ensure minimum log level is met, and check filter
  return false if (level_index > (log.level_index || 0)) || !include_message?(log)

  case @protocol
  when :syslog
    # Since the Ruby Syslog API supports sprintf format strings, double up all existing '%'
    message = formatter.call(log).gsub '%', '%%'
    ::Syslog.log @level_map[log.level], message
  when :tcp
    @remote_syslog.retry_on_connection_failure { @remote_syslog.write("#{syslog_packet_formatter(log)}\r\n") }
  when :udp
    @remote_syslog.send syslog_packet_formatter(log), 0, @host, @port
  else
    raise "Unsupported protocol: #{protocol}"
  end
  true
end

#reopenObject

After forking an active process call #reopen to re-open open the handles to resources



195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/semantic_logger/appender/syslog.rb', line 195

def reopen
  case @protocol
  when :syslog
    ::Syslog.open(@ident, @options, @facility)
  when :tcp
    # Use the local logger for @remote_syslog so errors with the remote logger can be recorded locally.
    @tcp_client_options[:logger] = SemanticLogger::Logger.logger
    @remote_syslog               = Net::TCPClient.new(@tcp_client_options)
  when :udp
    @remote_syslog = UDPSocket.new
  else
    raise "Unsupported protocol: #{@protocol}"
  end
end

#syslog_packet_formatter(log) ⇒ Object

Format the syslog packet so it can be sent over TCP or UDP



258
259
260
261
262
263
264
265
266
# File 'lib/semantic_logger/appender/syslog.rb', line 258

def syslog_packet_formatter(log)
  packet          = SyslogProtocol::Packet.new
  packet.hostname = @local_hostname
  packet.facility = @facility
  packet.severity = @level_map[log.level]
  packet.tag      = @ident
  packet.content  = default_formatter.call(log)
  packet.to_s
end