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, json_formatter, #level

Methods inherited from Base

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

Constructor Details

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

Create a Syslog appender instance.

Parameters

url: [String]
  Default: 'syslog://localhost'
  For writing logs to a remote syslog server
  URL 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

host: [String]
  Host name to provide to the remote syslog.
  Default: SemanticLogger.host

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

level: [:trace | :debug | :info | :warn | :error | :fatal]
  Override the log level for this appender.
  Default: SemanticLogger.default_level

filter: [Regexp|Proc]
  RegExp: Only include log messages where the class name matches the supplied.
  regular expression. All other messages will be ignored.
  Proc: Only include log messages where the supplied Proc returns true
        The Proc must return true or false.

application: [String]
  Identity of the program.
  Default: SemanticLogger.application

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_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
    }

Raises:

  • (ArgumentError)


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
192
# File 'lib/semantic_logger/appender/syslog.rb', line 146

def initialize(options = {}, &block)
  options             = options.dup
  level               = options.delete(:level)
  filter              = options.delete(:filter)
  @application        = options.delete(:application) || options.delete(:ident) || 'ruby'
  @options            = options.delete(:options) || (::Syslog::LOG_PID | ::Syslog::LOG_CONS)
  @facility           = options.delete(:facility) || ::Syslog::LOG_USER
  level_map           = options.delete(:level_map)
  @url                = options.delete(:url) || options.delete(:server) || 'syslog://localhost'
  uri                 = URI(@url)
  @server             = uri.host || 'localhost'
  @protocol           = (uri.scheme || :syslog).to_sym
  @port               = uri.port || 514
  @server             = 'localhost' if @protocol == :syslog
  @host               = options.delete(:host) || options.delete(:local_hostname) || SemanticLogger.host
  @tcp_client_options = options.delete(:tcp_client)

  raise "Unknown protocol #{@protocol}!" unless [:syslog, :tcp, :udp].include?(@protocol)
  raise(ArgumentError, "Unknown options: #{options.inspect}") if options.size > 0

  @level_map = DEFAULT_LEVEL_MAP.dup
  @level_map.update(level_map) if level_map

  # 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] = "#{@server}:#{@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

#applicationObject (readonly)

Returns the value of attribute application.



33
34
35
# File 'lib/semantic_logger/appender/syslog.rb', line 33

def application
  @application
end

#facilityObject (readonly)

Returns the value of attribute facility.



33
34
35
# File 'lib/semantic_logger/appender/syslog.rb', line 33

def facility
  @facility
end

#hostObject (readonly)

Returns the value of attribute host.



33
34
35
# File 'lib/semantic_logger/appender/syslog.rb', line 33

def host
  @host
end

#portObject (readonly)

Returns the value of attribute port.



33
34
35
# File 'lib/semantic_logger/appender/syslog.rb', line 33

def port
  @port
end

#protocolObject (readonly)

Returns the value of attribute protocol.



33
34
35
# File 'lib/semantic_logger/appender/syslog.rb', line 33

def protocol
  @protocol
end

#remote_syslogObject (readonly)

Returns the value of attribute remote_syslog.



33
34
35
# File 'lib/semantic_logger/appender/syslog.rb', line 33

def remote_syslog
  @remote_syslog
end

#serverObject (readonly)

Returns the value of attribute server.



33
34
35
# File 'lib/semantic_logger/appender/syslog.rb', line 33

def server
  @server
end

#urlObject (readonly)

Returns the value of attribute url.



33
34
35
# File 'lib/semantic_logger/appender/syslog.rb', line 33

def url
  @url
end

Instance Method Details

#default_formatterObject

Custom log formatter for syslog. Only difference is the removal of the timestamp string since it is in the syslog packet.



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/semantic_logger/appender/syslog.rb', line 238

def default_formatter
  Proc.new do |log|
    # Header with date, time, log level and process info
    entry = "#{log.level_to_s} [#{log.process_info}]"

    # Tags
    entry << ' ' << log.tags.collect { |tag| "[#{tag}]" }.join(' ') if log.tags && (log.tags.size > 0)

    # Duration
    entry << " (#{log.duration_human})" if log.duration

    # Class / app name
    entry << " #{log.name}"

    # Log message
    entry << " -- #{log.message}" if log.message

    # Payload
    if payload = log.payload_to_s(false)
      entry << ' -- ' << payload
    end

    # Exceptions
    if log.exception
      entry << " -- Exception: #{log.exception.class}: #{log.exception.message}\n"
      entry << log.backtrace_to_s
    end
    entry
  end
end

#flushObject

Flush is called by the semantic_logger during shutdown.



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

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 server.



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

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, self).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, @server, @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



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

def reopen
  case @protocol
  when :syslog
    ::Syslog.open(@application, @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



270
271
272
273
274
275
276
277
278
279
# File 'lib/semantic_logger/appender/syslog.rb', line 270

def syslog_packet_formatter(log)
  packet          = SyslogProtocol::Packet.new
  packet.hostname = @host
  packet.facility = @facility
  packet.severity = @level_map[log.level]
  packet.tag      = @application
  packet.content  = default_formatter.call(log, self)
  packet.time     = log.time
  packet.to_s
end