ustate-client 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -187,6 +187,16 @@ Then:
187
187
  c.query.states # => [UState::State(state: 'ok', service: 'My service')]
188
188
  c.query('state != "ok"').states # => []
189
189
 
190
+ Client state management
191
+ -----------------------
192
+
193
+ UState provides some classes to make managing state updates easier.
194
+
195
+ UState::MetricThread starts a thread to poll a metric periodically, which can
196
+ be used to flush an accumulated value to ustate at regular intervals.
197
+
198
+ UState::AutoState bundles a state and a client together. Any changes to the AutoState automatically send the new state to the client.
199
+
190
200
  The Dashboard
191
201
  =============
192
202
 
@@ -11,14 +11,15 @@ module UState
11
11
 
12
12
  @folds = {}
13
13
  @interval = opts[:interval] || INTERVAL
14
+ @server = opts[:server]
14
15
 
15
16
  start
16
17
  end
17
18
 
18
19
  # Combines states matching query with State.average
19
- def average(query, init = State.new)
20
+ def average(query, *a)
20
21
  fold query do |states|
21
- State.average states, init
22
+ State.average states, *a
22
23
  end
23
24
  end
24
25
 
@@ -45,24 +46,29 @@ module UState
45
46
  def start
46
47
  @runner = Thread.new do
47
48
  loop do
48
- interval = (@interval.to_f / @folds.size) rescue @interval
49
- @folds.each do |f, query|
50
- matching = @index.query(Query.new(string: query))
51
- unless matching.empty?
52
- if combined = f[matching]
53
- @index << combined
49
+ begin
50
+ interval = (@interval.to_f / @folds.size) rescue @interval
51
+ @folds.each do |f, query|
52
+ matching = @index.query(Query.new(string: query))
53
+ unless matching.empty?
54
+ if combined = f[matching]
55
+ @index << combined
56
+ end
54
57
  end
58
+ sleep interval
55
59
  end
56
- sleep interval
60
+ rescue Exception => e
61
+ @server.log.error e
62
+ sleep 1
57
63
  end
58
64
  end
59
65
  end
60
66
  end
61
67
 
62
68
  # Combines states matching query with State.sum
63
- def sum(query, init = State.new)
69
+ def sum(query, *a)
64
70
  fold query do |states|
65
- State.sum states, init
71
+ State.sum states, *a
66
72
  end
67
73
  end
68
74
  end
@@ -0,0 +1,125 @@
1
+ module UState
2
+ class AutoState
3
+ # Binds together a State 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 = State.new)
45
+ @client = client
46
+ @state = case state
47
+ when State
48
+ state
49
+ else
50
+ State.new state
51
+ end
52
+ end
53
+
54
+ def description=(description)
55
+ @state.description = description
56
+ flush
57
+ end
58
+
59
+ def description
60
+ @state.description
61
+ end
62
+
63
+ # Send state to client
64
+ def flush
65
+ @client << @state
66
+ end
67
+
68
+ def host=(host)
69
+ @state.host = host
70
+ flush
71
+ end
72
+
73
+ def host
74
+ @state.host
75
+ end
76
+
77
+ def metric_f=(metric_f)
78
+ @state.metric_f = metric_f
79
+ flush
80
+ end
81
+
82
+ def metric_f
83
+ @state.metric_f
84
+ end
85
+
86
+ # Performs multiple updates, followed by flush.
87
+ # Example: merge state: critical, metric_f: 10235.3
88
+ def merge(opts)
89
+ opts.each do |k, v|
90
+ @state.send "#{k}=", v
91
+ end
92
+ flush
93
+ end
94
+
95
+ # Issues an immediate update of the state with the :once option
96
+ # set, but does not update the local state. Useful for transient errors.
97
+ # Opts are merged with the state.
98
+ def once(opts)
99
+ o = @state.dup
100
+ opts.each do |k, v|
101
+ o.send "#{k}=", v
102
+ end
103
+ o.once = true
104
+ @client << o
105
+ end
106
+
107
+ def state=(state)
108
+ @state.state = state
109
+ flush
110
+ end
111
+
112
+ def state
113
+ @state.state
114
+ end
115
+
116
+ def service=(service)
117
+ @state.service = service
118
+ flush
119
+ end
120
+
121
+ def service
122
+ @state.service
123
+ end
124
+ end
125
+ end
@@ -23,9 +23,15 @@ class UState::Client
23
23
  # Send a state
24
24
  def <<(state_opts)
25
25
  # Create state
26
- state = UState::State.new(state_opts)
27
- state.time ||= Time.now.utc.to_i
28
- state.host ||= Socket.gethostname
26
+ case state_opts
27
+ when UState::State
28
+ state = state_opts
29
+ else
30
+ unless state_opts.include? :host
31
+ state_opts[:host] = Socket.gethostname
32
+ end
33
+ state = UState::State.new(state_opts)
34
+ end
29
35
 
30
36
  message = UState::Message.new :states => [state]
31
37
 
@@ -125,14 +125,16 @@ module UState
125
125
 
126
126
  # Compute maximum for each service
127
127
  maxima = if o[:global_maximum]
128
- max = states.map(&:metric).max
128
+ max = states.map(&:metric).compact.max
129
129
  services.inject({}) do |m, s|
130
130
  m[s] = max
131
131
  m
132
132
  end.merge o[:maxima]
133
133
  else
134
134
  states.inject(Hash.new(0)) do |m, s|
135
- m[s.service] = [s.metric, m[s.service]].max
135
+ if s.metric
136
+ m[s.service] = [s.metric, m[s.service]].max
137
+ end
136
138
  m
137
139
  end.merge o[:maxima]
138
140
  end
@@ -141,8 +143,16 @@ module UState
141
143
  # list of hosts explicitly given.
142
144
  hosts = o[:hosts] || states.map do |state|
143
145
  state.host
144
- end.compact
145
- hosts = hosts.uniq.sort
146
+ end
147
+ hosts = hosts.uniq.sort { |a, b|
148
+ if !a
149
+ -1
150
+ elsif !b
151
+ 1
152
+ else
153
+ a <=> b
154
+ end
155
+ }
146
156
 
147
157
  # Construct index
148
158
  by = states.inject({}) do |index, s|
@@ -23,6 +23,7 @@ module UState
23
23
  @from = opts[:from]
24
24
  @name = opts[:name]
25
25
  @host = opts[:host]
26
+ @server = opts[:server]
26
27
 
27
28
  @tell = {}
28
29
 
@@ -63,10 +64,14 @@ EOF
63
64
  # Dispatch emails to each address which is interested in this state
64
65
  def receive(*states)
65
66
  Thread.new do
66
- @tell.each do |address, q|
67
- if states.any? { |state| p state; q === state }
68
- email address, states.last
67
+ begin
68
+ @tell.each do |address, q|
69
+ if states.any? { |state| p state; q === state }
70
+ email address, states.last
71
+ end
69
72
  end
73
+ rescue Exception => e
74
+ @server.log.error e
70
75
  end
71
76
  end
72
77
  end
@@ -15,6 +15,7 @@ module UState
15
15
  @query = opts[:query]
16
16
  @host = opts[:host] || HOST
17
17
  @port = opts[:port] || PORT
18
+ @server = opts[:server]
18
19
  @interval = opts[:interval] || INTERVAL
19
20
  @locket = Mutex.new
20
21
 
@@ -68,10 +69,15 @@ module UState
68
69
  def start
69
70
  @runner = Thread.new do
70
71
  loop do
71
- @index.query(Query.new(string: @query)).each do |state|
72
- forward state
72
+ begin
73
+ @index.query(Query.new(string: @query)).each do |state|
74
+ forward state
75
+ end
76
+ sleep @interval
77
+ rescue Exception => e
78
+ @server.log.warn e
79
+ sleep 1
73
80
  end
74
- sleep @interval
75
81
  end
76
82
  end
77
83
  end
@@ -15,6 +15,7 @@ module UState
15
15
  require 'ustate/query_string'
16
16
  require 'ustate/query/ast'
17
17
  require 'ustate/metric_thread'
18
+ require 'logger'
18
19
  require 'mtrc'
19
20
 
20
21
  attr_accessor :backends
@@ -22,32 +23,37 @@ module UState
22
23
  attr_writer :aggregator
23
24
  attr_writer :emailer
24
25
  attr_writer :graphite
26
+
27
+ attr_accessor :log
25
28
 
26
29
  def initialize(opts = {})
27
30
  # Backends
28
31
  @backends = []
29
- b = Backends::TCP.new opts
32
+ b = Backends::TCP.new opts.merge(server: self)
30
33
  b.server = self
31
34
  @backends << b
32
35
 
33
- @index = Index.new
36
+ @index = Index.new :server => self
37
+
38
+ @log = Logger.new('ustate.log', 4, 134217728)
39
+ @log.level = Logger::INFO
34
40
 
35
41
  setup_signals
36
42
  end
37
43
 
38
44
  def aggregator(opts = {})
39
45
  require 'ustate/aggregator'
40
- @aggregator ||= UState::Aggregator.new(@index, opts)
46
+ @aggregator ||= UState::Aggregator.new(@index, opts.merge(server: self))
41
47
  end
42
48
 
43
49
  def emailer(opts = {})
44
50
  require 'ustate/emailer'
45
- @emailer ||= UState::Emailer.new(@index, opts)
51
+ @emailer ||= UState::Emailer.new(@index, opts.merge(server: self))
46
52
  end
47
53
 
48
54
  def graphite(opts = {})
49
55
  require 'ustate/graphite'
50
- @graphite ||= UState::Graphite.new(@index, opts)
56
+ @graphite ||= UState::Graphite.new(@index, opts.merge(server: self))
51
57
  end
52
58
 
53
59
  def start
@@ -13,6 +13,7 @@ class UState::Server::Backends::Base
13
13
 
14
14
  def initialize(opts = {})
15
15
  @connections = []
16
+ @server = opts[:server]
16
17
  @timeout = opts[:timeout] || TIMEOUT
17
18
  @maximum_connections = opts[:maximum_connections] || MAXIMUM_CONNECTIONS
18
19
  end
@@ -17,7 +17,7 @@ class UState::Server
17
17
 
18
18
  # Connect the server
19
19
  def connect
20
- puts "Listening on #{@host}:#{@port}"
20
+ @server.log.info "Listening on #{@host}:#{@port}"
21
21
  @signature = EventMachine.start_server(@host, @port, Connection, &method(:initialize_connection))
22
22
  end
23
23
 
@@ -25,6 +25,7 @@ module UState
25
25
  def initialize(opts = {})
26
26
  @db = Sequel.sqlite
27
27
 
28
+ @server = opts[:server]
28
29
  @threads = opts[:threads] || THREADS
29
30
  @pool = []
30
31
 
@@ -34,11 +34,10 @@ module UState
34
34
  init.service ||= mode states.map(&:service)
35
35
 
36
36
  # Time
37
- init.time ||= begin
38
- (states.inject(0) do |a, state|
39
- a + state.time.to_f
40
- end / states.size).to_i
41
- rescue
37
+ init.time = begin
38
+ times = states.map(&:time).compact
39
+ (times.inject(:+) / times.size).to_i
40
+ rescue
42
41
  end
43
42
  init.time ||= Time.now.to_i
44
43
 
@@ -69,10 +68,9 @@ module UState
69
68
  init.service ||= mode states.map(&:service)
70
69
 
71
70
  # Time
72
- init.time ||= begin
73
- (states.inject(0) do |a, state|
74
- a + state.time.to_f
75
- end / states.size).to_i
71
+ init.time = begin
72
+ times = states.map(&:time).compact
73
+ (times.inject(:+) / times.size).to_i
76
74
  rescue
77
75
  end
78
76
  init.time ||= Time.now.to_i
@@ -104,11 +102,10 @@ module UState
104
102
  end
105
103
 
106
104
  # Time
107
- init.time ||= begin
108
- (states.inject(0) { |a, state|
109
- a + state.time.to_f
110
- } / states.size).to_i
111
- rescue
105
+ init.time = begin
106
+ times = states.map(&:time).compact
107
+ (times.inject(:+) / times.size).to_i
108
+ rescue
112
109
  end
113
110
  init.time ||= Time.now.to_i
114
111
 
@@ -145,6 +142,12 @@ module UState
145
142
  end
146
143
  end
147
144
 
145
+ def initialize(*a)
146
+ super *a
147
+
148
+ @time ||= Time.now.to_i
149
+ end
150
+
148
151
  def metric
149
152
  @metric || metric_f
150
153
  end
@@ -1,3 +1,3 @@
1
1
  module UState
2
- VERSION = '0.0.4'
2
+ VERSION = '0.0.5'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ustate-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-09-13 00:00:00.000000000Z
12
+ date: 2011-09-17 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: beefcake
16
- requirement: &69429590 !ruby/object:Gem::Requirement
16
+ requirement: &73107330 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.3.5
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *69429590
24
+ version_requirements: *73107330
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: trollop
27
- requirement: &69428990 !ruby/object:Gem::Requirement
27
+ requirement: &73106920 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 1.16.2
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *69428990
35
+ version_requirements: *73106920
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: mtrc
38
- requirement: &69428610 !ruby/object:Gem::Requirement
38
+ requirement: &73106530 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: 0.0.4
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *69428610
46
+ version_requirements: *73106530
47
47
  description:
48
48
  email: aphyr@aphyr.com
49
49
  executables: []
@@ -54,6 +54,7 @@ files:
54
54
  - lib/ustate/graphite.rb
55
55
  - lib/ustate/query_string.treetop
56
56
  - lib/ustate/message.rb
57
+ - lib/ustate/auto_state.rb
57
58
  - lib/ustate/metric_thread.rb
58
59
  - lib/ustate/aggregator.rb
59
60
  - lib/ustate/version.rb