riemann-client 0.0.6
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/LICENSE +21 -0
- data/README.markdown +66 -0
- data/lib/riemann.rb +11 -0
- data/lib/riemann/auto_state.rb +120 -0
- data/lib/riemann/client.rb +89 -0
- data/lib/riemann/client/tcp.rb +86 -0
- data/lib/riemann/client/udp.rb +78 -0
- data/lib/riemann/event.rb +174 -0
- data/lib/riemann/message.rb +17 -0
- data/lib/riemann/metric_thread.rb +68 -0
- data/lib/riemann/query.rb +7 -0
- data/lib/riemann/state.rb +29 -0
- data/lib/riemann/version.rb +3 -0
- metadata +90 -0
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Kyle Kingsbury
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
Installing
|
2
|
+
==========
|
3
|
+
|
4
|
+
gem install riemann-client
|
5
|
+
|
6
|
+
Use
|
7
|
+
===
|
8
|
+
|
9
|
+
``` ruby
|
10
|
+
require 'riemann/client'
|
11
|
+
|
12
|
+
# Create a client. Host and port are optional.
|
13
|
+
c = Riemann::Client.new host: 'localhost', port: 5555
|
14
|
+
|
15
|
+
# Send a simple event
|
16
|
+
c << {service: 'testing', metric: 2.5}
|
17
|
+
|
18
|
+
# Or a more complex one
|
19
|
+
c << {
|
20
|
+
host: 'web3',
|
21
|
+
service: 'api latency',
|
22
|
+
state: 'warn',
|
23
|
+
metric: 63.5
|
24
|
+
description: "63.5 milliseconds per request"
|
25
|
+
time: Time.now.to_i - 10
|
26
|
+
}
|
27
|
+
|
28
|
+
# :host defaults to gethostname(). :time defaults to current unix time. You
|
29
|
+
# can explicitly override host...
|
30
|
+
|
31
|
+
c << {host: nil, service: 'the cloud', state: 'nebulous'}
|
32
|
+
|
33
|
+
# Get all the states from the server
|
34
|
+
c['true']
|
35
|
+
|
36
|
+
# Or specific states matching a query
|
37
|
+
c['host =~ "%.dc1" and (state = "critical" or state = "warning")']
|
38
|
+
|
39
|
+
```
|
40
|
+
|
41
|
+
Transports
|
42
|
+
==========
|
43
|
+
|
44
|
+
Riemann::Client sends small events over UDP by default, and uses TCP for
|
45
|
+
queries and large events. UDP sends are essentially "shouting into the void".
|
46
|
+
They will not block your application and are roughly an order of magnitude
|
47
|
+
faster than TCP, but you will not know if the server is down or encountered an
|
48
|
+
error. You can specify what transport to use by selecting a subclient:
|
49
|
+
|
50
|
+
``` ruby
|
51
|
+
c.udp << { :state "ok" } # => nil
|
52
|
+
c.tcp << { :state "ok" } # => #<Message ...>
|
53
|
+
c.tcp["true"] # => [#<Event ... >, ...]
|
54
|
+
c.udp["true"] # => raise Riemann::Client::Unsupported
|
55
|
+
```
|
56
|
+
|
57
|
+
Client state management
|
58
|
+
=======================
|
59
|
+
|
60
|
+
Riemann::Client provides some classes to make managing state updates easier.
|
61
|
+
|
62
|
+
Riemann::MetricThread starts a thread to poll a metric periodically, which can
|
63
|
+
be used to flush an accumulated value to ustate at regular intervals.
|
64
|
+
|
65
|
+
Riemann::AutoState bundles a state and a client together. Any changes to the
|
66
|
+
AutoState automatically send the new state to the client.
|
data/lib/riemann.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
module Riemann
|
2
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'beefcake'
|
5
|
+
require 'riemann/version'
|
6
|
+
require 'riemann/state'
|
7
|
+
require 'riemann/event'
|
8
|
+
require 'riemann/query'
|
9
|
+
require 'riemann/message'
|
10
|
+
require 'riemann/client'
|
11
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Riemann
|
2
|
+
class AutoState
|
3
|
+
# Binds together a state hash and a Client. Any change made here
|
4
|
+
# sends the state to the client. Useful when updates to a state are made
|
5
|
+
# decoherently, e.g. across many methods. Combine with MetricThread (or
|
6
|
+
# just Thread.new { loop { autostate.flush; sleep n } }) to ensure regular
|
7
|
+
# updates.
|
8
|
+
#
|
9
|
+
# example:
|
10
|
+
#
|
11
|
+
# class Job
|
12
|
+
# def initialize
|
13
|
+
# @state = AutoState.new
|
14
|
+
# @state.service = 'job'
|
15
|
+
# @state.state = 'starting up'
|
16
|
+
#
|
17
|
+
# run
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def run
|
21
|
+
# loop do
|
22
|
+
# begin
|
23
|
+
# a
|
24
|
+
# b
|
25
|
+
# rescue Exception => e
|
26
|
+
# @state.once(
|
27
|
+
# state: 'error',
|
28
|
+
# description: e.to_s
|
29
|
+
# )
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# def a
|
35
|
+
# @state.state = 'heavy lifting a'
|
36
|
+
# ...
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# def b
|
40
|
+
# @state.state = 'heavy lifting b'
|
41
|
+
# ...
|
42
|
+
# end
|
43
|
+
|
44
|
+
def initialize(client = Client.new, state = {})
|
45
|
+
@client = client
|
46
|
+
@state = state
|
47
|
+
end
|
48
|
+
|
49
|
+
def description=(description)
|
50
|
+
@state[:description] = description
|
51
|
+
flush
|
52
|
+
end
|
53
|
+
|
54
|
+
def description
|
55
|
+
@state[:description]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Send state to client
|
59
|
+
def flush
|
60
|
+
@state[:time] = Time.now.to_i
|
61
|
+
@client << @state
|
62
|
+
end
|
63
|
+
|
64
|
+
def host=(host)
|
65
|
+
@state[:host] = host
|
66
|
+
flush
|
67
|
+
end
|
68
|
+
|
69
|
+
def host
|
70
|
+
@state[:host]
|
71
|
+
end
|
72
|
+
|
73
|
+
def metric=(metric)
|
74
|
+
@state[:metric] = metric
|
75
|
+
flush
|
76
|
+
end
|
77
|
+
alias metric_f= metric=
|
78
|
+
|
79
|
+
def metric
|
80
|
+
@state[:metric]
|
81
|
+
end
|
82
|
+
alias metric_f metric
|
83
|
+
|
84
|
+
# Performs multiple updates, followed by flush.
|
85
|
+
# Example: merge state: critical, metric_f: 10235.3
|
86
|
+
def merge(opts)
|
87
|
+
@state.merge! opts
|
88
|
+
flush
|
89
|
+
end
|
90
|
+
alias << merge
|
91
|
+
|
92
|
+
# Issues an immediate update of the state with tag "once"
|
93
|
+
# set, but does not update the local state. Useful for transient errors.
|
94
|
+
# Opts are merged with the state.
|
95
|
+
def once(opts)
|
96
|
+
o = @state.merge opts
|
97
|
+
o[:time] = Time.now.to_i
|
98
|
+
o[:tags] = ((o[:tags] | ["once"]) rescue ["once"])
|
99
|
+
@client << o
|
100
|
+
end
|
101
|
+
|
102
|
+
def state=(state)
|
103
|
+
@state[:state] = state
|
104
|
+
flush
|
105
|
+
end
|
106
|
+
|
107
|
+
def state
|
108
|
+
@state[:state]
|
109
|
+
end
|
110
|
+
|
111
|
+
def service=(service)
|
112
|
+
@state[:service] = service
|
113
|
+
flush
|
114
|
+
end
|
115
|
+
|
116
|
+
def service
|
117
|
+
@state[:service]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'riemann'
|
2
|
+
|
3
|
+
class Riemann::Client
|
4
|
+
class Error < RuntimeError; end
|
5
|
+
class InvalidResponse < Error; end
|
6
|
+
class ServerError < Error; end
|
7
|
+
class Unsupported < Error; end
|
8
|
+
class TooBig < Unsupported; end
|
9
|
+
|
10
|
+
require 'thread'
|
11
|
+
require 'socket'
|
12
|
+
require 'time'
|
13
|
+
|
14
|
+
HOST = '127.0.0.1'
|
15
|
+
PORT = 5555
|
16
|
+
|
17
|
+
require 'riemann/client/tcp'
|
18
|
+
require 'riemann/client/udp'
|
19
|
+
|
20
|
+
attr_accessor :host, :port, :tcp, :udp
|
21
|
+
|
22
|
+
def initialize(opts = {})
|
23
|
+
@host = opts[:host] || HOST
|
24
|
+
@port = opts[:port] || PORT
|
25
|
+
@udp = UDP.new opts
|
26
|
+
@tcp = TCP.new opts
|
27
|
+
end
|
28
|
+
|
29
|
+
# Send a state
|
30
|
+
def <<(event_opts)
|
31
|
+
# Create state
|
32
|
+
case event_opts
|
33
|
+
when Riemann::State
|
34
|
+
event = event_opts
|
35
|
+
when Riemann::Event
|
36
|
+
event = event_opts
|
37
|
+
else
|
38
|
+
unless event_opts.include? :host
|
39
|
+
event_opts[:host] = Socket.gethostname
|
40
|
+
end
|
41
|
+
event = Riemann::Event.new(event_opts)
|
42
|
+
end
|
43
|
+
|
44
|
+
message = Riemann::Message.new :events => [event]
|
45
|
+
|
46
|
+
# Transmit
|
47
|
+
send_maybe_recv message
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns an array of states matching query.
|
51
|
+
def [](query)
|
52
|
+
response = query(query)
|
53
|
+
(response.events || []) |
|
54
|
+
(response.states || [])
|
55
|
+
end
|
56
|
+
|
57
|
+
# Close both UDP and TCP sockets.
|
58
|
+
def close
|
59
|
+
@udp.close
|
60
|
+
@tcp.close
|
61
|
+
end
|
62
|
+
|
63
|
+
# Connect both UDP and TCP sockets.
|
64
|
+
def connect
|
65
|
+
udp.connect
|
66
|
+
tcp.connect
|
67
|
+
end
|
68
|
+
|
69
|
+
def connected?
|
70
|
+
tcp.connected? and udp.connected?
|
71
|
+
end
|
72
|
+
|
73
|
+
# Ask for states
|
74
|
+
def query(string = "true")
|
75
|
+
send_recv Riemann::Message.new(query: Riemann::Query.new(string: string))
|
76
|
+
end
|
77
|
+
|
78
|
+
def send_recv(*a)
|
79
|
+
@tcp.send_recv *a
|
80
|
+
end
|
81
|
+
|
82
|
+
def send_maybe_recv(*a)
|
83
|
+
begin
|
84
|
+
@udp.send_maybe_recv *a
|
85
|
+
rescue TooBig
|
86
|
+
@tcp.send_maybe_recv *a
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Riemann
|
2
|
+
class Client
|
3
|
+
class TCP < Client
|
4
|
+
attr_accessor :host, :port, :socket
|
5
|
+
|
6
|
+
def initialize(opts = {})
|
7
|
+
@host = opts[:host] || HOST
|
8
|
+
@port = opts[:port] || PORT
|
9
|
+
@locket = Mutex.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def connect
|
13
|
+
@socket = TCPSocket.new(@host, @port)
|
14
|
+
end
|
15
|
+
|
16
|
+
def close
|
17
|
+
@locket.synchronize do
|
18
|
+
@socket.close
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def connected?
|
23
|
+
not @socket.closed?
|
24
|
+
end
|
25
|
+
|
26
|
+
# Read a message from a stream
|
27
|
+
def read_message(s)
|
28
|
+
if buffer = s.read(4) and buffer.size == 4
|
29
|
+
length = buffer.unpack('N').first
|
30
|
+
begin
|
31
|
+
str = s.read length
|
32
|
+
message = Riemann::Message.decode str
|
33
|
+
rescue => e
|
34
|
+
puts "Message was #{str.inspect}"
|
35
|
+
raise
|
36
|
+
end
|
37
|
+
|
38
|
+
unless message.ok
|
39
|
+
puts "Failed"
|
40
|
+
raise ServerError, message.error
|
41
|
+
end
|
42
|
+
|
43
|
+
message
|
44
|
+
else
|
45
|
+
raise InvalidResponse, "unexpected EOF"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def send_recv(message)
|
50
|
+
with_connection do |s|
|
51
|
+
s << message.encode_with_length
|
52
|
+
read_message s
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
alias send_maybe_recv send_recv
|
57
|
+
|
58
|
+
# Yields a connection in the block.
|
59
|
+
def with_connection
|
60
|
+
tries = 0
|
61
|
+
|
62
|
+
@locket.synchronize do
|
63
|
+
begin
|
64
|
+
tries += 1
|
65
|
+
yield (@socket || connect)
|
66
|
+
rescue IOError => e
|
67
|
+
raise if tries > 3
|
68
|
+
connect and retry
|
69
|
+
rescue Errno::EPIPE => e
|
70
|
+
raise if tries > 3
|
71
|
+
connect and retry
|
72
|
+
rescue Errno::ECONNREFUSED => e
|
73
|
+
raise if tries > 3
|
74
|
+
connect and retry
|
75
|
+
rescue Errno::ECONNRESET => e
|
76
|
+
raise if tries > 3
|
77
|
+
connect and retry
|
78
|
+
rescue InvalidResponse => e
|
79
|
+
raise if tries > 3
|
80
|
+
connect and retry
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Riemann
|
2
|
+
class Client
|
3
|
+
class UDP < Client
|
4
|
+
MAX_SIZE = 16384
|
5
|
+
|
6
|
+
attr_accessor :host, :port, :socket, :max_size
|
7
|
+
|
8
|
+
def initialize(opts = {})
|
9
|
+
@host = opts[:host] || HOST
|
10
|
+
@port = opts[:port] || PORT
|
11
|
+
@max_size = opts[:max_size] || MAX_SIZE
|
12
|
+
@locket = Mutex.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def connect
|
16
|
+
@socket = UDPSocket.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def close
|
20
|
+
@locket.synchronize do
|
21
|
+
@socket.close
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def connected?
|
26
|
+
not @socket.closed?
|
27
|
+
end
|
28
|
+
|
29
|
+
# Read a message from a stream
|
30
|
+
def read_message(s)
|
31
|
+
raise Unsupported
|
32
|
+
end
|
33
|
+
|
34
|
+
def send_recv(*a)
|
35
|
+
raise Unsupported
|
36
|
+
end
|
37
|
+
|
38
|
+
def send_maybe_recv(message)
|
39
|
+
with_connection do |s|
|
40
|
+
x = message.encode ''
|
41
|
+
unless x.length < @max_size
|
42
|
+
raise TooBig
|
43
|
+
end
|
44
|
+
|
45
|
+
s.send(x, 0, @host, @port)
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Yields a connection in the block.
|
51
|
+
def with_connection
|
52
|
+
tries = 0
|
53
|
+
|
54
|
+
@locket.synchronize do
|
55
|
+
begin
|
56
|
+
tries += 1
|
57
|
+
yield (@socket || connect)
|
58
|
+
rescue IOError => e
|
59
|
+
raise if tries > 3
|
60
|
+
connect and retry
|
61
|
+
rescue Errno::EPIPE => e
|
62
|
+
raise if tries > 3
|
63
|
+
connect and retry
|
64
|
+
rescue Errno::ECONNREFUSED => e
|
65
|
+
raise if tries > 3
|
66
|
+
connect and retry
|
67
|
+
rescue Errno::ECONNRESET => e
|
68
|
+
raise if tries > 3
|
69
|
+
connect and retry
|
70
|
+
rescue InvalidResponse => e
|
71
|
+
raise if tries > 3
|
72
|
+
connect and retry
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
module Riemann
|
2
|
+
class Event
|
3
|
+
include Beefcake::Message
|
4
|
+
|
5
|
+
optional :time, :int64, 1
|
6
|
+
optional :state, :string, 2
|
7
|
+
optional :service, :string, 3
|
8
|
+
optional :host, :string, 4
|
9
|
+
optional :description, :string, 5
|
10
|
+
repeated :tags, :string, 7
|
11
|
+
optional :ttl, :float, 8
|
12
|
+
optional :metric_f, :float, 15
|
13
|
+
|
14
|
+
# Average a set of states together. Chooses the mean metric, the mode
|
15
|
+
# state, mode service, and the mean time. If init is provided, its values
|
16
|
+
# override (where present) the computed ones.
|
17
|
+
def self.average(states, init = Event.new)
|
18
|
+
init = case init
|
19
|
+
when Event
|
20
|
+
init.dup
|
21
|
+
else
|
22
|
+
Event.new init
|
23
|
+
end
|
24
|
+
|
25
|
+
# Metric
|
26
|
+
init.metric_f ||= states.inject(0.0) { |a, state|
|
27
|
+
a + (state.metric || 0)
|
28
|
+
} / states.size
|
29
|
+
if init.metric_f.nan?
|
30
|
+
init.metric_f = 0.0
|
31
|
+
end
|
32
|
+
|
33
|
+
# Event
|
34
|
+
init.state ||= mode states.map(&:state)
|
35
|
+
init.service ||= mode states.map(&:service)
|
36
|
+
|
37
|
+
# Time
|
38
|
+
init.time = begin
|
39
|
+
times = states.map(&:time).compact
|
40
|
+
(times.inject(:+) / times.size).to_i
|
41
|
+
rescue
|
42
|
+
end
|
43
|
+
init.time ||= Time.now.to_i
|
44
|
+
|
45
|
+
init
|
46
|
+
end
|
47
|
+
|
48
|
+
# Sum a set of states together. Adds metrics, takes the mode state, mode
|
49
|
+
# service and the mean time. If init is provided, its values override
|
50
|
+
# (where present) the computed ones.
|
51
|
+
def self.sum(states, init = Event.new)
|
52
|
+
init = case init
|
53
|
+
when Event
|
54
|
+
init.dup
|
55
|
+
else
|
56
|
+
Event.new init
|
57
|
+
end
|
58
|
+
|
59
|
+
# Metric
|
60
|
+
init.metric_f ||= states.inject(0.0) { |a, state|
|
61
|
+
a + (state.metric || 0)
|
62
|
+
}
|
63
|
+
if init.metric_f.nan?
|
64
|
+
init.metric_f = 0.0
|
65
|
+
end
|
66
|
+
|
67
|
+
# Event
|
68
|
+
init.state ||= mode states.map(&:state)
|
69
|
+
init.service ||= mode states.map(&:service)
|
70
|
+
|
71
|
+
# Time
|
72
|
+
init.time = begin
|
73
|
+
times = states.map(&:time).compact
|
74
|
+
(times.inject(:+) / times.size).to_i
|
75
|
+
rescue
|
76
|
+
end
|
77
|
+
init.time ||= Time.now.to_i
|
78
|
+
|
79
|
+
init
|
80
|
+
end
|
81
|
+
|
82
|
+
# Finds the maximum of a set of states. Metric is the maximum. Event is the
|
83
|
+
# highest, as defined by Dash.config.state_order. Time is the mean.
|
84
|
+
def self.max(states, init = Event.new)
|
85
|
+
init = case init
|
86
|
+
when Event
|
87
|
+
init.dup
|
88
|
+
else
|
89
|
+
Event.new init
|
90
|
+
end
|
91
|
+
|
92
|
+
# Metric
|
93
|
+
init.metric_f ||= states.inject(0.0) { |a, state|
|
94
|
+
a + (state.metric || 0)
|
95
|
+
}
|
96
|
+
if init.metric_f.nan?
|
97
|
+
init.metric_f = 0.0
|
98
|
+
end
|
99
|
+
|
100
|
+
# Event
|
101
|
+
init.state ||= states.inject(nil) do |max, state|
|
102
|
+
state.state if Dash.config[:state_order][state.state] > Dash.config[:state_order][max]
|
103
|
+
end
|
104
|
+
|
105
|
+
# Time
|
106
|
+
init.time = begin
|
107
|
+
times = states.map(&:time).compact
|
108
|
+
(times.inject(:+) / times.size).to_i
|
109
|
+
rescue
|
110
|
+
end
|
111
|
+
init.time ||= Time.now.to_i
|
112
|
+
|
113
|
+
init
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.mode(array)
|
117
|
+
array.inject(Hash.new(0)) do |counts, e|
|
118
|
+
counts[e] += 1
|
119
|
+
counts
|
120
|
+
end.sort_by { |e, count| count }.last.first rescue nil
|
121
|
+
end
|
122
|
+
|
123
|
+
# Partition a list of states by a field
|
124
|
+
# Returns a hash of field_value => state
|
125
|
+
def self.partition(states, field)
|
126
|
+
states.inject({}) do |p, state|
|
127
|
+
k = state.send field
|
128
|
+
if p.include? k
|
129
|
+
p[k] << state
|
130
|
+
else
|
131
|
+
p[k] = [state]
|
132
|
+
end
|
133
|
+
p
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Sorts states by a field. nil values first.
|
138
|
+
def self.sort(states, field)
|
139
|
+
states.sort do |a, b|
|
140
|
+
a = a.send field
|
141
|
+
b = b.send field
|
142
|
+
if a.nil?
|
143
|
+
-1
|
144
|
+
elsif b.nil?
|
145
|
+
1
|
146
|
+
else
|
147
|
+
a <=> b
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def initialize(hash = nil)
|
153
|
+
if hash
|
154
|
+
if hash[:metric]
|
155
|
+
super hash.merge(metric_f: hash[:metric])
|
156
|
+
else
|
157
|
+
super hash
|
158
|
+
end
|
159
|
+
else
|
160
|
+
super()
|
161
|
+
end
|
162
|
+
|
163
|
+
@time ||= Time.now.to_i
|
164
|
+
end
|
165
|
+
|
166
|
+
def metric
|
167
|
+
metric_f
|
168
|
+
end
|
169
|
+
|
170
|
+
def metric=(m)
|
171
|
+
self.metric_f = m
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Riemann
|
2
|
+
class Message
|
3
|
+
include Beefcake::Message
|
4
|
+
|
5
|
+
optional :ok, :bool, 2
|
6
|
+
optional :error, :string, 3
|
7
|
+
repeated :states, State, 4
|
8
|
+
optional :query, Query, 5
|
9
|
+
repeated :events, Event, 6
|
10
|
+
|
11
|
+
def encode_with_length
|
12
|
+
buffer = ''
|
13
|
+
encoded = encode buffer
|
14
|
+
"#{[encoded.length].pack('N')}#{encoded}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class Riemann::MetricThread
|
2
|
+
# A metric thread is simple: it wraps some metric object which responds to <<,
|
3
|
+
# and every interval seconds, calls #flush which replaces the object and calls
|
4
|
+
# a user specified function.
|
5
|
+
|
6
|
+
INTERVAL = 10
|
7
|
+
|
8
|
+
attr_accessor :interval
|
9
|
+
attr_accessor :metric
|
10
|
+
|
11
|
+
# client = Riemann::Client.new
|
12
|
+
# m = MetricThread.new Mtrc::Rate do |rate|
|
13
|
+
# client << rate
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# loop do
|
17
|
+
# sleep rand
|
18
|
+
# m << rand
|
19
|
+
# end
|
20
|
+
def initialize(klass, *klass_args, &f)
|
21
|
+
@klass = klass
|
22
|
+
@klass_args = klass_args
|
23
|
+
@f = f
|
24
|
+
@interval = INTERVAL
|
25
|
+
|
26
|
+
@metric = new_metric
|
27
|
+
|
28
|
+
start
|
29
|
+
end
|
30
|
+
|
31
|
+
def <<(*a)
|
32
|
+
@metric.<<(*a)
|
33
|
+
end
|
34
|
+
|
35
|
+
def new_metric
|
36
|
+
@klass.new *@klass_args
|
37
|
+
end
|
38
|
+
|
39
|
+
def flush
|
40
|
+
old, @metric = @metric, new_metric
|
41
|
+
@f[old]
|
42
|
+
end
|
43
|
+
|
44
|
+
def start
|
45
|
+
raise RuntimeError, "already running" if @runner
|
46
|
+
|
47
|
+
@running = true
|
48
|
+
@runner = Thread.new do
|
49
|
+
while @running
|
50
|
+
sleep @interval
|
51
|
+
begin
|
52
|
+
flush
|
53
|
+
rescue Exception => e
|
54
|
+
end
|
55
|
+
end
|
56
|
+
@runner = nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def stop
|
61
|
+
stop!
|
62
|
+
@runner.join
|
63
|
+
end
|
64
|
+
|
65
|
+
def stop!
|
66
|
+
@running = false
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Riemann
|
2
|
+
class State
|
3
|
+
include Beefcake::Message
|
4
|
+
|
5
|
+
optional :time, :int64, 1
|
6
|
+
optional :state, :string, 2
|
7
|
+
optional :service, :string, 3
|
8
|
+
optional :host, :string, 4
|
9
|
+
optional :description, :string, 5
|
10
|
+
optional :once, :bool, 6
|
11
|
+
repeated :tags, :string, 7
|
12
|
+
optional :ttl, :float, 8
|
13
|
+
optional :metric_f, :float, 15
|
14
|
+
|
15
|
+
def initialize(*a)
|
16
|
+
super *a
|
17
|
+
|
18
|
+
@time ||= Time.now.to_i
|
19
|
+
end
|
20
|
+
|
21
|
+
def metric
|
22
|
+
@metric || metric_f
|
23
|
+
end
|
24
|
+
|
25
|
+
def metric=(m)
|
26
|
+
@metric = m
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: riemann-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.6
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Kyle Kingsbury
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-02-24 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: beefcake
|
16
|
+
requirement: &15270360 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.3.5
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *15270360
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: trollop
|
27
|
+
requirement: &15269600 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.16.2
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *15269600
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: mtrc
|
38
|
+
requirement: &15268880 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.0.4
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *15268880
|
47
|
+
description:
|
48
|
+
email: aphyr@aphyr.com
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- lib/riemann/event.rb
|
54
|
+
- lib/riemann/message.rb
|
55
|
+
- lib/riemann/auto_state.rb
|
56
|
+
- lib/riemann/metric_thread.rb
|
57
|
+
- lib/riemann/version.rb
|
58
|
+
- lib/riemann/state.rb
|
59
|
+
- lib/riemann/query.rb
|
60
|
+
- lib/riemann/client/udp.rb
|
61
|
+
- lib/riemann/client/tcp.rb
|
62
|
+
- lib/riemann/client.rb
|
63
|
+
- lib/riemann.rb
|
64
|
+
- LICENSE
|
65
|
+
- README.markdown
|
66
|
+
homepage: https://github.com/aphyr/riemann-ruby-client
|
67
|
+
licenses: []
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.8.7
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project: riemann-client
|
86
|
+
rubygems_version: 1.8.10
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Client for the distributed event system Riemann.
|
90
|
+
test_files: []
|