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 +4 -4
- data/.travis.yml +2 -1
- data/CHANGELOG.md +3 -0
- data/Gemfile +2 -2
- data/Rakefile +0 -1
- data/instrumental_agent.gemspec +6 -0
- data/lib/instrumental/agent.rb +16 -99
- data/lib/instrumental/version.rb +1 -1
- data/spec/agent_spec.rb +392 -417
- data/spec/test_server.rb +14 -50
- metadata +58 -11
- data/certs/equifax.ca.pem +0 -69
- data/certs/geotrust.ca.pem +0 -80
- data/certs/rapidssl.ca.pem +0 -94
- data/spec/test.crt +0 -16
- data/spec/test.csr +0 -12
- data/spec/test.key +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d22dc8e7673c5701cf1cdc654ebd8b953eff28c7
|
4
|
+
data.tar.gz: 53fa9eb53ace63abe3a243b3ce8397bf3d53f01f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc8ec3269967f078690443c315c0dfe1d450603855b2f9cb31a01f3625cb00642cb1e5975cfdc7b3ee25c04c9f4b5249f4c1d5b1aa8fc7e0fed5086fb5f9fc49
|
7
|
+
data.tar.gz: 3c846c8a723c0c5e9b5c1998a78d330caad7a7b67e663ebb8a5abd21e8866348af0538ef14149b7bd897190930703e9e2c990cb5168caa9fa929941354f762ea
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
source "https://rubygems.org"
|
2
2
|
|
3
3
|
gemspec
|
4
|
-
|
5
|
-
if RUBY_VERSION < "1.9"
|
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
data/instrumental_agent.gemspec
CHANGED
@@ -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
|
data/lib/instrumental/agent.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
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:
|
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
|
-
|
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
|
-
|
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
|
-
|
329
|
-
|
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
|
-
|
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
|
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)
|
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
|
data/lib/instrumental/version.rb
CHANGED
data/spec/agent_spec.rb
CHANGED
@@ -1,485 +1,460 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
def wait
|
4
|
-
sleep
|
3
|
+
def wait
|
4
|
+
sleep 0.2 # FIXME: hack
|
5
5
|
end
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
17
|
-
|
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
|
-
|
20
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
46
|
-
|
65
|
+
after do
|
66
|
+
@agent.stop
|
67
|
+
@agent = nil
|
68
|
+
@server.stop
|
69
|
+
end
|
47
70
|
|
48
|
-
|
49
|
-
|
50
|
-
|
71
|
+
it "should not connect to the server" do
|
72
|
+
wait
|
73
|
+
@server.connect_count.should == 0
|
74
|
+
end
|
51
75
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
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
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
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
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
-
|
164
|
-
|
165
|
-
|
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
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
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
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
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
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
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
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
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
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
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
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
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
|
-
|
237
|
-
|
238
|
-
|
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
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
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
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
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
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
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
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
end
|
309
|
+
describe Instrumental::Agent, "connection problems" do
|
310
|
+
after do
|
311
|
+
@agent.stop
|
312
|
+
@server.stop
|
313
|
+
end
|
271
314
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
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
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
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
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
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
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
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
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
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
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
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
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
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
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
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
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
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
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
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
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
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 "
|
468
|
-
|
469
|
-
|
470
|
-
|
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
|
-
|
473
|
-
|
474
|
-
|
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
|
480
|
-
Instrumental::Agent
|
481
|
-
|
482
|
-
|
483
|
-
|
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
|