1
0
mirror of https://github.com/meineerde/rackstash.git synced 2026-02-17 17:11:59 +00:00
rackstash/lib/rackstash/message.rb
Holger Just 2261315dcc Use a class instance for Message's default formatter
Calling #call in a custom class instead of a lambda performs much
better:

    require 'benchmark/ips'

    class RawFormatter
      def call(_severity, _timestamp, _progname, msg) msg end
    end

    Benchmark.ips do |x|
      message = 'my message'

      proc = ->(_severity, _timestamp, _progname, msg) { msg }
      instance = RawFormatter.new

      x.report('proc') do
        proc.call(nil, nil, nil, message)
      end

      x.report('instance') do
        instance.call(nil, nil, nil, message)
      end

      x.compare!
    end

    # Warming up --------------------------------------
    #                 proc   159.882k i/100ms
    #             instance   182.648k i/100ms
    # Calculating -------------------------------------
    #                 proc      4.612M (± 5.2%) i/s -     23.023M in   5.005950s
    #             instance      7.716M (± 6.3%) i/s -     38.539M in   5.015306s
    #
    # Comparison:
    #             instance:  7716097.7 i/s
    #                 proc:  4611920.5 i/s - 1.67x  slower
2017-01-25 23:35:28 +01:00

91 lines
2.1 KiB
Ruby

# Copyright 2017 Holger Just
#
# This software may be modified and distributed under the terms
# of the MIT license. See the LICENSE.txt file for details.
module Rackstash
# This class and all its data are immutable after initialization
class Message
RAW_FORMATTER = RawFormatter.new
SEVERITY_LABEL = [
'DEBUG'.freeze,
'INFO'.freeze,
'WARN'.freeze,
'ERROR'.freeze,
'FATAL'.freeze,
'ANY'.freeze
].freeze
attr_reader :message
attr_reader :severity
attr_reader :progname
attr_reader :time
attr_reader :formatter
def initialize(
msg,
severity: UNKNOWN,
time: Time.now.utc.freeze,
progname: PROGNAME,
formatter: RAW_FORMATTER
)
@message = cleanup_message(msg)
@severity = Integer(severity)
@severity = 0 if @severity < 0
@time = dup_freeze(time)
@progname = dup_freeze(progname)
@formatter = formatter
# Freeze the newly created message to ensure it can't be changed.
# All passed values are also effectively frozen, making the Message an
# immutable object.
freeze
end
def severity_label
SEVERITY_LABEL[@severity] || SEVERITY_LABEL.last
end
def to_s
@formatter.call(severity_label, @time, @progname, @message)
end
alias_method :to_str, :to_s
alias_method :as_json, :to_s
private
# Sanitize a single mesage to be added to the buffer, can be a single or
# multi line string
#
# @param msg [#to_s] a message to be added to the buffer
# @return [String] the sanitized frozen message
def cleanup_message(msg)
msg = msg.inspect unless msg.is_a?(String)
msg = utf8_encode(msg)
# remove useless ANSI color codes
msg.gsub!(/\e\[[0-9;]*m/, EMPTY_STRING)
msg.freeze
end
def utf8_encode(str)
str.to_s.encode(
Encoding::UTF_8,
invalid: :replace,
undef: :replace,
universal_newline: true
)
end
def dup_freeze(obj)
obj.frozen? ? obj : obj.dup.freeze
end
end
end