batsd 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/LICENSE +20 -0
- data/README.md +4 -0
- data/batsd.gemspec +18 -0
- data/lib/batsd.rb +224 -0
- data/lib/batsd/errors.rb +41 -0
- data/lib/batsd/version.rb +3 -0
- metadata +53 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Benjamin Hathaway
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/batsd.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
require "batsd/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "batsd"
|
7
|
+
|
8
|
+
s.version = Batsd::VERSION
|
9
|
+
|
10
|
+
s.homepage = "https://github.com/hathaway/batsd-client"
|
11
|
+
s.authors = ["Ben Hathaway"]
|
12
|
+
s.email = ["ben@hathaway.cc"]
|
13
|
+
|
14
|
+
s.summary = "Batsd Client"
|
15
|
+
s.description = "A Batsd client for Ruby. Provides an interface for querying data in a batsd server."
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
end
|
data/lib/batsd.rb
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'json'
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
##
|
6
|
+
# A batsd client for querying data from a batsd server.
|
7
|
+
class Batsd
|
8
|
+
|
9
|
+
##
|
10
|
+
# The default option values.
|
11
|
+
DEFAULTS = {
|
12
|
+
:host => "127.0.0.1",
|
13
|
+
:port => 8127,
|
14
|
+
:timeout => 2000,
|
15
|
+
:max_attempts => 2
|
16
|
+
}
|
17
|
+
|
18
|
+
##
|
19
|
+
# The hostname of the batsd server.
|
20
|
+
def host
|
21
|
+
@options[:host]
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# The port the batsd server is running on.
|
26
|
+
def port
|
27
|
+
@options[:port]
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# The timeout in milliseconds to use for the TCP connection.
|
32
|
+
def timeout
|
33
|
+
@options[:timeout]
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# The maxiumum number of attempts to make when querying for data.
|
38
|
+
# If an error is encountered, it will try this many total times before throwing an exception.
|
39
|
+
def max_attempts
|
40
|
+
@options[:max_attempts]
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_accessor :remote
|
44
|
+
|
45
|
+
def initialize(options = {})
|
46
|
+
@options = _parse_options(options)
|
47
|
+
connect!
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Send the 'ping' command to the server.
|
52
|
+
#
|
53
|
+
# @return [String] The response received from the server.
|
54
|
+
def ping
|
55
|
+
send_command("ping")
|
56
|
+
end
|
57
|
+
|
58
|
+
# Queries for the available keys in the batsd server.
|
59
|
+
#
|
60
|
+
# @return [Array] An array of string keys.
|
61
|
+
def available
|
62
|
+
keys = send_command("available")
|
63
|
+
keys.collect do |k|
|
64
|
+
if k.match /^timer/
|
65
|
+
["mean", "min", "max", "upper_90", "stddev", "count"].collect{|a| "#{k}:#{a}"}
|
66
|
+
else
|
67
|
+
k
|
68
|
+
end
|
69
|
+
end.flatten
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Get the stats for a given <code>metric_name</code> that's contained in the available
|
74
|
+
# set of datapoints within the range of <code>start_timestamp</code> to
|
75
|
+
# <code>end_timestamp</code>
|
76
|
+
#
|
77
|
+
# @param [String] metric_name
|
78
|
+
# The key for the metric you are querying for.
|
79
|
+
#
|
80
|
+
# @param [Time] start_timestamp
|
81
|
+
# The start of the time range of the query.
|
82
|
+
#
|
83
|
+
# @param [Time] end_timestamp
|
84
|
+
# The end of the time range of the query. Defaults to the current time.
|
85
|
+
#
|
86
|
+
# @param [Integer] attempts
|
87
|
+
# The number of attempts that have been made for this query. Defaults to 0.
|
88
|
+
#
|
89
|
+
# @return [Array] An array of hashes. Each hash contains the <code>:timestamp</code>
|
90
|
+
# and <code>:value</code> of the data point.
|
91
|
+
def stats(metric_name, start_timestamp, end_timestamp=Time.now, attempt=0)
|
92
|
+
results = []
|
93
|
+
values = send_command("values #{metric_name} #{start_timestamp.to_i} #{end_timestamp.to_i}")
|
94
|
+
if values[metric_name].nil?
|
95
|
+
if attempt < max_attempts
|
96
|
+
return values(metric_name, start_timestamp, end_timestamp, attempt+1)
|
97
|
+
else
|
98
|
+
raise InvalidValuesError
|
99
|
+
end
|
100
|
+
end
|
101
|
+
results = values[metric_name].collect{|v| { timestamp: Time.at(v["timestamp"].to_i), value: v["value"].to_f } }
|
102
|
+
results
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# Get only the values of the stats for a given <code>metric_name</code>
|
107
|
+
# that's contained in the available set of datapoints within the range
|
108
|
+
# of <code>start_timestamp</code> to <code>end_timestamp</code>
|
109
|
+
#
|
110
|
+
# @param [String] metric_name
|
111
|
+
# The key for the metric you are querying for.
|
112
|
+
#
|
113
|
+
# @param [Time] start_timestamp
|
114
|
+
# The start of the time range of the query.
|
115
|
+
#
|
116
|
+
# @param [Time] end_timestamp
|
117
|
+
# The end of the time range of the query. Defaults to the current time.
|
118
|
+
#
|
119
|
+
# @return [Array] An array of floating point numbers.
|
120
|
+
def values(metric_name, start_timestamp, end_timestamp=Time.now)
|
121
|
+
stats(metric_name, start_timestamp, end_timestamp).map{|s| s[:value]}
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Get only the timestamps of the stats for a given <code>metric_name</code>
|
126
|
+
# that's contained in the available set of datapoints within the range
|
127
|
+
# of <code>start_timestamp</code> to <code>end_timestamp</code>
|
128
|
+
#
|
129
|
+
# @param [String] metric_name
|
130
|
+
# The key for the metric you are querying for.
|
131
|
+
#
|
132
|
+
# @param [Time] start_timestamp
|
133
|
+
# The start of the time range of the query.
|
134
|
+
#
|
135
|
+
# @param [Time] end_timestamp
|
136
|
+
# The end of the time range of the query. Defaults to the current time.
|
137
|
+
#
|
138
|
+
# @return [Array] An array of Time objects.
|
139
|
+
def timestamps(metric_name, start_timestamp, end_timestamp=Time.now)
|
140
|
+
stats(metric_name, start_timestamp, end_timestamp).map{|s| s[:timestamp]}
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
protected
|
145
|
+
|
146
|
+
##
|
147
|
+
# Parse the options provided in the constructor. Use the defaults for
|
148
|
+
# any options not provided.
|
149
|
+
def _parse_options(options)
|
150
|
+
defaults = DEFAULTS.dup
|
151
|
+
options = options.dup
|
152
|
+
|
153
|
+
defaults.keys.each do |key|
|
154
|
+
options[key] ||= defaults[key]
|
155
|
+
end
|
156
|
+
|
157
|
+
options
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
##
|
163
|
+
# Connect to the remote batsd server over TCP.
|
164
|
+
def connect!
|
165
|
+
Timeout::timeout(5) do
|
166
|
+
self.remote = TCPSocket.new(host, port)
|
167
|
+
end
|
168
|
+
rescue Timeout::Error => e
|
169
|
+
raise ConnectionTimeoutError
|
170
|
+
rescue
|
171
|
+
raise CannotConnectError
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# Disconnect from the remote batsd server.
|
176
|
+
def disconnect
|
177
|
+
self.remote.close
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# Reconnect to the batsd server.
|
182
|
+
def reconnect!
|
183
|
+
disconnect
|
184
|
+
connect!
|
185
|
+
end
|
186
|
+
|
187
|
+
##
|
188
|
+
# Send a command to the remote and attempt to parse the response as JSON
|
189
|
+
#
|
190
|
+
# @param [String] command
|
191
|
+
# The command to send to the server.
|
192
|
+
#
|
193
|
+
# @return [Array, String] The response received from the server. Unless it is
|
194
|
+
# the ping command, the JSON response is parsed into an array.
|
195
|
+
def send_command(command, attempt=0)
|
196
|
+
Timeout::timeout(timeout.to_f / 1000.0) do
|
197
|
+
connect! unless self.remote
|
198
|
+
self.remote.puts command
|
199
|
+
@response = self.remote.gets
|
200
|
+
unless command == "ping"
|
201
|
+
results = JSON.parse(@response)
|
202
|
+
else
|
203
|
+
results = @response.delete("\n")
|
204
|
+
end
|
205
|
+
results
|
206
|
+
end
|
207
|
+
rescue TimeoutError => e
|
208
|
+
if attempt < max_attempts
|
209
|
+
send_command(command, attempt+1)
|
210
|
+
else
|
211
|
+
raise CommandTimeoutError
|
212
|
+
end
|
213
|
+
rescue Exception => e
|
214
|
+
if attempt < max_attempts
|
215
|
+
send_command(command, attempt+1)
|
216
|
+
else
|
217
|
+
self.remote = nil
|
218
|
+
raise CommandError
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
require 'batsd/version'
|
224
|
+
require 'batsd/errors'
|
data/lib/batsd/errors.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
class Batsd
|
2
|
+
##
|
3
|
+
# Base error for all batsd errors.
|
4
|
+
class BaseError < RuntimeError
|
5
|
+
end
|
6
|
+
|
7
|
+
##
|
8
|
+
# Base error for connection related errors.
|
9
|
+
class BaseConnectionError < BaseError
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Raised when connection to a batsd server cannot be made.
|
14
|
+
class CannotConnectError < BaseConnectionError
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Raised when a new connection times out
|
19
|
+
class ConnectionTimeoutError < BaseConnectionError
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Base error for command related errors.
|
24
|
+
class BaseCommandError < BaseError
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Raised when a command could not be run
|
29
|
+
class CommandError < BaseCommandError
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Raised when a command times out
|
34
|
+
class CommandTimeoutError < BaseCommandError
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Raised when the values returned weren't the same as what was asked for
|
39
|
+
class InvalidValuesError < BaseError
|
40
|
+
end
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: batsd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ben Hathaway
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-08 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A Batsd client for Ruby. Provides an interface for querying data in a
|
15
|
+
batsd server.
|
16
|
+
email:
|
17
|
+
- ben@hathaway.cc
|
18
|
+
executables: []
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- .gitignore
|
23
|
+
- LICENSE
|
24
|
+
- README.md
|
25
|
+
- batsd.gemspec
|
26
|
+
- lib/batsd.rb
|
27
|
+
- lib/batsd/errors.rb
|
28
|
+
- lib/batsd/version.rb
|
29
|
+
homepage: https://github.com/hathaway/batsd-client
|
30
|
+
licenses: []
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 1.8.11
|
50
|
+
signing_key:
|
51
|
+
specification_version: 3
|
52
|
+
summary: Batsd Client
|
53
|
+
test_files: []
|