riemann-client 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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,7 @@
1
+ module Riemann
2
+ class Query
3
+ include Beefcake::Message
4
+
5
+ optional :string, :string, 1
6
+ end
7
+ 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
@@ -0,0 +1,3 @@
1
+ module Riemann
2
+ VERSION = '0.0.6'
3
+ 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: []