instrumental_agent 0.3.0 → 0.4.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.
@@ -20,8 +20,8 @@ module Instrumental
20
20
  @logger = l
21
21
  end
22
22
 
23
- def self.logger
24
- @logger ||= Logger.new('/dev/null')
23
+ def self.logger(force = false)
24
+ @logger ||= Logger.new(File.open('/dev/null', 'a')) # append mode so it's forksafe
25
25
  end
26
26
 
27
27
  def self.all
@@ -52,11 +52,14 @@ module Instrumental
52
52
  @port = (collector[1] || 8000).to_i
53
53
  @enabled = options[:enabled]
54
54
  @test_mode = options[:test_mode]
55
+ @pid = Process.pid
56
+
55
57
 
56
58
  if @enabled
57
59
  @failures = 0
58
60
  @queue = Queue.new
59
- start_connection_thread
61
+ start_connection_worker
62
+ setup_cleanup_at_exit
60
63
  end
61
64
  end
62
65
 
@@ -95,7 +98,7 @@ module Instrumental
95
98
  end
96
99
 
97
100
  def connected?
98
- connection && connection.connected
101
+ @socket && !@socket.closed?
99
102
  end
100
103
 
101
104
  def logger
@@ -125,6 +128,14 @@ module Instrumental
125
128
 
126
129
  def send_command(cmd, *args)
127
130
  if enabled?
131
+ if @pid != Process.pid
132
+ logger.info "Detected fork"
133
+ @pid = Process.pid
134
+ @socket = nil
135
+ @queue = Queue.new
136
+ start_connection_worker
137
+ end
138
+
128
139
  cmd = "%s %s\n" % [cmd, args.collect(&:to_s).join(" ")]
129
140
  if @queue.size < MAX_BUFFER
130
141
  logger.debug "Queueing: #{cmd.chomp}"
@@ -137,7 +148,7 @@ module Instrumental
137
148
  end
138
149
  end
139
150
 
140
- def test_server_connection
151
+ def test_connection
141
152
  # FIXME: Test connection state hack
142
153
  begin
143
154
  @socket.read_nonblock(1) # TODO: put data back?
@@ -146,43 +157,57 @@ module Instrumental
146
157
  end
147
158
  end
148
159
 
149
- def start_connection_thread
150
- logger.info "Starting thread"
151
- @thread = Thread.new do
152
- begin
153
- @socket = TCPSocket.new(host, port)
154
- @failures = 0
155
- logger.info "connected to collector"
156
- @socket.puts "hello version #{Instrumental::VERSION} test_mode #{@test_mode}"
157
- @socket.puts "authenticate #{@api_key}"
160
+ def start_connection_worker
161
+ if enabled?
162
+ disconnect
163
+ logger.info "Starting thread"
164
+ @thread = Thread.new do
158
165
  loop do
159
- command_and_args = @queue.pop
160
- begin
161
- test_server_connection
162
- rescue Exception => err
163
- @queue << command_and_args # connection dead, requeue
164
- raise err
165
- end
166
-
167
- if command_and_args == 'exit'
168
- logger.info "exiting, #{@queue.size} commands remain"
169
- @socket.flush
170
- Thread.exit
171
- else
172
- logger.debug "Sending: #{command_and_args.chomp}"
173
- @socket.puts command_and_args
174
- end
166
+ break if connection_worker
175
167
  end
176
- rescue Exception => err
177
- logger.error err.to_s
178
- # FIXME: not always a disconnect
179
- @failures += 1
180
- delay = [(@failures - 1) ** BACKOFF, MAX_RECONNECT_DELAY].min
181
- logger.info "disconnected, reconnect in #{delay}..."
182
- sleep delay
183
- retry
184
168
  end
185
169
  end
170
+ end
171
+
172
+ def connection_worker
173
+ command_and_args = nil
174
+ logger.info "connecting to collector"
175
+ @socket = TCPSocket.new(host, port)
176
+ @failures = 0
177
+ logger.info "connected to collector at #{host}:#{port}"
178
+ @socket.puts "hello version #{Instrumental::VERSION} test_mode #{@test_mode}"
179
+ @socket.puts "authenticate #{@api_key}"
180
+ loop do
181
+ command_and_args = @queue.pop
182
+ test_connection
183
+
184
+ case command_and_args
185
+ when 'exit'
186
+ logger.info "exiting, #{@queue.size} commands remain"
187
+ return true
188
+ else
189
+ logger.debug "Sending: #{command_and_args.chomp}"
190
+ @socket.puts command_and_args
191
+ command_and_args = nil
192
+ end
193
+ end
194
+ rescue Exception => err
195
+ logger.error err.to_s
196
+ if command_and_args
197
+ logger.debug "requeueing: #{command_and_args}"
198
+ @queue << command_and_args
199
+ end
200
+ disconnect
201
+ @failures += 1
202
+ delay = [(@failures - 1) ** BACKOFF, MAX_RECONNECT_DELAY].min
203
+ logger.info "disconnected, reconnect in #{delay}..."
204
+ sleep delay
205
+ retry
206
+ ensure
207
+ disconnect
208
+ end
209
+
210
+ def setup_cleanup_at_exit
186
211
  at_exit do
187
212
  if !@queue.empty? && @thread.alive?
188
213
  if @failures > 0
@@ -196,6 +221,16 @@ module Instrumental
196
221
  end
197
222
  end
198
223
  end
224
+
225
+ def disconnect
226
+ if connected?
227
+ logger.info "Disconnecting..."
228
+ @socket.flush
229
+ @socket.close
230
+ end
231
+ @socket = nil
232
+ end
233
+
199
234
  end
200
235
 
201
236
  end
@@ -1,3 +1,3 @@
1
1
  module Instrumental
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/spec/agent_spec.rb CHANGED
@@ -19,7 +19,7 @@ describe Instrumental::Agent, "disabled" do
19
19
  @server.connect_count.should == 0
20
20
  end
21
21
 
22
- it "should not connect to the server" do
22
+ it "should not connect to the server after receiving a metric" do
23
23
  wait
24
24
  @agent.gauge('disabled_test', 1)
25
25
  wait
@@ -149,6 +149,21 @@ describe Instrumental::Agent, "enabled" do
149
149
  @server.commands.last.should == "increment reconnect_test 1 1234"
150
150
  end
151
151
 
152
+ it "should automatically reconnect when forked" do
153
+ wait
154
+ @agent.increment('fork_reconnect_test', 1, 2)
155
+ fork do
156
+ @agent.increment('fork_reconnect_test', 1, 3) # triggers reconnect
157
+ end
158
+ wait
159
+ @agent.increment('fork_reconnect_test', 1, 4) # triggers reconnect
160
+ wait
161
+ @server.connect_count.should == 2
162
+ @server.commands.should include("increment fork_reconnect_test 1 2")
163
+ @server.commands.should include("increment fork_reconnect_test 1 3")
164
+ @server.commands.should include("increment fork_reconnect_test 1 4")
165
+ end
166
+
152
167
  it "should never let an exception reach the user" do
153
168
  @agent.stub!(:send_command).and_raise(Exception.new("Test Exception"))
154
169
  @agent.increment('throws_exception', 2).should be_nil
data/spec/test_server.rb CHANGED
@@ -5,6 +5,7 @@ class TestServer
5
5
  @connect_count = 0
6
6
  @connections = []
7
7
  @commands = []
8
+ @host = 'localhost'
8
9
  listen
9
10
  end
10
11
 
@@ -14,18 +15,22 @@ class TestServer
14
15
  Thread.new do
15
16
  begin
16
17
  # puts "listening"
17
- socket = @server.accept
18
- @connect_count += 1
19
- @connections << socket
20
- # puts "connection received"
21
18
  loop do
22
- command = socket.gets.strip
23
- # puts "got: #{command}"
24
- commands << command
19
+ socket = @server.accept
20
+ Thread.new do
21
+ @connect_count += 1
22
+ @connections << socket
23
+ # puts "connection received"
24
+ loop do
25
+ command = socket.gets.strip
26
+ # puts "got: #{command}"
27
+ commands << command
28
+ end
29
+ end
25
30
  end
26
31
  rescue Exception => err
27
32
  unless @stopping
28
- # puts "EXCEPTION:", err unless @stopping
33
+ puts "EXCEPTION:", err unless @stopping
29
34
  retry
30
35
  end
31
36
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: instrumental_agent
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 15
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 3
8
+ - 4
9
9
  - 0
10
- version: 0.3.0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Elijah Miller
@@ -17,7 +17,7 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2011-11-17 00:00:00 -05:00
20
+ date: 2011-12-01 00:00:00 -05:00
21
21
  default_executable:
22
22
  dependencies:
23
23
  - !ruby/object:Gem::Dependency