forked from reinh/statsd
-
Notifications
You must be signed in to change notification settings - Fork 4
/
statsd.rb
146 lines (127 loc) · 4.34 KB
/
statsd.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
require 'socket'
# = Statsd: A Statsd client (https://github.com/etsy/statsd)
#
# @example Set up a global Statsd client for a server on localhost:9125
# $statsd = Statsd.new 'localhost', 8125
# @example Send some stats
# $statsd.increment 'garets'
# $statsd.timing 'glork', 320
# @example Use {#time} to time the execution of a block
# $statsd.time('account.activate') { @account.activate! }
# @example Create a namespaced statsd client and increment 'account.activate'
# statsd = Statsd.new('localhost').tap{|sd| sd.namespace = 'account'}
# statsd.increment 'activate'
class Statsd
# A namespace to prepend to all statsd calls.
attr_reader :namespace
# StatsD host. Defaults to 127.0.0.1.
attr_accessor :host
# StatsD port. Defaults to 8125.
attr_accessor :port
class << self
# Set to a standard logger instance to enable debug logging.
attr_reader :logger
def logger=(logger) #:nodoc:
@logger = logger
# Only include logging behavior if a logger is set.
include Logging
end
end
# @param [String] host your statsd host
# @param [Integer] port your statsd port
def initialize(host='127.0.0.1', port=8125)
@host, @port = host, port
@prefix = nil
@socket = UDPSocket.new
end
def namespace=(namespace) #:nodoc:
@namespace = namespace
@prefix = "#{namespace}."
end
# Sends an increment (count = 1) for the given stat to the statsd server.
#
# @param [String] stat stat name
# @param [Numeric] sample_rate sample rate, 1 for always
# @see #count
def increment(stat, sample_rate=1)
count stat, 1, sample_rate
end
# Sends a decrement (count = -1) for the given stat to the statsd server.
#
# @param [String] stat stat name
# @param [Numeric] sample_rate sample rate, 1 for always
# @see #count
def decrement(stat, sample_rate=1)
count stat, -1, sample_rate
end
# Sends an arbitrary count for the given stat to the statsd server.
#
# @param [String] stat stat name
# @param [Integer] count count
# @param [Numeric] sample_rate sample rate, 1 for always
def count(stat, count, sample_rate=1)
send_stats stat, count, :c, sample_rate
end
# Sends an arbitary gauge value for the given stat to the statsd server.
#
# This is useful for recording things like available disk space,
# memory usage, and the like, which have different semantics than
# counters.
#
# @param [String] stat stat name.
# @param [Numeric] gauge value.
# @param [Numeric] sample_rate sample rate, 1 for always
# @example Report the current user count:
# $statsd.gauge('user.count', User.count)
def gauge(stat, value, sample_rate=1)
send_stats stat, value, :g, sample_rate
end
# Sends a timing (in ms) for the given stat to the statsd server. The
# sample_rate determines what percentage of the time this report is sent. The
# statsd server then uses the sample_rate to correctly track the average
# timing for the stat.
#
# @param [String] stat stat name
# @param [Integer] ms timing in milliseconds
# @param [Numeric] sample_rate sample rate, 1 for always
def timing(stat, ms, sample_rate=1)
send_stats stat, ms, :ms, sample_rate
end
# Reports execution time of the provided block using {#timing}.
#
# @param [String] stat stat name
# @param [Numeric] sample_rate sample rate, 1 for always
# @yield The operation to be timed
# @see #timing
# @example Report the time (in ms) taken to activate an account
# $statsd.time('account.activate') { @account.activate! }
def time(stat, sample_rate=1)
start = Time.now
result = yield
timing(stat, ((Time.now - start) * 1000).round, sample_rate)
result
end
private
def send_stats(stat, delta, type, sample_rate=1)
# Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores.
stat = stat.to_s.gsub('::', '.').tr(':|@', '_')
if sample_rate == 1 or rand < sample_rate
rate = "|@#{sample_rate}" unless sample_rate == 1
send_to_socket "#{@prefix}#{stat}:#{delta}|#{type}#{rate}"
end
end
module Sending
def send_to_socket(message)
@socket.send(message, 0, @host, @port)
end
end
include Sending
module Logging
def send_to_socket(message)
self.class.logger.debug {"Statsd: #{message}"}
super
rescue => boom
self.class.logger.error {"Statsd: #{boom.class} #{boom}"}
end
end
end