instrumental_agent 0.13.0 → 0.13.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 68d5c4451d15ba2eeb83f189d14f4a34c6ec77fd
4
- data.tar.gz: 6e7e2fd6ec37a5eb210563deec7c04ee91794a84
3
+ metadata.gz: d22dc8e7673c5701cf1cdc654ebd8b953eff28c7
4
+ data.tar.gz: 53fa9eb53ace63abe3a243b3ce8397bf3d53f01f
5
5
  SHA512:
6
- metadata.gz: 7973eec6253fe0e536480351318746c2ab6561a272485d39297b13e4cc50fdda98dd32fd538baf22c7eac9128ba61cb64982b0c221002155bf14a77862d04ede
7
- data.tar.gz: c1979ec7dcc1f04c3c5d4aaad1debf8d6ffecaa87ee2e527332e3fda2d96904db701f2f5c922897f09ed841775c3c84233682edb18c101b963553aeaba5c42e7
6
+ metadata.gz: bc8ec3269967f078690443c315c0dfe1d450603855b2f9cb31a01f3625cb00642cb1e5975cfdc7b3ee25c04c9f4b5249f4c1d5b1aa8fc7e0fed5086fb5f9fc49
7
+ data.tar.gz: 3c846c8a723c0c5e9b5c1998a78d330caad7a7b67e663ebb8a5abd21e8866348af0538ef14149b7bd897190930703e9e2c990cb5168caa9fa929941354f762ea
data/.travis.yml CHANGED
@@ -1,4 +1,3 @@
1
- sudo: false
2
1
  language: ruby
3
2
  rvm:
4
3
  - 1.8.7
@@ -7,3 +6,5 @@ rvm:
7
6
  - 2.0.0
8
7
  - jruby-18mode
9
8
  - jruby-19mode
9
+ - rbx-18mode
10
+ - rbx-19mode
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ### 0.13.1 [August 3, 2015]
2
+ * Revert encrypted support
3
+
1
4
  ### 0.13.0 [August 3, 2015]
2
5
  * Add support for encrypted transport, tests for same
3
6
 
data/Gemfile CHANGED
@@ -1,8 +1,8 @@
1
1
  source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
- ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
5
- if RUBY_VERSION < "1.9" && !%w{jruby rbx}.include?(ruby_engine)
4
+
5
+ if RUBY_VERSION < "1.9"
6
6
  # Built and installed via ext/mkrf_conf.rb
7
7
  gem 'system_timer', '~> 1.2'
8
8
  end
data/Rakefile CHANGED
@@ -1,4 +1,3 @@
1
- require 'bundler/setup'
2
1
  require 'bundler/gem_tasks'
3
2
  require 'rspec/core/rake_task'
4
3
 
@@ -19,4 +19,10 @@ Gem::Specification.new do |s|
19
19
  s.add_development_dependency(%q<rake>, [">= 0"])
20
20
  s.add_development_dependency(%q<rspec>, ["~> 2.0"])
21
21
  s.add_development_dependency(%q<fuubar>, [">= 0"])
22
+ if RUBY_VERSION >= "1.9.2"
23
+ s.add_development_dependency(%q<guard>, [">= 0"])
24
+ s.add_development_dependency(%q<guard-rspec>, [">= 0"])
25
+ s.add_development_dependency(%q<growl>, [">= 0"])
26
+ s.add_development_dependency(%q<rb-fsevent>, [">= 0"])
27
+ end
22
28
  end
@@ -1,24 +1,22 @@
1
1
  require 'instrumental/version'
2
2
  require 'instrumental/system_timer'
3
3
  require 'logger'
4
- require 'openssl' rescue nil
5
4
  require 'thread'
6
5
  require 'socket'
7
6
 
8
7
 
9
8
  module Instrumental
10
9
  class Agent
11
- BACKOFF = 2.0
12
- CONNECT_TIMEOUT = 20
13
- EXIT_FLUSH_TIMEOUT = 5
14
- HOSTNAME = Socket.gethostbyname(Socket.gethostname).first rescue Socket.gethostname
15
- MAX_BUFFER = 5000
10
+ BACKOFF = 2.0
16
11
  MAX_RECONNECT_DELAY = 15
17
- REPLY_TIMEOUT = 10
18
-
12
+ MAX_BUFFER = 5000
13
+ REPLY_TIMEOUT = 10
14
+ CONNECT_TIMEOUT = 20
15
+ EXIT_FLUSH_TIMEOUT = 5
16
+ HOSTNAME = Socket.gethostbyname(Socket.gethostname).first rescue Socket.gethostname
19
17
 
20
18
  attr_accessor :host, :port, :synchronous, :queue
21
- attr_reader :connection, :enabled, :secure
19
+ attr_reader :connection, :enabled
22
20
 
23
21
  def self.logger=(l)
24
22
  @logger = l
@@ -44,33 +42,18 @@ module Instrumental
44
42
 
45
43
  # defaults
46
44
  # host: collector.instrumentalapp.com
47
- # port: 8001
45
+ # port: 8000
48
46
  # enabled: true
49
47
  # synchronous: false
50
- # secure: true
51
- # verify: true
52
48
  @api_key = api_key
53
49
  @host, @port = options[:collector].to_s.split(':')
54
50
  @host ||= 'collector.instrumentalapp.com'
55
- requested_secure = options[:secure] == true
56
- desired_secure = options[:secure].nil? ? allows_secure? : !!options[:secure]
57
- if !allows_secure? && desired_secure
58
- logger.warn "Cannot connect to Instrumental via encrypted transport, SSL not available"
59
- if requested_secure
60
- options[:enabled] = false
61
- logger.error "You requested secure protocol to connect to Instrumental, but it is not available on this system (OpenSSL is not defined). Connecting to Instrumental has been disabled."
62
- end
63
- desired_secure = false
64
- end
65
- @secure = desired_secure
66
- @verify_cert = options[:verify_cert].nil? ? true : !!options[:verify_cert]
67
- default_port = @secure ? 8001 : 8000
68
- @port = (@port || default_port).to_i
51
+ @port = (@port || 8000).to_i
69
52
  @enabled = options.has_key?(:enabled) ? !!options[:enabled] : true
70
53
  @synchronous = !!options[:synchronous]
71
54
  @pid = Process.pid
72
55
  @allow_reconnect = true
73
- @certs = certificates
56
+
74
57
  setup_cleanup_at_exit if @enabled
75
58
  end
76
59
 
@@ -308,41 +291,10 @@ module Instrumental
308
291
  message
309
292
  end
310
293
 
311
- def wait_exceptions
312
- classes = [Errno::EAGAIN]
313
- if defined?(IO::EAGAINWaitReadable)
314
- classes << IO::EAGAINWaitReadable
315
- end
316
- if defined?(IO::EWOULDBLOCKWaitReadable)
317
- classes << IO::EWOULDBLOCKWaitReadable
318
- end
319
- if defined?(IO::WaitReadable)
320
- classes << IO::WaitReadable
321
- end
322
- classes
323
- end
324
-
325
-
326
294
  def test_connection
327
295
  begin
328
- # In the case where the socket is an OpenSSL::SSL::SSLSocket,
329
- # on Ruby 1.8.6, 1.8.7 or 1.9.1, read_nonblock does not exist,
330
- # and so the case of testing socket liveliness via a nonblocking
331
- # read that catches a wait condition won't work.
332
- #
333
- # We grab the SSL socket's underlying IO object and perform the
334
- # non blocking read there in order to ensure the socket is still
335
- # valid
336
- if @socket.respond_to?(:read_nonblock)
337
- @socket.read_nonblock(1)
338
- elsif @socket.respond_to?(:io)
339
- # The SSL Socket may send down additional data at close time,
340
- # so we perform two nonblocking reads, one to pull any pending
341
- # data on the socket, and the second to actually perform the connection
342
- # liveliness test
343
- @socket.io.read_nonblock(1024) && @socket.io.read_nonblock(1024)
344
- end
345
- rescue *wait_exceptions
296
+ @socket.read_nonblock(1)
297
+ rescue Errno::EAGAIN
346
298
  # noop
347
299
  end
348
300
  end
@@ -371,29 +323,15 @@ module Instrumental
371
323
  end
372
324
  end
373
325
 
374
- def open_socket(remote_host, remote_port, secure, verify_cert)
375
- sock = TCPSocket.open(remote_host, remote_port)
376
- if secure
377
- context = OpenSSL::SSL::SSLContext.new()
378
- if verify_cert
379
- context.set_params(:verify_mode => OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT)
380
- else
381
- context.set_params(:verify_mode => OpenSSL::SSL::VERIFY_NONE)
382
- end
383
- ssl_socket = OpenSSL::SSL::SSLSocket.new(sock, context)
384
- ssl_socket.sync_close = true
385
- ssl_socket.connect
386
- sock = ssl_socket
387
- end
388
- sock
389
- end
326
+
390
327
 
391
328
  def run_worker_loop
392
329
  command_and_args = nil
393
330
  command_options = nil
394
331
  logger.info "connecting to collector"
332
+ @socket = Socket.new(Socket::PF_INET, Socket::SOCK_STREAM, 0)
395
333
  with_timeout(CONNECT_TIMEOUT) do
396
- @socket = open_socket(host, port, @secure, @verify_cert)
334
+ @socket.connect Socket.pack_sockaddr_in(port, ipv4_address_for_host(host, port))
397
335
  end
398
336
  logger.info "connected to collector at #{host}:#{port}"
399
337
  hello_options = {
@@ -466,17 +404,11 @@ module Instrumental
466
404
  !@thread.nil? && @pid == Process.pid
467
405
  end
468
406
 
469
- def flush_socket(socket)
470
- socket.flush
471
- end
472
-
473
407
  def disconnect
474
408
  if connected?
475
409
  logger.info "Disconnecting..."
476
410
  begin
477
- with_timeout(EXIT_FLUSH_TIMEOUT) do
478
- flush_socket(@socket)
479
- end
411
+ with_timeout(EXIT_FLUSH_TIMEOUT) { @socket.flush }
480
412
  rescue Timeout::Error
481
413
  logger.info "Timed out flushing socket..."
482
414
  end
@@ -485,21 +417,6 @@ module Instrumental
485
417
  @socket = nil
486
418
  end
487
419
 
488
- def allows_secure?
489
- defined?(OpenSSL)
490
- end
491
-
492
- def certificates
493
- if allows_secure?
494
- base_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", ".."))
495
- %w{equifax geotrust rapidssl}.map do |name|
496
- OpenSSL::X509::Certificate.new(File.open(File.join(base_dir, "certs", "#{name}.ca.pem")))
497
- end
498
- else
499
- []
500
- end
501
- end
502
-
503
420
  end
504
421
 
505
422
  end
@@ -1,3 +1,3 @@
1
1
  module Instrumental
2
- VERSION = "0.13.0"
2
+ VERSION = "0.13.1"
3
3
  end
data/spec/agent_spec.rb CHANGED
@@ -1,485 +1,460 @@
1
1
  require 'spec_helper'
2
2
 
3
- def wait(n=0.2)
4
- sleep n # FIXME: hack
3
+ def wait
4
+ sleep 0.2 # FIXME: hack
5
5
  end
6
6
 
7
- FORK_SUPPORTED = begin
8
- Process.wait(fork { true })
9
- true
10
- rescue Exception => e
11
- false
12
- end
7
+ describe Instrumental::Agent, "disabled" do
8
+ before do
9
+ Instrumental::Agent.logger.level = Logger::UNKNOWN
10
+ @server = TestServer.new
11
+ @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :enabled => false)
12
+ end
13
13
 
14
+ after do
15
+ @agent.stop
16
+ @agent = nil
17
+ @server.stop
18
+ end
14
19
 
20
+ it "should not connect to the server" do
21
+ wait
22
+ @server.connect_count.should == 0
23
+ end
15
24
 
16
- shared_examples "Instrumental Agent" do
17
- context do
25
+ it "should not connect to the server after receiving a metric" do
26
+ wait
27
+ @agent.gauge('disabled_test', 1)
28
+ wait
29
+ @server.connect_count.should == 0
30
+ end
18
31
 
19
- # Inferred:
20
- # secure? and verify_cert? are set
32
+ it "should no op on flush without reconnect" do
33
+ 1.upto(100) { @agent.gauge('disabled_test', 1) }
34
+ @agent.flush(false)
35
+ wait
36
+ @server.commands.should be_empty
37
+ end
21
38
 
22
- # Agent options
23
- let(:enabled) { true }
24
- let(:synchronous) { false }
25
- let(:token) { 'test_token' }
26
- let(:agent) { Instrumental::Agent.new(token, :collector => server.host_and_port, :synchronous => synchronous, :enabled => enabled, :secure => secure?, :verify_cert => verify_cert?) }
39
+ it "should no op on flush with reconnect" do
40
+ 1.upto(100) { @agent.gauge('disabled_test', 1) }
41
+ @agent.flush(true)
42
+ wait
43
+ @server.commands.should be_empty
44
+ end
27
45
 
28
- # Server options
29
- let(:listen) { true }
30
- let(:response) { true }
31
- let(:authenticate) { true }
32
- let(:server) { TestServer.new(:listen => listen, :authenticate => authenticate, :response => response, :secure => secure?) }
46
+ it "should no op on an empty flush" do
47
+ @agent.flush(true)
48
+ wait
49
+ @server.commands.should be_empty
50
+ end
33
51
 
34
- before do
35
- Instrumental::Agent.logger.level = Logger::UNKNOWN
36
- @server = server
37
- wait
38
- end
52
+ it "should send metrics to logger" do
53
+ now = Time.now
54
+ @agent.logger.should_receive(:debug).with("gauge metric 1 #{now.to_i} 1")
55
+ @agent.gauge("metric", 1)
56
+ end
57
+ end
39
58
 
40
- after do
41
- agent.stop
42
- server.stop
43
- end
59
+ describe Instrumental::Agent, "enabled" do
60
+ before do
61
+ @server = TestServer.new
62
+ @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
63
+ end
44
64
 
45
- describe Instrumental::Agent, "disabled" do
46
- let(:enabled) { false }
65
+ after do
66
+ @agent.stop
67
+ @agent = nil
68
+ @server.stop
69
+ end
47
70
 
48
- it "should not connect to the server" do
49
- server.connect_count.should == 0
50
- end
71
+ it "should not connect to the server" do
72
+ wait
73
+ @server.connect_count.should == 0
74
+ end
51
75
 
52
- it "should not connect to the server after receiving a metric" do
53
- agent.gauge('disabled_test', 1)
54
- wait
55
- server.connect_count.should == 0
56
- end
76
+ it "should connect to the server after sending a metric" do
77
+ @agent.increment("test.foo")
78
+ wait
79
+ @server.connect_count.should == 1
80
+ end
57
81
 
58
- it "should no op on flush without reconnect" do
59
- 1.upto(100) { agent.gauge('disabled_test', 1) }
60
- agent.flush(false)
61
- wait
62
- server.commands.should be_empty
63
- end
82
+ it "should announce itself, and include version" do
83
+ @agent.increment("test.foo")
84
+ wait
85
+ @server.commands[0].should =~ /hello .*/
86
+ @server.commands[0].should =~ / version /
87
+ @server.commands[0].should =~ / hostname /
88
+ @server.commands[0].should =~ / pid /
89
+ @server.commands[0].should =~ / runtime /
90
+ @server.commands[0].should =~ / platform /
91
+ end
64
92
 
65
- it "should no op on flush with reconnect" do
66
- 1.upto(100) { agent.gauge('disabled_test', 1) }
67
- agent.flush(true)
68
- wait
69
- server.commands.should be_empty
70
- end
93
+ it "should authenticate using the token" do
94
+ @agent.increment("test.foo")
95
+ wait
96
+ @server.commands[1].should == "authenticate test_token"
97
+ end
71
98
 
72
- it "should no op on an empty flush" do
73
- agent.flush(true)
74
- wait
75
- server.commands.should be_empty
76
- end
99
+ it "should report a gauge" do
100
+ now = Time.now
101
+ @agent.gauge('gauge_test', 123)
102
+ wait
103
+ @server.commands.last.should == "gauge gauge_test 123 #{now.to_i} 1"
104
+ end
77
105
 
78
- it "should send metrics to logger" do
79
- now = Time.now
80
- agent.logger.should_receive(:debug).with("gauge metric 1 #{now.to_i} 1")
81
- agent.gauge("metric", 1)
82
- end
83
- end
106
+ it "should report a time as gauge and return the block result" do
107
+ now = Time.now
108
+ @agent.time("time_value_test") do
109
+ 1 + 1
110
+ end.should == 2
111
+ wait
112
+ @server.commands.last.should =~ /gauge time_value_test .* #{now.to_i}/
113
+ end
84
114
 
85
- describe Instrumental::Agent, "enabled" do
115
+ it "should return the value gauged" do
116
+ now = Time.now
117
+ @agent.gauge('gauge_test', 123).should == 123
118
+ @agent.gauge('gauge_test', 989).should == 989
119
+ wait
120
+ end
86
121
 
87
- it "should not connect to the server" do
88
- server.connect_count.should == 0
89
- end
122
+ it "should report a gauge with a set time" do
123
+ @agent.gauge('gauge_test', 123, 555)
124
+ wait
125
+ @server.commands.last.should == "gauge gauge_test 123 555 1"
126
+ end
90
127
 
91
- it "should connect to the server after sending a metric" do
92
- agent.increment("test.foo")
93
- wait
94
- server.connect_count.should == 1
95
- end
128
+ it "should report a gauge with a set time and count" do
129
+ @agent.gauge('gauge_test', 123, 555, 111)
130
+ wait
131
+ @server.commands.last.should == "gauge gauge_test 123 555 111"
132
+ end
96
133
 
97
- it "should announce itself, and include version" do
98
- agent.increment("test.foo")
99
- wait
100
- server.commands[0].should =~ /hello .*/
101
- server.commands[0].should =~ / version /
102
- server.commands[0].should =~ / hostname /
103
- server.commands[0].should =~ / pid /
104
- server.commands[0].should =~ / runtime /
105
- server.commands[0].should =~ / platform /
106
- end
134
+ it "should report an increment" do
135
+ now = Time.now
136
+ @agent.increment("increment_test")
137
+ wait
138
+ @server.commands.last.should == "increment increment_test 1 #{now.to_i} 1"
139
+ end
107
140
 
108
- it "should authenticate using the token" do
109
- agent.increment("test.foo")
110
- wait
111
- server.commands[1].should == "authenticate test_token"
112
- end
141
+ it "should return the value incremented by" do
142
+ now = Time.now
143
+ @agent.increment("increment_test").should == 1
144
+ @agent.increment("increment_test", 5).should == 5
145
+ wait
146
+ end
113
147
 
114
- it "should report a gauge" do
115
- now = Time.now
116
- agent.gauge('gauge_test', 123)
117
- wait
118
- server.commands.last.should == "gauge gauge_test 123 #{now.to_i} 1"
119
- end
148
+ it "should report an increment a value" do
149
+ now = Time.now
150
+ @agent.increment("increment_test", 2)
151
+ wait
152
+ @server.commands.last.should == "increment increment_test 2 #{now.to_i} 1"
153
+ end
120
154
 
121
- it "should report a time as gauge and return the block result" do
122
- now = Time.now
123
- agent.time("time_value_test") do
124
- 1 + 1
125
- end.should == 2
126
- wait
127
- server.commands.last.should =~ /gauge time_value_test .* #{now.to_i}/
128
- end
155
+ it "should report an increment with a set time" do
156
+ @agent.increment('increment_test', 1, 555)
157
+ wait
158
+ @server.commands.last.should == "increment increment_test 1 555 1"
159
+ end
129
160
 
130
- it "should return the value gauged" do
131
- now = Time.now
132
- agent.gauge('gauge_test', 123).should == 123
133
- agent.gauge('gauge_test', 989).should == 989
134
- wait
135
- end
161
+ it "should report an increment with a set time and count" do
162
+ @agent.increment('increment_test', 1, 555, 111)
163
+ wait
164
+ @server.commands.last.should == "increment increment_test 1 555 111"
165
+ end
136
166
 
137
- it "should report a gauge with a set time" do
138
- agent.gauge('gauge_test', 123, 555)
139
- wait
140
- server.commands.last.should == "gauge gauge_test 123 555 1"
167
+ it "should discard data that overflows the buffer" do
168
+ with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
169
+ 5.times do |i|
170
+ @agent.increment('overflow_test', i + 1, 300)
141
171
  end
172
+ wait
173
+ @server.commands.should include("increment overflow_test 1 300 1")
174
+ @server.commands.should include("increment overflow_test 2 300 1")
175
+ @server.commands.should include("increment overflow_test 3 300 1")
176
+ @server.commands.should_not include("increment overflow_test 4 300 1")
177
+ @server.commands.should_not include("increment overflow_test 5 300 1")
178
+ end
179
+ end
142
180
 
143
- it "should report a gauge with a set time and count" do
144
- agent.gauge('gauge_test', 123, 555, 111)
145
- wait
146
- server.commands.last.should == "gauge gauge_test 123 555 111"
147
- end
181
+ it "should send all data in synchronous mode" do
182
+ with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
183
+ @agent.synchronous = true
184
+ 5.times do |i|
185
+ @agent.increment('overflow_test', i + 1, 300)
186
+ end
187
+ @agent.instance_variable_get(:@queue).size.should == 0
188
+ wait # let the server receive the commands
189
+ @server.commands.should include("increment overflow_test 1 300 1")
190
+ @server.commands.should include("increment overflow_test 2 300 1")
191
+ @server.commands.should include("increment overflow_test 3 300 1")
192
+ @server.commands.should include("increment overflow_test 4 300 1")
193
+ @server.commands.should include("increment overflow_test 5 300 1")
194
+ end
195
+ end
148
196
 
149
- it "should report an increment" do
150
- now = Time.now
151
- agent.increment("increment_test")
152
- wait
153
- server.commands.last.should == "increment increment_test 1 #{now.to_i} 1"
154
- end
197
+ it "should automatically reconnect when forked" do
198
+ wait
199
+ @agent.increment('fork_reconnect_test', 1, 2)
200
+ fork do
201
+ @agent.increment('fork_reconnect_test', 1, 3) # triggers reconnect
202
+ end
203
+ wait
204
+ @agent.increment('fork_reconnect_test', 1, 4) # triggers reconnect
205
+ wait
206
+ @server.connect_count.should == 2
207
+ @server.commands.should include("increment fork_reconnect_test 1 2 1")
208
+ @server.commands.should include("increment fork_reconnect_test 1 3 1")
209
+ @server.commands.should include("increment fork_reconnect_test 1 4 1")
210
+ end
155
211
 
156
- it "should return the value incremented by" do
157
- now = Time.now
158
- agent.increment("increment_test").should == 1
159
- agent.increment("increment_test", 5).should == 5
160
- wait
161
- end
212
+ it "should never let an exception reach the user" do
213
+ @agent.stub!(:send_command).and_raise(Exception.new("Test Exception"))
214
+ @agent.increment('throws_exception', 2).should be_nil
215
+ wait
216
+ @agent.gauge('throws_exception', 234).should be_nil
217
+ wait
218
+ end
162
219
 
163
- it "should report an increment a value" do
164
- now = Time.now
165
- agent.increment("increment_test", 2)
166
- wait
167
- server.commands.last.should == "increment increment_test 2 #{now.to_i} 1"
168
- end
220
+ it "should let exceptions in time bubble up" do
221
+ expect { @agent.time('za') { raise "fail" } }.to raise_error
222
+ end
169
223
 
170
- it "should report an increment with a set time" do
171
- agent.increment('increment_test', 1, 555)
172
- wait
173
- server.commands.last.should == "increment increment_test 1 555 1"
174
- end
224
+ it "should return nil if the user overflows the MAX_BUFFER" do
225
+ 1.upto(Instrumental::Agent::MAX_BUFFER) do
226
+ @agent.increment("test").should == 1
227
+ thread = @agent.instance_variable_get(:@thread)
228
+ thread.kill
229
+ end
230
+ @agent.increment("test").should be_nil
231
+ end
175
232
 
176
- it "should report an increment with a set time and count" do
177
- agent.increment('increment_test', 1, 555, 111)
178
- wait
179
- server.commands.last.should == "increment increment_test 1 555 111"
180
- end
233
+ it "should track invalid metrics" do
234
+ @agent.logger.should_receive(:warn).with(/%%/)
235
+ @agent.increment(' %% .!#@$%^&*', 1, 1)
236
+ wait
237
+ @server.commands.join("\n").should include("increment agent.invalid_metric")
238
+ end
181
239
 
182
- it "should discard data that overflows the buffer" do
183
- with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
184
- 5.times do |i|
185
- agent.increment('overflow_test', i + 1, 300)
186
- end
187
- wait
188
- server.commands.should include("increment overflow_test 1 300 1")
189
- server.commands.should include("increment overflow_test 2 300 1")
190
- server.commands.should include("increment overflow_test 3 300 1")
191
- server.commands.should_not include("increment overflow_test 4 300 1")
192
- server.commands.should_not include("increment overflow_test 5 300 1")
193
- end
194
- end
240
+ it "should allow reasonable metric names" do
241
+ @agent.increment('a')
242
+ @agent.increment('a.b')
243
+ @agent.increment('hello.world')
244
+ @agent.increment('ThisIsATest.Of.The.Emergency.Broadcast.System.12345')
245
+ wait
246
+ @server.commands.join("\n").should_not include("increment agent.invalid_metric")
247
+ end
195
248
 
196
- it "should send all data in synchronous mode" do
197
- with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
198
- agent.synchronous = true
199
- 5.times do |i|
200
- agent.increment('overflow_test', i + 1, 300)
201
- end
202
- agent.instance_variable_get(:@queue).size.should == 0
203
- wait # let the server receive the commands
204
- server.commands.should include("increment overflow_test 1 300 1")
205
- server.commands.should include("increment overflow_test 2 300 1")
206
- server.commands.should include("increment overflow_test 3 300 1")
207
- server.commands.should include("increment overflow_test 4 300 1")
208
- server.commands.should include("increment overflow_test 5 300 1")
209
- end
210
- end
249
+ it "should track invalid values" do
250
+ @agent.logger.should_receive(:warn).with(/hello.*testington/)
251
+ @agent.increment('testington', 'hello')
252
+ wait
253
+ @server.commands.join("\n").should include("increment agent.invalid_value")
254
+ end
211
255
 
212
- if FORK_SUPPORTED
213
- it "should automatically reconnect when forked" do
214
- agent.increment('fork_reconnect_test', 1, 2)
215
- fork do
216
- agent.increment('fork_reconnect_test', 1, 3) # triggers reconnect
217
- end
218
- wait(1)
219
- agent.increment('fork_reconnect_test', 1, 4) # triggers reconnect
220
- wait(1)
221
- server.connect_count.should == 2
222
- server.commands.should include("increment fork_reconnect_test 1 2 1")
223
- server.commands.should include("increment fork_reconnect_test 1 3 1")
224
- server.commands.should include("increment fork_reconnect_test 1 4 1")
225
- end
226
- end
256
+ it "should allow reasonable values" do
257
+ @agent.increment('a', -333.333)
258
+ @agent.increment('a', -2.2)
259
+ @agent.increment('a', -1)
260
+ @agent.increment('a', 0)
261
+ @agent.increment('a', 1)
262
+ @agent.increment('a', 2.2)
263
+ @agent.increment('a', 333.333)
264
+ @agent.increment('a', Float::EPSILON)
265
+ wait
266
+ @server.commands.join("\n").should_not include("increment agent.invalid_value")
267
+ end
227
268
 
228
- it "should never let an exception reach the user" do
229
- agent.stub(:send_command).and_raise(Exception.new("Test Exception"))
230
- agent.increment('throws_exception', 2).should be_nil
231
- wait
232
- agent.gauge('throws_exception', 234).should be_nil
233
- wait
234
- end
269
+ it "should send notices to the server" do
270
+ tm = Time.now
271
+ @agent.notice("Test note", tm)
272
+ wait
273
+ @server.commands.join("\n").should include("notice #{tm.to_i} 0 Test note")
274
+ end
235
275
 
236
- it "should let exceptions in time bubble up" do
237
- expect { agent.time('za') { raise "fail" } }.to raise_error
238
- end
276
+ it "should prevent a note w/ newline characters from being sent to the server" do
277
+ @agent.notice("Test note\n").should be_nil
278
+ wait
279
+ @server.commands.join("\n").should_not include("notice Test note")
280
+ end
239
281
 
240
- it "should return nil if the user overflows the MAX_BUFFER" do
241
- 1.upto(Instrumental::Agent::MAX_BUFFER) do
242
- agent.increment("test").should == 1
243
- thread = agent.instance_variable_get(:@thread)
244
- thread.kill
245
- end
246
- agent.increment("test").should be_nil
247
- end
282
+ it "should allow outgoing metrics to be stopped" do
283
+ tm = Time.now
284
+ @agent.increment("foo.bar", 1, tm)
285
+ @agent.stop
286
+ wait
287
+ @agent.increment("foo.baz", 1, tm)
288
+ wait
289
+ @server.commands.join("\n").should include("increment foo.baz 1 #{tm.to_i}")
290
+ @server.commands.join("\n").should_not include("increment foo.bar 1 #{tm.to_i}")
291
+ end
248
292
 
249
- it "should track invalid metrics" do
250
- agent.logger.should_receive(:warn).with(/%%/)
251
- agent.increment(' %% .!#@$%^&*', 1, 1)
252
- wait
253
- server.commands.join("\n").should include("increment agent.invalid_metric")
254
- end
293
+ it "should allow flushing pending values to the server" do
294
+ 1.upto(100) { @agent.gauge('a', rand(50)) }
295
+ @agent.instance_variable_get(:@queue).size.should >= 100
296
+ @agent.flush
297
+ @agent.instance_variable_get(:@queue).size.should == 0
298
+ wait
299
+ @server.commands.grep(/^gauge a /).size.should == 100
300
+ end
255
301
 
256
- it "should allow reasonable metric names" do
257
- agent.increment('a')
258
- agent.increment('a.b')
259
- agent.increment('hello.world')
260
- agent.increment('ThisIsATest.Of.The.Emergency.Broadcast.System.12345')
261
- wait
262
- server.commands.join("\n").should_not include("increment agent.invalid_metric")
263
- end
302
+ it "should no op on an empty flush" do
303
+ @agent.flush(true)
304
+ wait
305
+ @server.commands.should be_empty
306
+ end
307
+ end
264
308
 
265
- it "should track invalid values" do
266
- agent.logger.should_receive(:warn).with(/hello.*testington/)
267
- agent.increment('testington', 'hello')
268
- wait
269
- server.commands.join("\n").should include("increment agent.invalid_value")
270
- end
309
+ describe Instrumental::Agent, "connection problems" do
310
+ after do
311
+ @agent.stop
312
+ @server.stop
313
+ end
271
314
 
272
- it "should allow reasonable values" do
273
- agent.increment('a', -333.333)
274
- agent.increment('a', -2.2)
275
- agent.increment('a', -1)
276
- agent.increment('a', 0)
277
- agent.increment('a', 1)
278
- agent.increment('a', 2.2)
279
- agent.increment('a', 333.333)
280
- agent.increment('a', Float::EPSILON)
281
- wait
282
- server.commands.join("\n").should_not include("increment agent.invalid_value")
283
- end
315
+ it "should automatically reconnect on disconnect" do
316
+ @server = TestServer.new
317
+ @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
318
+ @agent.increment("reconnect_test", 1, 1234)
319
+ wait
320
+ @server.disconnect_all
321
+ wait
322
+ @agent.increment('reconnect_test', 1, 5678) # triggers reconnect
323
+ wait
324
+ @server.connect_count.should == 2
325
+ @server.commands.last.should == "increment reconnect_test 1 5678 1"
326
+ end
284
327
 
285
- it "should send notices to the server" do
286
- tm = Time.now
287
- agent.notice("Test note", tm)
288
- wait
289
- server.commands.join("\n").should include("notice #{tm.to_i} 0 Test note")
290
- end
328
+ it "should buffer commands when server is down" do
329
+ @server = TestServer.new(:listen => false)
330
+ @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
331
+ wait
332
+ @agent.increment('reconnect_test', 1, 1234)
333
+ wait
334
+ @agent.queue.pop(true).should include("increment reconnect_test 1 1234 1\n")
335
+ end
291
336
 
292
- it "should prevent a note w/ newline characters from being sent to the server" do
293
- agent.notice("Test note\n").should be_nil
294
- wait
295
- server.commands.join("\n").should_not include("notice Test note")
296
- end
337
+ it "should buffer commands when server is not responsive" do
338
+ @server = TestServer.new(:response => false)
339
+ @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
340
+ wait
341
+ @agent.increment('reconnect_test', 1, 1234)
342
+ wait
343
+ @agent.queue.pop(true).should include("increment reconnect_test 1 1234 1\n")
344
+ end
297
345
 
298
- it "should allow outgoing metrics to be stopped" do
299
- tm = Time.now
300
- agent.increment("foo.bar", 1, tm)
301
- agent.stop
302
- wait
303
- agent.increment("foo.baz", 1, tm)
304
- wait
305
- server.commands.join("\n").should include("increment foo.baz 1 #{tm.to_i}")
306
- server.commands.join("\n").should_not include("increment foo.bar 1 #{tm.to_i}")
307
- end
346
+ it "should buffer commands when authentication fails" do
347
+ @server = TestServer.new(:authenticate => false)
348
+ @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
349
+ wait
350
+ @agent.increment('reconnect_test', 1, 1234)
351
+ wait
352
+ @agent.queue.pop(true).should include("increment reconnect_test 1 1234 1\n")
353
+ end
308
354
 
309
- it "should allow flushing pending values to the server" do
310
- 1.upto(100) { agent.gauge('a', rand(50)) }
311
- agent.instance_variable_get(:@queue).size.should > 0
312
- agent.flush
313
- agent.instance_variable_get(:@queue).size.should == 0
314
- wait
315
- server.commands.grep(/^gauge a /).size.should == 100
316
- end
355
+ it "should warn once when buffer is full" do
356
+ with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
357
+ @server = TestServer.new(:listen => false)
358
+ @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
359
+ wait
360
+ @agent.logger.should_receive(:warn).with(/Queue full/).once
317
361
 
318
- it "should no op on an empty flush" do
319
- agent.flush(true)
320
- wait
321
- server.commands.should be_empty
322
- end
362
+ @agent.increment('buffer_full_warn_test', 1, 1234)
363
+ @agent.increment('buffer_full_warn_test', 1, 1234)
364
+ @agent.increment('buffer_full_warn_test', 1, 1234)
365
+ @agent.increment('buffer_full_warn_test', 1, 1234)
366
+ @agent.increment('buffer_full_warn_test', 1, 1234)
323
367
  end
368
+ end
324
369
 
325
- describe Instrumental::Agent, "connection problems" do
326
- it "should automatically reconnect on disconnect" do
327
- agent.increment("reconnect_test", 1, 1234)
328
- wait
329
- server.disconnect_all
330
- wait(1)
331
- agent.increment('reconnect_test', 1, 5678) # triggers reconnect
332
- wait(1)
333
- server.connect_count.should == 2
334
- server.commands.last.should == "increment reconnect_test 1 5678 1"
335
- end
336
-
337
- context 'not listening' do
338
- let(:listen) { false }
339
-
340
- it "should buffer commands when server is down" do
341
- agent.increment('reconnect_test', 1, 1234)
342
- wait
343
- agent.queue.pop(true).should include("increment reconnect_test 1 1234 1\n")
344
- end
345
-
346
- it "should warn once when buffer is full" do
347
- with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
348
- wait
349
- agent.logger.should_receive(:warn).with(/Queue full/).once
350
-
351
- agent.increment('buffer_full_warn_test', 1, 1234)
352
- agent.increment('buffer_full_warn_test', 1, 1234)
353
- agent.increment('buffer_full_warn_test', 1, 1234)
354
- agent.increment('buffer_full_warn_test', 1, 1234)
355
- agent.increment('buffer_full_warn_test', 1, 1234)
356
- end
357
- end
358
- end
359
-
360
- context 'not responding' do
361
- let(:response) { false }
362
-
363
- it "should buffer commands when server is not responsive" do
364
- agent.increment('reconnect_test', 1, 1234)
365
- wait
366
- agent.queue.pop(true).should include("increment reconnect_test 1 1234 1\n")
367
- end
368
- end
369
-
370
-
371
- context 'not authenticating' do
372
- let(:authenticate) { false }
373
-
374
- it "should buffer commands when authentication fails" do
375
- agent.increment('reconnect_test', 1, 1234)
376
- wait
377
- agent.queue.pop(true).should include("increment reconnect_test 1 1234 1\n")
378
- end
379
- end
370
+ it "should send commands in a short-lived process" do
371
+ @server = TestServer.new
372
+ @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
373
+ if pid = fork { @agent.increment('foo', 1, 1234) }
374
+ Process.wait(pid)
375
+ @server.commands.last.should == "increment foo 1 1234 1"
376
+ end
377
+ end
380
378
 
381
- if FORK_SUPPORTED
382
- it "should send commands in a short-lived process" do
383
- if pid = fork { agent.increment('foo', 1, 1234) }
384
- Process.wait(pid)
385
- server.commands.last.should == "increment foo 1 1234 1"
386
- end
387
- end
388
-
389
- it "should send commands in a process that bypasses at_exit when using #cleanup" do
390
- if pid = fork { agent.increment('foo', 1, 1234); agent.cleanup; exit! }
391
- Process.wait(pid)
392
- server.commands.last.should == "increment foo 1 1234 1"
393
- end
394
- end
395
-
396
- it "should not wait longer than EXIT_FLUSH_TIMEOUT seconds to exit a process" do
397
- agent.stub(:open_socket) { |*args, &block| sleep(5) && block.call }
398
- with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
399
- if (pid = fork { agent.increment('foo', 1) })
400
- tm = Time.now.to_f
401
- Process.wait(pid)
402
- diff = Time.now.to_f - tm
403
- diff.abs.should >= 3
404
- diff.abs.should < 5
405
- end
406
- end
407
- end
408
-
409
- it "should not wait to exit a process if there are no commands queued" do
410
- agent.stub(:open_socket) { |*args, &block| sleep(5) && block.call }
411
- with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
412
- if (pid = fork { agent.increment('foo', 1); agent.queue.clear })
413
- tm = Time.now.to_f
414
- Process.wait(pid)
415
- diff = Time.now.to_f - tm
416
- diff.should < 1
417
- end
418
- end
419
- end
420
- end
379
+ it "should send commands in a process that bypasses at_exit when using #cleanup" do
380
+ @server = TestServer.new
381
+ @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
382
+ if pid = fork { @agent.increment('foo', 1, 1234); @agent.cleanup; exit! }
383
+ Process.wait(pid)
384
+ @server.commands.last.should == "increment foo 1 1234 1"
385
+ end
386
+ end
421
387
 
422
- it "should not wait longer than EXIT_FLUSH_TIMEOUT to attempt flushing the socket when disconnecting" do
423
- agent.increment('foo', 1)
424
- wait
425
- agent.should_receive(:flush_socket) do
426
- r, w = IO.pipe
427
- Thread.new do
428
- IO.select([r]) # mimic an endless blocking select poll
429
- end.join
430
- end
431
- with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
432
- tm = Time.now.to_f
433
- agent.cleanup
434
- diff = Time.now.to_f - tm
435
- diff.should <= 3
436
- end
388
+ it "should not wait longer than EXIT_FLUSH_TIMEOUT seconds to exit a process" do
389
+ @server = TestServer.new
390
+ @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
391
+ Socket.stub!(:new) { |*args| sleep(5) && StringIO.new }
392
+ with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
393
+ if (pid = fork { @agent.increment('foo', 1) })
394
+ tm = Time.now.to_f
395
+ Process.wait(pid)
396
+ diff = Time.now.to_f - tm
397
+ diff.should >= 3
398
+ diff.should < 5
437
399
  end
438
400
  end
401
+ end
439
402
 
440
- describe Instrumental::Agent, "enabled with sync option" do
441
- let(:synchronous) { true }
442
-
443
- it "should send all data in synchronous mode" do
444
- with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
445
- 5.times do |i|
446
- agent.increment('overflow_test', i + 1, 300)
447
- end
448
- wait # let the server receive the commands
449
- server.commands.should include("increment overflow_test 1 300 1")
450
- server.commands.should include("increment overflow_test 2 300 1")
451
- server.commands.should include("increment overflow_test 3 300 1")
452
- server.commands.should include("increment overflow_test 4 300 1")
453
- server.commands.should include("increment overflow_test 5 300 1")
454
- end
403
+ it "should not wait to exit a process if there are no commands queued" do
404
+ @server = TestServer.new
405
+ @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
406
+ Socket.stub!(:new) { |*args| sleep(5) && StringIO.new }
407
+ with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
408
+ if (pid = fork { @agent.increment('foo', 1); @agent.queue.clear })
409
+ tm = Time.now.to_f
410
+ Process.wait(pid)
411
+ diff = Time.now.to_f - tm
412
+ diff.should < 1
455
413
  end
456
-
457
414
  end
458
415
  end
459
- end
460
416
 
461
- describe "Insecure" do
462
- let(:secure?) { false }
463
- let(:verify_cert?) { false }
464
- it_behaves_like "Instrumental Agent"
417
+ it "should not wait longer than EXIT_FLUSH_TIMEOUT to attempt flushing the socket when disconnecting" do
418
+ @server = TestServer.new
419
+ @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
420
+ @agent.increment('foo', 1)
421
+ wait
422
+ @agent.instance_variable_get(:@socket).should_receive(:flush).and_return {
423
+ r, w = IO.pipe
424
+ IO.select([r]) # mimic an endless blocking select poll
425
+ }
426
+ with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
427
+ tm = Time.now.to_f
428
+ @agent.cleanup
429
+ diff = Time.now.to_f - tm
430
+ diff.should <= 3
431
+ end
432
+ end
465
433
  end
466
434
 
467
- describe "Secure without cert verify" do
468
- let(:secure?) { true }
469
- let(:verify_cert?) { false }
470
- it_behaves_like "Instrumental Agent"
435
+ describe Instrumental::Agent, "enabled with sync option" do
436
+ before do
437
+ @server = TestServer.new
438
+ @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => true)
439
+ end
471
440
 
472
- it "should be disabled if the system does not allow secure connections but the user specifically requested secure" do
473
- Instrumental::Agent.any_instance.stub(:allows_secure?) { false }
474
- agent = Instrumental::Agent.new('test-token', :enabled => true, :secure => true)
475
- agent.secure.should == false
476
- agent.enabled.should == false
441
+ after do
442
+ @agent.stop
443
+ @server.stop
477
444
  end
478
445
 
479
- it "should be fallback to insecure if the system does not allow secure connections but the user did not specifically request secure" do
480
- Instrumental::Agent.any_instance.stub(:allows_secure?) { false }
481
- agent = Instrumental::Agent.new('test-token', :enabled => true)
482
- agent.secure.should == false
483
- agent.enabled.should == true
446
+ it "should send all data in synchronous mode" do
447
+ with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
448
+ 5.times do |i|
449
+ @agent.increment('overflow_test', i + 1, 300)
450
+ end
451
+ wait # let the server receive the commands
452
+ @server.commands.should include("increment overflow_test 1 300 1")
453
+ @server.commands.should include("increment overflow_test 2 300 1")
454
+ @server.commands.should include("increment overflow_test 3 300 1")
455
+ @server.commands.should include("increment overflow_test 4 300 1")
456
+ @server.commands.should include("increment overflow_test 5 300 1")
457
+ end
484
458
  end
459
+
485
460
  end