lib/statsd.rb in dogstatsd-ruby-1.4.1 vs lib/statsd.rb in dogstatsd-ruby-1.5.0

- old
+ new

@@ -30,10 +30,22 @@ ['priority', 'p'], ['source_type_name', 's'], ['alert_type', 't'] ] + # Service check options + SC_OPT_KEYS = [ + ['timestamp', 'd:'], + ['hostname', 'h:'], + ['tags', '#'], + ['message', 'm:'] + ] + OK = 0 + WARNING = 1 + CRITICAL = 2 + UNKNOWN = 3 + # A namespace to prepend to all statsd calls. Defaults to no namespace. attr_reader :namespace # StatsD host. Defaults to 127.0.0.1. attr_reader :host @@ -55,11 +67,11 @@ attr_accessor :logger end # Return the current version of the library. def self.VERSION - "1.4.1" + "1.5.0" end # @param [String] host your statsd host # @param [Integer] port your statsd port # @option opts [String] :namespace set a namespace to be prepended to every metric name @@ -70,11 +82,11 @@ @socket = UDPSocket.new self.namespace = opts[:namespace] self.tags = opts[:tags] @buffer = Array.new self.max_buffer_size = max_buffer_size - alias :send :send_to_socket + alias :send_stat :send_to_socket end def namespace=(namespace) #:nodoc: @namespace = namespace @prefix = namespace.nil? ? nil : "#{namespace}." @@ -169,10 +181,13 @@ send_stats stat, ms, :ms, opts end # Reports execution time of the provided block using {#timing}. # + # If the block fails, the stat is still reported, then the error + # is reraised + # # @param [String] stat stat name # @param [Hash] opts the options to create the metric with # @option opts [Numeric] :sample_rate sample rate, 1 for always # @option opts [Array<String>] :tags An array of tags # @yield The operation to be timed @@ -180,12 +195,15 @@ # @example Report the time (in ms) taken to activate an account # $statsd.time('account.activate') { @account.activate! } def time(stat, opts={}) start = Time.now result = yield - timing(stat, ((Time.now - start) * 1000).round, opts) + time_since(stat, start, opts) result + rescue + time_since(stat, start, opts) + raise end # Sends a value to be tracked as a set to the statsd server. # # @param [String] stat stat name. # @param [Numeric] value set value. @@ -196,26 +214,69 @@ # $statsd.set('visitors.uniques', User.id) def set(stat, value, opts={}) send_stats stat, value, :s, opts end + + # This method allows you to send custom service check statuses. + # + # @param [String] name Service check name + # @param [String] status Service check status. + # @param [Hash] opts the additional data about the service check + # @option opts [Integer, nil] :timestamp (nil) Assign a timestamp to the event. Default is now when none + # @option opts [String, nil] :hostname (nil) Assign a hostname to the event. + # @option opts [Array<String>, nil] :tags (nil) An array of tags + # @option opts [String, nil] :message (nil) A message to associate with this service check status + # @example Report a critical service check status + # $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent']) + def service_check(name, status, opts={}) + service_check_string = format_service_check(name, status, opts) + send_to_socket service_check_string + end + def format_service_check(name, status, opts={}) + sc_string = "_sc|#{name}|#{status}" + + SC_OPT_KEYS.each do |name_key| + if opts[name_key[0].to_sym] + if name_key[0] == 'tags' + tags = opts[:tags] + tags.each do |tag| + rm_pipes tag + end + tags = "#{tags.join(",")}" unless tags.empty? + sc_string << "|##{tags}" + elsif name_key[0] == 'message' + message = opts[:message] + rm_pipes message + escape_service_check_message message + sc_string << "|m:#{message}" + else + value = opts[name_key[0].to_sym] + rm_pipes value + sc_string << "|#{name_key[1]}#{value}" + end + end + end + return sc_string + end + # This end point allows you to post events to the stream. You can tag them, set priority and even aggregate them with other events. # # Aggregation in the stream is made on hostname/event_type/source_type/aggregation_key. # If there's no event type, for example, then that won't matter; # it will be grouped with other events that don't have an event type. # # @param [String] title Event title # @param [String] text Event text. Supports \n # @param [Hash] opts the additional data about the event - # @option opts [Time, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none + # @option opts [Integer, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none # @option opts [String, nil] :hostname (nil) Assign a hostname to the event. # @option opts [String, nil] :aggregation_key (nil) Assign an aggregation key to the event, to group it with some others # @option opts [String, nil] :priority ('normal') Can be "normal" or "low" # @option opts [String, nil] :source_type_name (nil) Assign a source type to the event # @option opts [String, nil] :alert_type ('info') Can be "error", "warning", "info" or "success". - # @option opts [Array<String>, nil] :source_type_name (nil) An array of tags + # @option opts [Array<String>] :tags tags to be added to every metric # @example Report an awful event: # $statsd.event('Something terrible happened', 'The end is near if we do nothing', :alert_type=>'warning', :tags=>['end_of_times','urgent']) def event(title, text, opts={}) event_string = format_event(title, text, opts) raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string.length > 8 * 1024 @@ -230,59 +291,70 @@ # $statsd.batch do |s| # s.gauge('users.online',156) # s.increment('page.views') # end def batch() - alias :send :send_to_buffer + alias :send_stat :send_to_buffer yield self flush_buffer - alias :send :send_to_socket + alias :send_stat :send_to_socket end def format_event(title, text, opts={}) escape_event_content title escape_event_content text event_string_data = "_e{#{title.length},#{text.length}}:#{title}|#{text}" # We construct the string to be sent by adding '|key:value' parts to it when needed - # All pipes ('|') in the metada are removed. Title and Text can keep theirs + # All pipes ('|') in the metadata are removed. Title and Text can keep theirs OPTS_KEYS.each do |name_key| if name_key[0] != 'tags' && opts[name_key[0].to_sym] value = opts[name_key[0].to_sym] rm_pipes value event_string_data << "|#{name_key[1]}:#{value}" end end - tags = opts[:tags] || nil + full_tags = tags + (opts[:tags] || []) # Tags are joined and added as last part to the string to be sent - if tags - tags.each do |tag| + unless full_tags.empty? + full_tags.each do |tag| rm_pipes tag end - tags = "#{tags.join(",")}" unless tags.empty? - event_string_data << "|##{tags}" + event_string_data << "|##{full_tags.join(',')}" end raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string_data.length > 8 * 1024 return event_string_data end + private + def escape_event_content(msg) - msg = msg.sub! "\n", "\\n" + msg.gsub! "\n", "\\n" end + def rm_pipes(msg) - msg = msg.sub! "|", "" + msg.gsub! "|", "" end + def escape_service_check_message(msg) + msg.gsub! 'm:', 'm\:' + msg.gsub! "\n", "\\n" + end + + def time_since(stat, start, opts) + timing(stat, ((Time.now - start) * 1000).round, opts) + end + def send_stats(stat, delta, type, opts={}) sample_rate = opts[:sample_rate] || 1 if sample_rate == 1 or rand < sample_rate # Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores. stat = stat.to_s.gsub('::', '.').tr(':|@', '_') rate = "|@#{sample_rate}" unless sample_rate == 1 ts = (tags || []) + (opts[:tags] || []) tags = "|##{ts.join(",")}" unless ts.empty? - send "#{@prefix}#{stat}:#{delta}|#{type}#{rate}#{tags}" + send_stat "#{@prefix}#{stat}:#{delta}|#{type}#{rate}#{tags}" end end def send_to_buffer(message) @buffer << message