lib/syslog-sd/notifier.rb in syslog-sd-1.3.2 vs lib/syslog-sd/notifier.rb in syslog-sd-1.4.0.beta1

- old
+ new

@@ -118,97 +118,133 @@ exception end def notify_with_level!(message_level, *args) return unless @enabled - extract_hash(*args) - @hash['level'] = message_level unless message_level.nil? - if @hash['level'] >= level - str = serialize_hash + hash = extract_hash(*args) + hash['level'] = message_level unless message_level.nil? + if hash['level'] >= level + str = serialize_hash(hash) @sender.send_datagram(str) str end end def extract_hash(object = nil, args = {}) + args = self.class.stringify_keys(args) + primary_data = if object.respond_to?(:to_hash) object.to_hash elsif object.is_a?(Exception) args['level'] ||= SyslogSD::ERROR self.class.extract_hash_from_exception(object) else args['level'] ||= SyslogSD::INFO { 'short_message' => object.to_s } end - @hash = default_options.merge(self.class.stringify_keys(args.merge(primary_data))) - convert_hoptoad_keys_to_graylog2 - set_file_and_line if @collect_file_and_line - set_timestamp - check_presence_of_mandatory_attributes - @hash + hash = self.class.stringify_keys(primary_data) + hash = default_options.merge(args.merge(hash)) + hash = convert_airbrake_keys_to_graylog2(hash) + hash = set_file_and_line(hash) + hash = set_timestamp(hash) + check_presence_of_mandatory_attributes(hash) + hash end + CALLER_REGEXP = /^(.*):(\d+).*/ + def self.extract_hash_from_exception(exception) - bt = exception.backtrace || ["Backtrace is not available."] - { 'short_message' => "#{exception.class}: #{exception.message}", 'full_message' => "Backtrace:\n" + bt.join("\n") } + error_class = exception.class.name + error_message = exception.message + + # always collect file and line there (ignore @collect_file_and_line) + # since we already know them, no need to call `caller` + file, line = nil, nil + bt = exception.backtrace + if bt + match = CALLER_REGEXP.match(bt[0]) + if match + file = match[1] + line = match[2].to_i + end + else + bt = ["Backtrace is not available."] + end + + { 'short_message' => "#{error_class}: #{error_message}", 'full_message' => "Backtrace:\n" + bt.join("\n"), + 'error_class' => error_class, 'error_message' => error_message, + 'file' => file, 'line' => line } end - # Converts Hoptoad-specific keys in +@hash+ to Graylog2-specific. - def convert_hoptoad_keys_to_graylog2 - if @hash['short_message'].to_s.empty? - if @hash.has_key?('error_class') && @hash.has_key?('error_message') - @hash['short_message'] = @hash.delete('error_class') + ': ' + @hash.delete('error_message') + # Converts Airbrake-specific keys in +hash+ to Graylog2-specific. + def convert_airbrake_keys_to_graylog2(hash) + if hash['short_message'].to_s.empty? + if hash.has_key?('error_class') && hash.has_key?('error_message') + hash['short_message'] = hash['error_class'] + ': ' + hash['error_message'] end end + hash end - CALLER_REGEXP = /^(.*):(\d+).*/ LIB_PATTERN = File.join('lib', 'syslog-sd') - def set_file_and_line - stack = caller - begin - frame = stack.shift - end while frame.include?(LIB_PATTERN) - match = CALLER_REGEXP.match(frame) - @hash['file'] = match[1] - @hash['line'] = match[2].to_i + def set_file_and_line(hash) + return hash unless hash['file'].nil? || hash['line'].nil? + + if @collect_file_and_line + stack = caller + begin + frame = stack.shift + end while frame.include?(LIB_PATTERN) + + match = CALLER_REGEXP.match(frame) + if match + hash['file'] = match[1] + hash['line'] = match[2].to_i + else + hash['file'] = 'unknown' + hash['line'] = -1 + end + end + + hash end - def set_timestamp - @hash['timestamp'] = Time.now.utc.to_f if @hash['timestamp'].nil? + def set_timestamp(hash) + hash['timestamp'] ||= Time.now.utc.to_f + hash end - def check_presence_of_mandatory_attributes + def check_presence_of_mandatory_attributes(hash) %w(short_message host).each do |attribute| - if @hash[attribute].to_s.empty? + if hash[attribute].to_s.empty? raise ArgumentError.new("#{attribute} is missing. Options short_message and host must be set.") end end end - def serialize_hash - raise ArgumentError.new("Hash is empty.") if @hash.nil? || @hash.empty? + def serialize_hash(hash) + raise ArgumentError.new("Hash is empty.") if hash.nil? || hash.empty? - @hash['level'] = @level_mapping[@hash['level']] + hash['level'] = @level_mapping[hash['level']] - prival = 128 + @hash.delete('level') # 128 = 16(local0) * 8 + prival = 128 + hash.delete('level') # 128 = 16(local0) * 8 t = Time.now.utc timestamp = timestamp_as_float ? t.to_f : t.strftime("%Y-%m-%dT%H:%M:%S.#{t.usec.to_s[0,3]}Z") - host = @hash.delete('host') - facility = @hash.delete('facility') || '-' - procid = @hash.delete('procid') - msgid = @hash.delete('msgid') || '-' - short_message = @hash.delete('short_message') - "<#{prival}>1 #{timestamp} #{host} #{facility} #{procid} #{msgid} #{serialize_sd} #{short_message}" + host = hash.delete('host') + facility = hash.delete('facility') || '-' + procid = hash.delete('procid') + msgid = hash.delete('msgid') || '-' + short_message = hash.delete('short_message') + "<#{prival}>1 #{timestamp} #{host} #{facility} #{procid} #{msgid} #{serialize_sd(hash)} #{short_message}" end - def serialize_sd - return '-' if @hash.empty? + def serialize_sd(hash) + return '-' if hash.empty? res = @sd_id - @hash.each_pair do |key, value| + hash.each_pair do |key, value| value = value.to_s.gsub(/([\\\]\"])/) { "\\#{$1}" } res += " #{key}=\"#{value}\"" end '[' + res + ']' end