module OverSIP # Logging client module. Any class desiring to log messages must include (or extend) this module. # In order to identify itself in the logs, the class can define log_ig() method or set @log_id # attribute. module Logger def self.init_logger_mq group=nil @@logger_mq = ::OverSIP::PosixMQ.create_queue({ :name => ::OverSIP.syslogger_mq_name, :mode => :write, :maxmsg => 1000, :msgsize => 2000, :group => group }) end def self.load_methods # When not yet daemonized, also log to syslog. if not ::OverSIP.daemonized? ::Syslog.close if ::Syslog.opened? syslog_options = ::Syslog::LOG_PID | ::Syslog::LOG_NDELAY syslog_facility = ::OverSIP::Syslog::SYSLOG_FACILITY_MAPPING[::OverSIP.configuration[:core][:syslog_facility]] rescue ::Syslog::LOG_DAEMON ::Syslog.open(::OverSIP.master_name, syslog_options, syslog_facility) end begin @@threshold = ::OverSIP::Syslog::SYSLOG_SEVERITY_MAPPING[::OverSIP.configuration[:core][:syslog_level]] rescue @@threshold = 0 # debug. end $oversip_debug = ( @@threshold == 0 ? true : false ) @@congested = false ::OverSIP::Syslog::SYSLOG_SEVERITY_MAPPING.each do |level, level_value| method_str = " def log_system_#{level}(msg) " if ::OverSIP.syslogger_ready? method_str << " return false if @@threshold > #{level_value} || @@congested begin unless @@logger_mq.trysend ::OverSIP::Logger.syslog_system_msg2str(#{level_value}, msg, log_id), 0 @@congested = true ::EM.add_timer(1) do @@logger_mq.trysend ::OverSIP::Logger.syslog_system_msg2str(4, \"logger message queue was full, some logs have been lost\", log_id), 1 @@congested = false end end rescue ::Errno::EMSGSIZE @@logger_mq.trysend ::OverSIP::Logger.syslog_system_msg2str(4, \"too long message could not be logged\", log_id), 1 rescue nil rescue ::Exception => e @@logger_mq.trysend ::OverSIP::Logger.syslog_system_msg2str(4, \"unexpected logging error (\#{e.class}: \#{e.message})\", log_id), 1 rescue nil end " end if not ::OverSIP.daemonized? if %w{debug info notice}.include? level method_str << " puts ::OverSIP::Logger.fg_system_msg2str('#{level}', msg, log_id) if not ::OverSIP.syslogger_ready? ::OverSIP::Syslog.log ::OverSIP::Logger.syslog_system_msg2str(#{level_value}, msg, log_id) end " else method_str << " $stderr.puts ::OverSIP::Logger.fg_system_msg2str('#{level}', msg, log_id) if not ::OverSIP.syslogger_ready? ::OverSIP::Syslog.log ::OverSIP::Logger.syslog_system_msg2str(#{level_value}, msg, log_id) end " end end method_str << "end" self.module_eval method_str if ::OverSIP.syslogger_ready? # User logs. method_str = " def log_#{level}(msg) return false if @@threshold > #{level_value} || @@congested begin unless @@logger_mq.trysend ::OverSIP::Logger.syslog_user_msg2str(#{level_value}, msg, log_id), 0 @@congested = true ::EM.add_timer(1) do @@logger_mq.trysend ::OverSIP::Logger.syslog_user_msg2str(4, \"logger message queue was full, some logs have been lost\", log_id), 1 @@congested = false end end rescue ::Errno::EMSGSIZE @@logger_mq.trysend ::OverSIP::Logger.syslog_user_msg2str(4, \"too long message could not be logged\", log_id), 1 rescue nil rescue ::Exception => e @@logger_mq.trysend ::OverSIP::Logger.syslog_user_msg2str(4, \"unexpected logging error (\#{e.class}: \#{e.message})\", log_id), 1 rescue nil end end " self.module_eval method_str end end # .each # A convenient method to ensure that fatal logs are properly logged and program # exited with error status. def fatal msg log_system_crit msg log_system_crit "exiting with error status" ::OverSIP::Launcher.terminate error=true, fatal=true end end # Generate nice log messages. It accepst three parameters: # - level_value: Integer representing the log level. # - msg: the String or Exception to be logged. # - log_id: a String helping to identify the generator of this log message. def self.syslog_system_msg2str(level_value, msg, log_id) case msg when ::String level_value.to_s << "<" << log_id << "> " << msg when ::Exception "#{level_value}<#{log_id}> #{msg.message} (#{msg.class })\n#{(msg.backtrace || [])[0..3].join("\n")}" else level_value.to_s << "<" << log_id << "> " << msg.inspect end end def self.syslog_user_msg2str(level_value, msg, log_id) case msg when ::String level_value.to_s << "<" << log_id << "> [user] " << msg when ::Exception "#{level_value}<#{log_id}> [user] #{msg.message} (#{msg.class })\n#{(msg.backtrace || [])[0..3].join("\n")}" else level_value.to_s << "<" << log_id << "> [user] " << msg.inspect end end def self.fg_system_msg2str(level, msg, log_id) case msg when ::String "#{level.upcase}: <#{log_id}> " << msg when ::Exception "#{level.upcase}: <#{log_id}> #{msg.message} (#{msg.class })\n#{(msg.backtrace || [])[0..3].join("\n")}" else "#{level.upcase}: <#{log_id}> " << msg.inspect end end def self.close @@logger_mq.close rescue nil @@logger_mq.unlink rescue nil end # Default logging identifier is the class name. If log_id() method is redefined by the # class including this module, or it sets @log_id, then such a value takes preference. def log_id @log_id ||= (self.is_a?(::Module) ? self.name.split("::").last : self.class.name) end end # module Logger end