lib/statsd/instrument.rb in statsd-instrument-1.5.0 vs lib/statsd/instrument.rb in statsd-instrument-1.6.0
- old
+ new
@@ -1,38 +1,46 @@
require 'socket'
require 'benchmark'
-require 'timeout'
-class << Benchmark
- def ms
- 1000 * realtime { yield }
- end
-end
+require 'statsd/instrument/version'
-
module StatsD
class << self
attr_accessor :host, :port, :mode, :logger, :enabled, :default_sample_rate,
:prefix, :implementation
end
self.enabled = true
self.default_sample_rate = 1.0
self.implementation = :statsd
- TimeoutClass = defined?(::SystemTimer) ? ::SystemTimer : ::Timeout
-
# StatsD.server = 'localhost:1234'
def self.server=(conn)
self.host, port = conn.split(':')
self.port = port.to_i
+ invalidate_socket
end
+ def self.host=(host)
+ @host = host
+ invalidate_socket
+ end
+
+ def self.port=(port)
+ @port = port
+ invalidate_socket
+ end
+
module Instrument
+
+ def self.generate_metric_name(metric_name, callee, *args)
+ metric_name.respond_to?(:call) ? metric_name.call(callee, args).gsub('::', '.') : metric_name.gsub('::', '.')
+ end
+
def statsd_measure(method, name, sample_rate = StatsD.default_sample_rate)
add_to_method(method, name, :measure) do |old_method, new_method, metric_name, *args|
define_method(new_method) do |*args, &block|
- StatsD.measure(metric_name.respond_to?(:call) ? metric_name.call(self, args) : metric_name, nil, sample_rate) { send(old_method, *args, &block) }
+ StatsD.measure(StatsD::Instrument.generate_metric_name(metric_name, self, *args), nil, sample_rate) { send(old_method, *args, &block) }
end
end
end
def statsd_count_success(method, name, sample_rate = StatsD.default_sample_rate)
@@ -45,14 +53,12 @@
raise
else
truthiness = (yield(result) rescue false) if block_given?
result
ensure
- result = truthiness == false ? 'failure' : 'success'
- key = metric_name.respond_to?(:call) ? metric_name.call(self, args) : metric_name
-
- StatsD.increment("#{key}.#{result}", sample_rate)
+ suffix = truthiness == false ? 'failure' : 'success'
+ StatsD.increment("#{StatsD::Instrument.generate_metric_name(metric_name, self, *args)}.#{suffix}", 1, sample_rate)
end
end
end
end
@@ -66,26 +72,43 @@
raise
else
truthiness = (yield(result) rescue false) if block_given?
result
ensure
- StatsD.increment(metric_name.respond_to?(:call) ? metric_name.call(self, args) : metric_name, sample_rate) if truthiness
+ StatsD.increment(StatsD::Instrument.generate_metric_name(metric_name, self, *args), sample_rate) if truthiness
end
end
end
end
def statsd_count(method, name, sample_rate = StatsD.default_sample_rate)
add_to_method(method, name, :count) do |old_method, new_method, metric_name|
define_method(new_method) do |*args, &block|
- StatsD.increment(metric_name.respond_to?(:call) ? metric_name.call(self, args) : metric_name, sample_rate)
+ StatsD.increment(StatsD::Instrument.generate_metric_name(metric_name, self, *args), 1, sample_rate)
send(old_method, *args, &block)
end
end
end
+ def statsd_remove_count(method, name)
+ remove_from_method(method, name, :count)
+ end
+
+ def statsd_remove_count_if(method, name)
+ remove_from_method(method, name, :count_if)
+ end
+
+ def statsd_remove_count_success(method, name)
+ remove_from_method(method, name, :count_success)
+ end
+
+ def statsd_remove_measure(method, name)
+ remove_from_method(method, name, :measure)
+ end
+
private
+
def add_to_method(method, name, action, &block)
metric_name = name
method_name_without_statsd = :"#{method}_for_#{action}_on_#{self.name}_without_#{name}"
# raw_ssl_request_for_measure_on_FedEx_without_ActiveMerchant.Shipping.#{self.class.name}.ssl_request
@@ -98,68 +121,107 @@
alias_method method_name_without_statsd, method
yield method_name_without_statsd, method_name_with_statsd, metric_name
alias_method method, method_name_with_statsd
end
+
+ def remove_from_method(method, name, action)
+ method_name_without_statsd = :"#{method}_for_#{action}_on_#{self.name}_without_#{name}"
+ method_name_with_statsd = :"#{method}_for_#{action}_on_#{self.name}_with_#{name}"
+ send(:remove_method, method_name_with_statsd)
+ alias_method method, method_name_without_statsd
+ send(:remove_method, method_name_without_statsd)
+ end
end
# glork:320|ms
- def self.measure(key, milli = nil, sample_rate = default_sample_rate)
+ def self.measure(key, milli = nil, sample_rate = default_sample_rate, tags = nil)
result = nil
- ms = milli || Benchmark.ms do
+ ms = milli || 1000 * Benchmark.realtime do
result = yield
end
- write(key, ms, :ms, sample_rate)
+ collect(key, ms, :ms, sample_rate, tags)
result
end
# gorets:1|c
- def self.increment(key, delta = 1, sample_rate = default_sample_rate)
- write(key, delta, :incr, sample_rate)
+ def self.increment(key, delta = 1, sample_rate = default_sample_rate, tags = nil)
+ collect(key, delta, :incr, sample_rate, tags)
end
# gaugor:333|g
# guagor:1234|kv|@1339864935 (statsite)
- def self.gauge(key, value, sample_rate_or_epoch = default_sample_rate)
- write(key, value, :g, sample_rate_or_epoch)
+ def self.gauge(key, value, sample_rate_or_epoch = default_sample_rate, tags = nil)
+ collect(key, value, :g, sample_rate_or_epoch, tags)
end
+ # histogram:123.45|h
+ def self.histogram(key, value, sample_rate_or_epoch = default_sample_rate, tags = nil)
+ collect(key, value, :h, sample_rate_or_epoch, tags)
+ end
+
private
+ def self.invalidate_socket
+ @socket = nil
+ end
+
def self.socket
- @socket ||= UDPSocket.new
+ if @socket.nil?
+ @socket = UDPSocket.new
+ @socket.connect(host, port)
+ end
+ @socket
end
- def self.write(k,v,op, sample_rate = default_sample_rate)
+ def self.collect(k, v, op, sample_rate = default_sample_rate, tags = nil)
return unless enabled
return if sample_rate < 1 && rand > sample_rate
- k = k.gsub('::', '.')
+ command = generate_packet(k, v, op, sample_rate, tags)
+ write_packet(command)
+ end
+ def self.write_packet(command)
+ if mode.to_s == 'production'
+ begin
+ socket.send(command, 0)
+ rescue SocketError, IOError, SystemCallError => e
+ logger.error e
+ end
+ else
+ logger.info "[StatsD] #{command}"
+ end
+ end
+
+ def self.clean_tags(tags)
+ tags.map do |tag|
+ components = tag.split(':', 2)
+ components.map { |c| c.gsub(/[^\w\.-]+/, '_') }.join(':')
+ end
+ end
+
+ def self.generate_packet(k, v, op, sample_rate = default_sample_rate, tags = nil)
command = "#{self.prefix + '.' if self.prefix}#{k}:#{v}"
case op
when :incr
command << '|c'
when :ms
command << '|ms'
when :g
command << (self.implementation == :statsite ? '|kv' : '|g')
+ when :h
+ raise NotImplementedError, "Histograms only supported on DataDog implementation." unless self.implementation == :datadog
+ command << '|h'
end
command << "|@#{sample_rate}" if sample_rate < 1 || (self.implementation == :statsite && sample_rate > 1)
- command << "\n" if self.implementation == :statsite
-
- if mode.to_s == 'production'
- socket_wrapper { socket.send(command, 0, host, port) }
- else
- logger.info "[StatsD] #{command}"
+ if tags
+ raise ArgumentError, "Tags are only supported on Datadog" unless self.implementation == :datadog
+ command << "|##{clean_tags(tags).join(',')}"
end
- end
- def self.socket_wrapper(options = {})
- TimeoutClass.timeout(options.fetch(:timeout, 0.1)) { yield }
- rescue Timeout::Error, SocketError, IOError, SystemCallError => e
- logger.error e
+ command << "\n" if self.implementation == :statsite
+ return command
end
end
-