lib/syslogger.rb in syslogger-1.6.5 vs lib/syslogger.rb in syslogger-1.6.6
- old
+ new
@@ -1,35 +1,43 @@
+require 'forwardable'
require 'syslog'
require 'logger'
-require 'thread'
class Syslogger
+ extend Forwardable
MUTEX = Mutex.new
- attr_reader :level, :ident, :options, :facility, :max_octets
- attr_accessor :formatter
+ attr_reader :level, :options, :facility
+ attr_accessor :ident, :formatter, :max_octets
MAPPING = {
Logger::DEBUG => Syslog::LOG_DEBUG,
Logger::INFO => Syslog::LOG_INFO,
Logger::WARN => Syslog::LOG_WARNING,
Logger::ERROR => Syslog::LOG_ERR,
Logger::FATAL => Syslog::LOG_CRIT,
Logger::UNKNOWN => Syslog::LOG_ALERT
}.freeze
- #
+ LEVELS = %w[debug info warn error fatal unknown].freeze
+
# Initializes default options for the logger
+ #
# <tt>ident</tt>:: the name of your program [default=$0].
+ #
# <tt>options</tt>:: syslog options [default=<tt>Syslog::LOG_PID | Syslog::LOG_CONS</tt>].
+ #
# Correct values are:
# LOG_CONS : writes the message on the console if an error occurs when sending the message;
# LOG_NDELAY : no delay before sending the message;
# LOG_PERROR : messages will also be written on STDERR;
# LOG_PID : adds the process number to the message (just after the program name)
- # <tt>facility</tt>:: the syslog facility [default=nil] Correct values include:
+ #
+ # <tt>facility</tt>:: the syslog facility [default=nil]
+ #
+ # Correct values include:
# Syslog::LOG_DAEMON
# Syslog::LOG_USER
# Syslog::LOG_SYSLOG
# Syslog::LOG_LOCAL2
# Syslog::LOG_NEWS
@@ -40,50 +48,42 @@
# logger.level = Logger::INFO # use Logger levels
# logger.warn "warning message"
# logger.debug "debug message"
# logger.info "my_subapp" { "Some lazily computed message" }
#
- def initialize(ident = $0, options = Syslog::LOG_PID | Syslog::LOG_CONS, facility = nil)
+ def initialize(ident = $PROGRAM_NAME, options = Syslog::LOG_PID | Syslog::LOG_CONS, facility = nil)
@ident = ident
@options = options || (Syslog::LOG_PID | Syslog::LOG_CONS)
@facility = facility
@level = Logger::INFO
- @formatter = proc do |_, _, _, msg|
- msg
- end
+ @formatter = SimpleFormatter.new
end
- %w{debug info warn error fatal unknown}.each do |logger_method|
+ LEVELS.each do |logger_method|
# Accepting *args as message could be nil.
# Default params not supported in ruby 1.8.7
define_method logger_method.to_sym do |*args, &block|
severity = Logger.const_get(logger_method.upcase)
- return true if @level > severity
+ return true if level > severity
+
add(severity, nil, args.first, &block)
end
- unless logger_method == 'unknown'
- define_method "#{logger_method}?".to_sym do
- @level <= Logger.const_get(logger_method.upcase)
- end
+ next if logger_method == 'unknown'.freeze
+
+ define_method "#{logger_method}?".to_sym do
+ level <= Logger.const_get(logger_method.upcase)
end
end
- # Log a message at the Logger::INFO level. Useful for use with Rack::CommonLogger
+ # Log a message at the Logger::INFO level.
def write(msg)
add(Logger::INFO, msg)
end
+ alias << write
+ alias puts write
- # Logs a message at the Logger::INFO level.
- def <<(msg)
- add(Logger::INFO, msg)
- end
-
- def puts(msg)
- add(Logger::INFO, msg)
- end
-
# Low level method to add a message.
# +severity+:: the level of the message. One of Logger::DEBUG, Logger::INFO, Logger::WARN, Logger::ERROR, Logger::FATAL, Logger::UNKNOWN
# +message+:: the message string.
# If nil, the method will call the block and use the result as the message string.
# If both are nil or no block is given, it will use the progname as per the behaviour of both the standard Ruby logger, and the Rails BufferedLogger.
@@ -91,60 +91,31 @@
def add(severity, message = nil, progname = nil, &block)
if message.nil? && block.nil? && !progname.nil?
message, progname = progname, nil
end
progname ||= @ident
- mask = Syslog::LOG_UPTO(MAPPING[@level])
+ mask = Syslog::LOG_UPTO(MAPPING[level])
communication = message || block && block.call
formatted_communication = clean(formatter.call(severity, Time.now, progname, communication))
# Call Syslog
syslog_add(progname, severity, mask, formatted_communication)
end
- # Set the max octets of the messages written to the log
- def max_octets=(max_octets)
- @max_octets = max_octets
- end
-
# Sets the minimum level for messages to be written in the log.
# +level+:: one of <tt>Logger::DEBUG</tt>, <tt>Logger::INFO</tt>, <tt>Logger::WARN</tt>, <tt>Logger::ERROR</tt>, <tt>Logger::FATAL</tt>, <tt>Logger::UNKNOWN</tt>
def level=(level)
@level = sanitize_level(level)
end
- # Sets the ident string passed along to Syslog
- def ident=(ident)
- @ident = ident
- end
-
# Tagging code borrowed from ActiveSupport gem
def tagged(*tags)
- new_tags = push_tags(*tags)
- yield self
- ensure
- pop_tags(new_tags.size)
+ formatter.tagged(*tags) { yield self }
end
- def push_tags(*tags)
- tags.flatten.reject { |i| i.respond_to?(:empty?) ? i.empty? : !i }.tap do |new_tags|
- current_tags.concat(new_tags).uniq!
- end
- end
+ def_delegators :formatter, :current_tags, :push_tags, :pop_tags, :clear_tags!
- def pop_tags(size = 1)
- current_tags.pop size
- end
-
- def clear_tags!
- current_tags.clear
- end
-
- def current_tags
- Thread.current[:syslogger_tagged_logging_tags] ||= []
- end
-
protected
def sanitize_level(new_level)
begin
new_level = Logger.const_get(new_level.to_s.upcase)
@@ -161,42 +132,78 @@
# Borrowed from SyslogLogger.
def clean(message)
message = message.to_s.dup
message.strip! # remove whitespace
- message.gsub!(/\n/, '\\n') # escape newlines
- message.gsub!(/%/, '%%') # syslog(3) freaks on % (printf)
- message.gsub!(/\e\[[^m]*m/, '') # remove useless ansi color codes
+ message.gsub!(/\n/, '\\n'.freeze) # escape newlines
+ message.gsub!(/%/, '%%'.freeze) # syslog(3) freaks on % (printf)
+ message.gsub!(/\e\[[^m]*m/, ''.freeze) # remove useless ansi color codes
message
end
private
- def tags_text
- tags = current_tags
- if tags.any?
- clean(tags.collect { |tag| "[#{tag}] " }.join) << ' '
- end
- end
-
def syslog_add(progname, severity, mask, formatted_communication)
MUTEX.synchronize do
Syslog.open(progname, @options, @facility) do |s|
s.mask = mask
- if self.max_octets
- buffer = "#{tags_text}"
+ if max_octets
+ buffer = ''
formatted_communication.bytes do |byte|
buffer.concat(byte)
# if the last byte we added is potentially part of an escape, we'll go ahead and add another byte
- if buffer.bytesize >= self.max_octets && !['%'.ord,'\\'.ord].include?(byte)
+ if buffer.bytesize >= max_octets && !['%'.ord, '\\'.ord].include?(byte)
s.log(MAPPING[severity], buffer)
buffer = ''
end
end
s.log(MAPPING[severity], buffer) unless buffer.empty?
else
- s.log(MAPPING[severity], "#{tags_text}#{formatted_communication}")
+ s.log(MAPPING[severity], formatted_communication)
end
end
+ end
+ end
+
+ # Borrowed from ActiveSupport.
+ # See: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/tagged_logging.rb
+ class SimpleFormatter < Logger::Formatter
+ # This method is invoked when a log event occurs.
+ def call(_severity, _timestamp, _progname, msg)
+ "#{tags_text}#{msg}"
+ end
+
+ def tagged(*tags)
+ new_tags = push_tags(*tags)
+ yield self
+ ensure
+ pop_tags(new_tags.size)
+ end
+
+ def push_tags(*tags)
+ tags.flatten.reject { |i| i.respond_to?(:empty?) ? i.empty? : !i }.tap do |new_tags|
+ current_tags.concat(new_tags).uniq!
+ end
+ end
+
+ def pop_tags(size = 1)
+ current_tags.pop size
+ end
+
+ def clear_tags!
+ current_tags.clear
+ end
+
+ # Fix: https://github.com/crohr/syslogger/issues/29
+ # See: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/tagged_logging.rb#L47
+ def current_tags
+ # We use our object ID here to avoid conflicting with other instances
+ thread_key = @thread_key ||= "syslogger_tagged_logging_tags:#{object_id}".freeze
+ Thread.current[thread_key] ||= []
+ end
+
+ def tags_text
+ tags = current_tags
+ tags.collect { |tag| "[#{tag}] " }.join if tags.any?
end
end
end