instrumental_agent 1.0.1 → 3.0.0.beta
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 +5 -5
- data/.ruby-version +1 -0
- data/.travis.yml +4 -6
- data/CHANGELOG.md +21 -0
- data/Gemfile +3 -5
- data/README.md +40 -9
- data/instrumental_agent.gemspec +6 -2
- data/lib/instrumental/agent.rb +262 -155
- data/lib/instrumental/capistrano.rb +4 -46
- data/lib/instrumental/capistrano/capistrano2.rb +47 -0
- data/lib/instrumental/capistrano/capistrano3.rake +56 -0
- data/lib/instrumental/command_structs.rb +32 -0
- data/lib/instrumental/event_aggregator.rb +26 -0
- data/lib/instrumental/version.rb +1 -1
- data/script/setup +22 -4
- data/script/test +34 -2
- data/spec/agent_spec.rb +579 -109
- data/spec/command_struct_specs.rb +20 -0
- data/spec/event_aggregator_spec.rb +53 -0
- data/spec/spec_helper.rb +8 -1
- data/spec/test_server.rb +13 -6
- metadata +47 -17
- data/certs/equifax.ca.pem +0 -69
- data/certs/geotrust.ca.pem +0 -80
- data/certs/rapidssl.ca.pem +0 -94
@@ -1,47 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
if Capistrano::Configuration.instance
|
6
|
-
Capistrano::Configuration.instance.load do
|
7
|
-
namespace :instrumental do
|
8
|
-
namespace :util do
|
9
|
-
desc "marker for beginning of deploy"
|
10
|
-
task :deploy_start do
|
11
|
-
set :instrumental_deploy_start, Time.now
|
12
|
-
end
|
13
|
-
|
14
|
-
desc "marker for end of deploy"
|
15
|
-
task :deploy_end do
|
16
|
-
set :instrumental_deploy_end, Time.now
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
desc "send a notice to instrumental about the deploy"
|
21
|
-
task :record_deploy_notice do
|
22
|
-
start_at = exists?(:instrumental_deploy_start) ? instrumental_deploy_start : Time.now
|
23
|
-
end_at = exists?(:instrumental_deploy_end) ? instrumental_deploy_end : start_at
|
24
|
-
deploy_duration_in_seconds = end_at - start_at
|
25
|
-
deployer = Etc.getlogin.chomp
|
26
|
-
agent_options = { :synchronous => true }
|
27
|
-
agent_options[:collector] = instrumental_host if exists?(:instrumental_host)
|
28
|
-
agent = Instrumental::Agent.new(instrumental_key, agent_options)
|
29
|
-
message = if exists?(:deploy_message)
|
30
|
-
deploy_message
|
31
|
-
else
|
32
|
-
"#{deployer} deployed #{current_revision}"
|
33
|
-
end
|
34
|
-
agent.notice(message,
|
35
|
-
start_at,
|
36
|
-
deploy_duration_in_seconds)
|
37
|
-
logger.info("Notified Instrumental of deployment")
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
before "deploy", "instrumental:util:deploy_start"
|
42
|
-
after "deploy", "instrumental:util:deploy_end"
|
43
|
-
before "deploy:migrations", "instrumental:util:deploy_start"
|
44
|
-
after "deploy:migrations", "instrumental:util:deploy_end"
|
45
|
-
after "instrumental:util:deploy_end", "instrumental:record_deploy_notice"
|
46
|
-
end
|
1
|
+
if Gem::Specification.find_by_name("capistrano").version >= Gem::Version.new("3.0.0")
|
2
|
+
load File.expand_path("../capistrano/capistrano3.rake", __FILE__)
|
3
|
+
else
|
4
|
+
require_relative "capistrano/capistrano2"
|
47
5
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "etc"
|
2
|
+
require "instrumental_agent"
|
3
|
+
|
4
|
+
Capistrano::Configuration.instance.load do
|
5
|
+
_cset(:instrumental_hooks) { true }
|
6
|
+
_cset(:instrumental_key) { nil }
|
7
|
+
_cset(:deployer) { Etc.getlogin.chomp }
|
8
|
+
|
9
|
+
if fetch(:instrumental_hooks)
|
10
|
+
before "deploy", "instrumental:util:deploy_start"
|
11
|
+
after "deploy", "instrumental:util:deploy_end"
|
12
|
+
before "deploy:migrations", "instrumental:util:deploy_start"
|
13
|
+
after "deploy:migrations", "instrumental:util:deploy_end"
|
14
|
+
after "instrumental:util:deploy_end", "instrumental:record_deploy_notice"
|
15
|
+
end
|
16
|
+
|
17
|
+
namespace :instrumental do
|
18
|
+
namespace :util do
|
19
|
+
desc "marker for beginning of deploy"
|
20
|
+
task :deploy_start do
|
21
|
+
set :instrumental_deploy_start, Time.now
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "marker for end of deploy"
|
25
|
+
task :deploy_end do
|
26
|
+
set :instrumental_deploy_end, Time.now
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "send a notice to instrumental about the deploy"
|
31
|
+
task :record_deploy_notice do
|
32
|
+
start_at = fetch(:instrumental_deploy_start, Time.now)
|
33
|
+
end_at = fetch(:instrumental_deploy_end, start_at)
|
34
|
+
deploy_duration_in_seconds = end_at - start_at
|
35
|
+
deployer = fetch(:deployer)
|
36
|
+
agent_options = { :synchronous => true }
|
37
|
+
agent_options[:collector] = instrumental_host if fetch(:instrumental_host, false)
|
38
|
+
agent = Instrumental::Agent.new(fetch(:instrumental_key), agent_options)
|
39
|
+
message = fetch(:deploy_message, "#{deployer} deployed #{current_revision}")
|
40
|
+
|
41
|
+
agent.notice(message,
|
42
|
+
start_at,
|
43
|
+
deploy_duration_in_seconds)
|
44
|
+
logger.info("Notified Instrumental of deployment")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "etc"
|
2
|
+
require "instrumental_agent"
|
3
|
+
|
4
|
+
namespace :load do
|
5
|
+
task :defaults do
|
6
|
+
set :instrumental_hooks, true
|
7
|
+
set :instrumental_key, nil
|
8
|
+
set :deployer, Etc.getlogin.chomp
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
namespace :deploy do
|
13
|
+
before :starting, :check_instrumental_hooks do
|
14
|
+
invoke "instrumental:util:add_hooks" if fetch(:instrumental_hooks)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
namespace :instrumental do
|
19
|
+
namespace :util do
|
20
|
+
desc "add instrumental hooks to deploy"
|
21
|
+
task :add_hooks do
|
22
|
+
before "deploy", "instrumental:util:deploy_start"
|
23
|
+
after "deploy", "instrumental:util:deploy_end"
|
24
|
+
after "instrumental:util:deploy_end", "instrumental:record_deploy_notice"
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "marker for beginning of deploy"
|
28
|
+
task :deploy_start do
|
29
|
+
set :instrumental_deploy_start, Time.now
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "marker for end of deploy"
|
33
|
+
task :deploy_end do
|
34
|
+
set :instrumental_deploy_end, Time.now
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "send a notice to instrumental about the deploy"
|
39
|
+
task :record_deploy_notice do
|
40
|
+
start_at = fetch(:instrumental_deploy_start, Time.now)
|
41
|
+
end_at = fetch(:instrumental_deploy_end, start_at)
|
42
|
+
deploy_duration_in_seconds = end_at - start_at
|
43
|
+
deployer = fetch(:deployer)
|
44
|
+
agent_options = { :synchronous => true }
|
45
|
+
agent_options[:collector] = instrumental_host if fetch(:instrumental_host, false)
|
46
|
+
message = fetch(:deploy_message, "#{deployer} deployed #{fetch(:current_revision)}".strip)
|
47
|
+
|
48
|
+
if fetch(:instrumental_key)
|
49
|
+
agent = Instrumental::Agent.new(fetch(:instrumental_key), agent_options)
|
50
|
+
agent.notice(message,
|
51
|
+
start_at,
|
52
|
+
deploy_duration_in_seconds)
|
53
|
+
puts "Notified Instrumental of deployment"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Instrumental
|
2
|
+
METRIC_TYPES = ["increment".freeze, "gauge".freeze].freeze
|
3
|
+
|
4
|
+
Command = Struct.new(:command, :metric, :value, :time, :count) do
|
5
|
+
def initialize(command, metric, value, time, count)
|
6
|
+
super(command, metric, value, time.to_i, count.to_i)
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
[command, metric, value, time, count].map(&:to_s).join(" ")
|
11
|
+
end
|
12
|
+
|
13
|
+
def metadata
|
14
|
+
"#{metric}:#{time}".freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
def +(other_command)
|
18
|
+
return self if other_command.nil?
|
19
|
+
Command.new(command, metric, value + other_command.value, time, count + other_command.count)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Notice = Struct.new(:note, :time, :duration) do
|
24
|
+
def initialize(note, time, duration)
|
25
|
+
super(note, time.to_i, duration.to_i)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
["notice".freeze, time, duration, note].map(&:to_s).join(" ")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class EventAggregator
|
2
|
+
attr_accessor :counts, :values, :received_at, :frequency
|
3
|
+
|
4
|
+
def initialize(frequency:)
|
5
|
+
@values = Hash.new
|
6
|
+
@frequency = frequency
|
7
|
+
end
|
8
|
+
|
9
|
+
def put(command)
|
10
|
+
command_at = command.time
|
11
|
+
unless(command_at % frequency == 0)
|
12
|
+
command.time = (command_at - (command_at % frequency))
|
13
|
+
end
|
14
|
+
metadata = command.metadata
|
15
|
+
@values[metadata] = (command + @values[metadata])
|
16
|
+
end
|
17
|
+
|
18
|
+
def size
|
19
|
+
@values.size
|
20
|
+
end
|
21
|
+
|
22
|
+
def coerce_time(time)
|
23
|
+
itime = time.to_i
|
24
|
+
(itime - (itime % frequency)).to_i
|
25
|
+
end
|
26
|
+
end
|
data/lib/instrumental/version.rb
CHANGED
data/script/setup
CHANGED
@@ -2,7 +2,25 @@
|
|
2
2
|
set -e
|
3
3
|
cd "$(dirname "$0")/.."
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
rbenv which ruby >/dev/null 2>&1 || (brew upgrade ruby-build || true; rbenv install)
|
6
|
+
|
7
|
+
# Setup rbenv so we can switch rubies below
|
8
|
+
eval "$(rbenv init - --no-rehash)"
|
9
|
+
|
10
|
+
for ruby_version in `ruby -ryaml -e 'puts YAML.load(File.read(".travis.yml"))["rvm"].join(" ")'`; do
|
11
|
+
rbenv versions --bare | grep "^${ruby_version}$" || rbenv install $ruby_version
|
12
|
+
rbenv shell $ruby_version
|
13
|
+
|
14
|
+
gem list -i bundler >/dev/null || gem install bundler
|
15
|
+
|
16
|
+
bundle install
|
17
|
+
done
|
18
|
+
|
19
|
+
tput bold # bold text
|
20
|
+
tput setaf 2 # green text
|
21
|
+
echo "****************************************************************"
|
22
|
+
echo "* *"
|
23
|
+
echo "* Good to go! *"
|
24
|
+
echo "* *"
|
25
|
+
echo "****************************************************************"
|
26
|
+
tput sgr0 # reset to default text
|
data/script/test
CHANGED
@@ -2,5 +2,37 @@
|
|
2
2
|
set -e
|
3
3
|
cd "$(dirname "$0")/.."
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
eval "$(rbenv init - --no-rehash)"
|
6
|
+
|
7
|
+
rspec_file_line="$1"
|
8
|
+
if [[ "$rspec_file_line" != "" ]]; then
|
9
|
+
rspec_file_line="[${rspec_file_line}]"
|
10
|
+
fi
|
11
|
+
|
12
|
+
success="true"
|
13
|
+
|
14
|
+
for ruby_version in `ruby -ryaml -e 'puts YAML.load(File.read(".travis.yml"))["rvm"].join(" ")'`; do
|
15
|
+
{
|
16
|
+
echo "testing ruby version $ruby_version" &&
|
17
|
+
rbenv shell $ruby_version &&
|
18
|
+
bundle exec rspec
|
19
|
+
} || success="false"
|
20
|
+
done
|
21
|
+
|
22
|
+
if [ $success == "true" ]; then
|
23
|
+
tput bold # bold text
|
24
|
+
tput setaf 2 # green text
|
25
|
+
echo "======================================"
|
26
|
+
echo "= Passed ="
|
27
|
+
echo "======================================"
|
28
|
+
tput sgr0 # reset to default text
|
29
|
+
exit 0
|
30
|
+
else
|
31
|
+
tput bold # bold text
|
32
|
+
tput setaf 1 # red text
|
33
|
+
echo "======================================"
|
34
|
+
echo "= FAILED ="
|
35
|
+
echo "======================================"
|
36
|
+
tput sgr0 # reset to default text
|
37
|
+
exit 1
|
38
|
+
fi
|
data/spec/agent_spec.rb
CHANGED
@@ -1,7 +1,21 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
def wait(n=0.2)
|
4
|
-
|
3
|
+
def wait(n=0.2, &block)
|
4
|
+
start = Time.now
|
5
|
+
if block_given?
|
6
|
+
begin
|
7
|
+
yield
|
8
|
+
rescue Exception => ex
|
9
|
+
if (Time.now - start) < 5
|
10
|
+
sleep n
|
11
|
+
retry
|
12
|
+
else
|
13
|
+
raise ex
|
14
|
+
end
|
15
|
+
end
|
16
|
+
else
|
17
|
+
sleep n
|
18
|
+
end
|
5
19
|
end
|
6
20
|
|
7
21
|
FORK_SUPPORTED = begin
|
@@ -24,7 +38,9 @@ shared_examples "Instrumental Agent" do
|
|
24
38
|
let(:synchronous) { false }
|
25
39
|
let(:token) { 'test_token' }
|
26
40
|
let(:address) { server.host_and_port }
|
27
|
-
let(:
|
41
|
+
let(:metrician) { false }
|
42
|
+
let(:frequency) { 0 }
|
43
|
+
let(:agent) { Instrumental::Agent.new(token, :collector => address, :synchronous => synchronous, :enabled => enabled, :secure => secure?, :verify_cert => verify_cert?, :metrician => metrician, :frequency => frequency) }
|
28
44
|
|
29
45
|
# Server options
|
30
46
|
let(:listen) { true }
|
@@ -32,6 +48,12 @@ shared_examples "Instrumental Agent" do
|
|
32
48
|
let(:authenticate) { true }
|
33
49
|
let(:server) { TestServer.new(:listen => listen, :authenticate => authenticate, :response => response, :secure => secure?) }
|
34
50
|
|
51
|
+
# Time Travel Options
|
52
|
+
let(:start_of_minute) do
|
53
|
+
now = Time.now.to_i
|
54
|
+
Time.at(now - (now % 60))
|
55
|
+
end
|
56
|
+
|
35
57
|
before do
|
36
58
|
Instrumental::Agent.logger.level = Logger::UNKNOWN
|
37
59
|
@server = server
|
@@ -52,31 +74,36 @@ shared_examples "Instrumental Agent" do
|
|
52
74
|
|
53
75
|
it "should not connect to the server after receiving a metric" do
|
54
76
|
agent.gauge('disabled_test', 1)
|
55
|
-
wait
|
56
|
-
|
77
|
+
wait do
|
78
|
+
expect(server.connect_count).to eq(0)
|
79
|
+
end
|
57
80
|
end
|
58
81
|
|
59
82
|
it "should no op on flush without reconnect" do
|
60
83
|
1.upto(100) { agent.gauge('disabled_test', 1) }
|
61
84
|
agent.flush(false)
|
62
|
-
wait
|
63
|
-
|
85
|
+
wait do
|
86
|
+
expect(server.commands).to be_empty
|
87
|
+
end
|
64
88
|
end
|
65
89
|
|
66
90
|
it "should no op on flush with reconnect" do
|
67
91
|
1.upto(100) { agent.gauge('disabled_test', 1) }
|
68
92
|
agent.flush(true)
|
69
|
-
wait
|
70
|
-
|
93
|
+
wait do
|
94
|
+
expect(server.commands).to be_empty
|
95
|
+
end
|
71
96
|
end
|
72
97
|
|
73
98
|
it "should no op on an empty flush" do
|
74
99
|
agent.flush(true)
|
75
|
-
wait
|
76
|
-
|
100
|
+
wait do
|
101
|
+
expect(server.commands).to be_empty
|
102
|
+
end
|
77
103
|
end
|
78
104
|
|
79
105
|
it "should send metrics to logger" do
|
106
|
+
Timecop.freeze
|
80
107
|
now = Time.now
|
81
108
|
expect(agent.logger).to receive(:debug).with("gauge metric 1 #{now.to_i} 1")
|
82
109
|
agent.gauge("metric", 1)
|
@@ -91,14 +118,16 @@ shared_examples "Instrumental Agent" do
|
|
91
118
|
|
92
119
|
it "should connect to the server after sending a metric" do
|
93
120
|
agent.increment("test.foo")
|
94
|
-
wait
|
95
|
-
|
121
|
+
wait do
|
122
|
+
expect(server.connect_count).to eq(1)
|
123
|
+
end
|
96
124
|
end
|
97
125
|
|
98
126
|
it "should announce itself, and include version" do
|
99
127
|
agent.increment("test.foo")
|
100
|
-
wait
|
101
|
-
|
128
|
+
wait do
|
129
|
+
expect(server.commands[0]).to match(/hello .*/)
|
130
|
+
end
|
102
131
|
expect(server.commands[0]).to match(/ version /)
|
103
132
|
expect(server.commands[0]).to match(/ hostname /)
|
104
133
|
expect(server.commands[0]).to match(/ pid /)
|
@@ -108,15 +137,18 @@ shared_examples "Instrumental Agent" do
|
|
108
137
|
|
109
138
|
it "should authenticate using the token" do
|
110
139
|
agent.increment("test.foo")
|
111
|
-
wait
|
112
|
-
|
140
|
+
wait do
|
141
|
+
expect(server.commands[1]).to eq("authenticate test_token")
|
142
|
+
end
|
113
143
|
end
|
114
144
|
|
115
145
|
it "should report a gauge" do
|
146
|
+
Timecop.freeze
|
116
147
|
now = Time.now
|
117
148
|
agent.gauge('gauge_test', 123)
|
118
|
-
wait
|
119
|
-
|
149
|
+
wait do
|
150
|
+
expect(server.commands.last).to eq("gauge gauge_test 123 #{now.to_i} 1")
|
151
|
+
end
|
120
152
|
end
|
121
153
|
|
122
154
|
it "should report a time as gauge and return the block result" do
|
@@ -125,8 +157,9 @@ shared_examples "Instrumental Agent" do
|
|
125
157
|
1 + 1
|
126
158
|
end
|
127
159
|
expect(return_value).to eq(2)
|
128
|
-
wait
|
129
|
-
|
160
|
+
wait do
|
161
|
+
expect(server.commands.last).to match(/gauge time_value_test .* #{now.to_i}/)
|
162
|
+
end
|
130
163
|
end
|
131
164
|
|
132
165
|
it "should report a time_ms as gauge and return the block result" do
|
@@ -136,8 +169,9 @@ shared_examples "Instrumental Agent" do
|
|
136
169
|
1 + 1
|
137
170
|
end
|
138
171
|
expect(return_value).to eq(2)
|
139
|
-
wait
|
140
|
-
|
172
|
+
wait do
|
173
|
+
expect(server.commands.last).to match(/gauge time_value_test 1000/)
|
174
|
+
end
|
141
175
|
end
|
142
176
|
|
143
177
|
it "should return the value gauged" do
|
@@ -147,21 +181,24 @@ shared_examples "Instrumental Agent" do
|
|
147
181
|
|
148
182
|
it "should report a gauge with a set time" do
|
149
183
|
agent.gauge('gauge_test', 123, 555)
|
150
|
-
wait
|
151
|
-
|
184
|
+
wait do
|
185
|
+
expect(server.commands.last).to eq("gauge gauge_test 123 555 1")
|
186
|
+
end
|
152
187
|
end
|
153
188
|
|
154
189
|
it "should report a gauge with a set time and count" do
|
155
190
|
agent.gauge('gauge_test', 123, 555, 111)
|
156
|
-
wait
|
157
|
-
|
191
|
+
wait do
|
192
|
+
expect(server.commands.last).to eq("gauge gauge_test 123 555 111")
|
193
|
+
end
|
158
194
|
end
|
159
195
|
|
160
196
|
it "should report an increment" do
|
161
197
|
now = Time.now
|
162
198
|
agent.increment("increment_test")
|
163
|
-
wait
|
164
|
-
|
199
|
+
wait do
|
200
|
+
expect(server.commands.last).to eq("increment increment_test 1 #{now.to_i} 1")
|
201
|
+
end
|
165
202
|
end
|
166
203
|
|
167
204
|
it "should return the value incremented by" do
|
@@ -172,33 +209,43 @@ shared_examples "Instrumental Agent" do
|
|
172
209
|
it "should report an increment a value" do
|
173
210
|
now = Time.now
|
174
211
|
agent.increment("increment_test", 2)
|
175
|
-
wait
|
176
|
-
|
212
|
+
wait do
|
213
|
+
expect(server.commands.last).to eq("increment increment_test 2 #{now.to_i} 1")
|
214
|
+
end
|
177
215
|
end
|
178
216
|
|
179
217
|
it "should report an increment with a set time" do
|
180
218
|
agent.increment('increment_test', 1, 555)
|
181
|
-
wait
|
182
|
-
|
219
|
+
wait do
|
220
|
+
expect(server.commands.last).to eq("increment increment_test 1 555 1")
|
221
|
+
end
|
183
222
|
end
|
184
223
|
|
185
224
|
it "should report an increment with a set time and count" do
|
186
225
|
agent.increment('increment_test', 1, 555, 111)
|
187
|
-
wait
|
188
|
-
|
226
|
+
wait do
|
227
|
+
expect(server.commands.last).to eq("increment increment_test 1 555 111")
|
228
|
+
end
|
189
229
|
end
|
190
230
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
231
|
+
context do
|
232
|
+
let(:listen) { false }
|
233
|
+
it "should discard data that overflows the buffer" do
|
234
|
+
with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
|
235
|
+
allow(agent.logger).to receive(:debug)
|
236
|
+
expect(agent.logger).to receive(:debug).with("Dropping command, queue full(3): increment overflow_test 4 300 1")
|
237
|
+
expect(agent.logger).to receive(:debug).with("Dropping command, queue full(3): increment overflow_test 5 300 1")
|
238
|
+
1.upto(5) do |i|
|
239
|
+
agent.increment('overflow_test', i, 300)
|
240
|
+
end
|
241
|
+
|
242
|
+
wait
|
243
|
+
expect(agent.sender_queue.size).to eq(3)
|
244
|
+
expect(agent.sender_queue.pop.first.to_s).to start_with("increment overflow_test 1 300 1")
|
245
|
+
expect(agent.sender_queue.pop.first.to_s).to start_with("increment overflow_test 2 300 1")
|
246
|
+
expect(agent.sender_queue.pop.first.to_s).to start_with("increment overflow_test 3 300 1")
|
247
|
+
expect(agent.sender_queue.size).to eq(0)
|
195
248
|
end
|
196
|
-
wait
|
197
|
-
expect(server.commands).to include("increment overflow_test 1 300 1")
|
198
|
-
expect(server.commands).to include("increment overflow_test 2 300 1")
|
199
|
-
expect(server.commands).to include("increment overflow_test 3 300 1")
|
200
|
-
expect(server.commands).to_not include("increment overflow_test 4 300 1")
|
201
|
-
expect(server.commands).to_not include("increment overflow_test 5 300 1")
|
202
249
|
end
|
203
250
|
end
|
204
251
|
|
@@ -208,7 +255,7 @@ shared_examples "Instrumental Agent" do
|
|
208
255
|
5.times do |i|
|
209
256
|
agent.increment('overflow_test', i + 1, 300)
|
210
257
|
end
|
211
|
-
expect(agent.instance_variable_get(:@
|
258
|
+
expect(agent.instance_variable_get(:@sender_queue).size).to eq(0)
|
212
259
|
wait # let the server receive the commands
|
213
260
|
expect(server.commands).to include("increment overflow_test 1 300 1")
|
214
261
|
expect(server.commands).to include("increment overflow_test 2 300 1")
|
@@ -224,8 +271,10 @@ shared_examples "Instrumental Agent" do
|
|
224
271
|
fork do
|
225
272
|
agent.increment('fork_reconnect_test', 1, 3) # triggers reconnect
|
226
273
|
end
|
274
|
+
|
227
275
|
wait(1)
|
228
276
|
agent.increment('fork_reconnect_test', 1, 4) # triggers reconnect
|
277
|
+
|
229
278
|
wait(1)
|
230
279
|
expect(server.connect_count).to eq(2)
|
231
280
|
|
@@ -241,24 +290,25 @@ shared_examples "Instrumental Agent" do
|
|
241
290
|
sleep 1
|
242
291
|
}
|
243
292
|
|
244
|
-
|
245
|
-
allow(agent).to receive(:
|
246
|
-
|
293
|
+
run_sender_loop_calls = 0
|
294
|
+
allow(agent).to receive(:run_sender_loop) {
|
295
|
+
run_sender_loop_calls += 1
|
247
296
|
sleep 3 # keep the worker thread alive
|
248
297
|
}
|
249
298
|
|
250
299
|
t = Thread.new { agent.increment("race") }
|
251
300
|
agent.increment("race")
|
252
301
|
wait(2)
|
253
|
-
expect(
|
254
|
-
expect(agent.
|
302
|
+
expect(run_sender_loop_calls).to eq(1)
|
303
|
+
expect(agent.sender_queue.size).to eq(2)
|
255
304
|
end
|
256
305
|
|
257
306
|
it "should never let an exception reach the user" do
|
258
307
|
expect(agent).to receive(:send_command).twice { raise(Exception.new("Test Exception")) }
|
259
308
|
expect(agent.increment('throws_exception', 2)).to eq(nil)
|
260
|
-
wait
|
261
|
-
|
309
|
+
wait do
|
310
|
+
expect(agent.increment('throws_exception', 234)).to eq(nil)
|
311
|
+
end
|
262
312
|
end
|
263
313
|
|
264
314
|
it "should let exceptions in time bubble up" do
|
@@ -273,27 +323,22 @@ shared_examples "Instrumental Agent" do
|
|
273
323
|
expect(agent.increment("test")).to eq(nil)
|
274
324
|
end
|
275
325
|
|
276
|
-
it "should track invalid metrics" do
|
277
|
-
expect(agent.logger).to receive(:warn).with(/%%/)
|
278
|
-
agent.increment(' %% .!#@$%^&*', 1, 1)
|
279
|
-
wait
|
280
|
-
expect(server.commands.join("\n")).to include("increment agent.invalid_metric")
|
281
|
-
end
|
282
|
-
|
283
326
|
it "should allow reasonable metric names" do
|
284
327
|
agent.increment('a')
|
285
328
|
agent.increment('a.b')
|
286
329
|
agent.increment('hello.world')
|
287
330
|
agent.increment('ThisIsATest.Of.The.Emergency.Broadcast.System.12345')
|
288
|
-
wait
|
289
|
-
|
331
|
+
wait do
|
332
|
+
expect(server.commands.join("\n")).to_not include("increment agent.invalid_metric")
|
333
|
+
end
|
290
334
|
end
|
291
335
|
|
292
336
|
it "should track invalid values" do
|
293
337
|
expect(agent.logger).to receive(:warn).with(/hello.*testington/)
|
294
338
|
agent.increment('testington', 'hello')
|
295
|
-
wait
|
296
|
-
|
339
|
+
wait do
|
340
|
+
expect(server.commands.join("\n")).to include("increment agent.invalid_value")
|
341
|
+
end
|
297
342
|
end
|
298
343
|
|
299
344
|
it "should allow reasonable values" do
|
@@ -305,61 +350,86 @@ shared_examples "Instrumental Agent" do
|
|
305
350
|
agent.increment('a', 2.2)
|
306
351
|
agent.increment('a', 333.333)
|
307
352
|
agent.increment('a', Float::EPSILON)
|
308
|
-
wait
|
309
|
-
|
353
|
+
wait do
|
354
|
+
expect(server.commands.join("\n")).to_not include("increment agent.invalid_value")
|
355
|
+
end
|
310
356
|
end
|
311
357
|
|
312
358
|
it "should send notices to the server" do
|
359
|
+
Timecop.freeze
|
313
360
|
tm = Time.now
|
314
361
|
agent.notice("Test note", tm)
|
315
|
-
wait
|
316
|
-
|
362
|
+
wait do
|
363
|
+
expect(server.commands.join("\n")).to include("notice #{tm.to_i} 0 Test note")
|
364
|
+
end
|
317
365
|
end
|
318
366
|
|
319
367
|
it "should prevent a note w/ newline characters from being sent to the server" do
|
320
|
-
expect(agent.notice("
|
321
|
-
|
322
|
-
|
368
|
+
expect(agent.notice("Test_bad_note\n")).to eq(nil)
|
369
|
+
|
370
|
+
# Send a note that make it through so we're sure the bad note would have
|
371
|
+
# arrived if it was going to.
|
372
|
+
Timecop.freeze
|
373
|
+
tm = Time.now
|
374
|
+
agent.notice("Test_good_note", tm)
|
375
|
+
wait do
|
376
|
+
expect(server.commands.join("\n")).to include("Test_good_note")
|
377
|
+
end
|
378
|
+
|
379
|
+
expect(server.commands.join("\n")).to_not include("Test_bad_note")
|
323
380
|
end
|
324
381
|
|
325
382
|
it "should allow outgoing metrics to be stopped" do
|
326
383
|
tm = Time.now
|
327
384
|
agent.increment("foo.bar", 1, tm)
|
328
385
|
agent.stop
|
386
|
+
|
387
|
+
# In Java the test server hangs sometimes when the agent disconnects so
|
388
|
+
# this cleans up the server.
|
389
|
+
server.stop
|
329
390
|
wait
|
330
|
-
|
391
|
+
server.listen
|
392
|
+
|
331
393
|
wait
|
332
|
-
|
394
|
+
agent.increment("foo.baz", 1, tm)
|
395
|
+
wait do
|
396
|
+
expect(server.commands.join("\n")).to include("increment foo.baz 1 #{tm.to_i}")
|
397
|
+
end
|
333
398
|
expect(server.commands.join("\n")).to_not include("increment foo.bar 1 #{tm.to_i}")
|
334
399
|
end
|
335
400
|
|
336
401
|
it "should allow flushing pending values to the server" do
|
337
402
|
1.upto(100) { agent.gauge('a', rand(50)) }
|
338
|
-
expect(agent.instance_variable_get(:@
|
403
|
+
expect(agent.instance_variable_get(:@sender_queue).size).to be > 0
|
339
404
|
agent.flush
|
340
|
-
expect(agent.instance_variable_get(:@
|
341
|
-
wait
|
342
|
-
|
405
|
+
expect(agent.instance_variable_get(:@sender_queue).size).to eq(0)
|
406
|
+
wait do
|
407
|
+
expect(server.commands.grep(/^gauge a /).size).to eq(100)
|
408
|
+
end
|
343
409
|
end
|
344
410
|
|
345
411
|
it "should no op on an empty flush" do
|
346
412
|
agent.flush(true)
|
347
|
-
wait
|
348
|
-
|
413
|
+
wait do
|
414
|
+
expect(server.commands).to be_empty
|
415
|
+
end
|
349
416
|
end
|
350
417
|
end
|
351
418
|
|
352
419
|
describe Instrumental::Agent, "connection problems" do
|
353
420
|
it "should automatically reconnect on disconnect" do
|
354
|
-
agent.increment("
|
355
|
-
wait
|
421
|
+
agent.increment("reconnect_test1", 1, 1234)
|
422
|
+
wait do
|
423
|
+
expect(server.commands.grep(/reconnect_test1/).size).to eq(1)
|
424
|
+
end
|
356
425
|
server.disconnect_all
|
357
426
|
wait(1)
|
358
427
|
agent.increment('reconnect_test', 1, 5678) # triggers reconnect
|
359
|
-
wait
|
360
|
-
|
361
|
-
|
362
|
-
|
428
|
+
wait do
|
429
|
+
expect(server.connect_count).to eq(2)
|
430
|
+
# Ensure the last command sent has been received after the reconnect attempt
|
431
|
+
expect(server.commands.last).to eq("increment reconnect_test 1 5678 1")
|
432
|
+
end
|
363
433
|
end
|
364
434
|
|
365
435
|
context 'not listening' do
|
@@ -370,7 +440,7 @@ shared_examples "Instrumental Agent" do
|
|
370
440
|
agent.increment('reconnect_test', 1, 1234)
|
371
441
|
wait
|
372
442
|
# The agent should not have sent the metric yet, the server is not responding
|
373
|
-
expect(agent.
|
443
|
+
expect(agent.sender_queue.pop(true).first.to_s).to eq("increment reconnect_test 1 1234 1")
|
374
444
|
end
|
375
445
|
|
376
446
|
it "should warn once when buffer is full" do
|
@@ -405,55 +475,124 @@ shared_examples "Instrumental Agent" do
|
|
405
475
|
agent.increment('reconnect_test', 1, 1234)
|
406
476
|
wait
|
407
477
|
# Since server hasn't responded to hello or authenticate, worker thread will not send data
|
408
|
-
expect(agent.
|
478
|
+
expect(agent.sender_queue.pop(true).first.to_s).to eq("increment reconnect_test 1 1234 1")
|
409
479
|
end
|
410
480
|
end
|
411
481
|
|
412
482
|
context 'server hangup' do
|
413
483
|
it "should cancel the worker thread when the host has hung up" do
|
414
484
|
# Start the background agent thread and let it send one metric successfully
|
415
|
-
agent.gauge('
|
416
|
-
wait
|
485
|
+
agent.gauge('connection_failure1', 1, 1234)
|
486
|
+
wait do
|
487
|
+
expect(server.commands.grep(/connection_failure/).size).to eq(1)
|
488
|
+
end
|
417
489
|
# Stop the server
|
418
490
|
server.stop
|
419
491
|
wait
|
420
492
|
# Send one metric to the stopped server
|
421
|
-
agent.gauge('
|
422
|
-
wait
|
493
|
+
agent.gauge('connection_failure2', 1, 1234)
|
423
494
|
# The agent thread should have stopped running since the network write would
|
424
495
|
# have failed. The queue will still contain the metric that has yet to be sent
|
425
|
-
|
426
|
-
|
496
|
+
wait do
|
497
|
+
expect(agent.send(:running?)).to eq(false)
|
498
|
+
end
|
499
|
+
expect(agent.sender_queue.size).to eq(1)
|
427
500
|
end
|
428
501
|
|
429
502
|
it "should restart the worker thread after hanging it up during an unreachable host event" do
|
430
503
|
# Start the background agent thread and let it send one metric successfully
|
431
504
|
agent.gauge('connection_failure', 1, 1234)
|
432
|
-
wait
|
505
|
+
wait do
|
506
|
+
expect(server.commands.grep(/connection_failure/).size).to eq(1)
|
507
|
+
end
|
433
508
|
# Stop the server
|
434
509
|
server.stop
|
435
510
|
wait
|
436
511
|
# Send one metric to the stopped server
|
437
512
|
agent.gauge('connection_failure', 1, 1234)
|
438
|
-
wait
|
439
513
|
# The agent thread should have stopped running since the network write would
|
440
514
|
# have failed. The queue will still contain the metric that has yet to be sent
|
441
|
-
|
442
|
-
|
443
|
-
|
515
|
+
wait do
|
516
|
+
expect(agent.send(:running?)).to eq(false)
|
517
|
+
end
|
518
|
+
expect(agent.sender_queue.size).to eq(1)
|
444
519
|
# Start the server back up again
|
445
520
|
server.listen
|
446
|
-
wait
|
447
521
|
# Sending another metric should kickstart the background worker thread
|
448
522
|
agent.gauge('connection_failure', 1, 1234)
|
449
|
-
wait
|
450
523
|
# The agent should now be running the background thread, and the queue should be empty
|
451
|
-
|
452
|
-
|
524
|
+
wait do
|
525
|
+
expect(agent.send(:running?)).to eq(true)
|
526
|
+
expect(agent.sender_queue.size).to eq(0)
|
527
|
+
end
|
453
528
|
end
|
454
529
|
|
455
|
-
|
530
|
+
it "should restart the worker thread after hanging it up during a bad ssl handshake event" do
|
531
|
+
# Start the background agent thread and let it send one metric successfully
|
532
|
+
agent.gauge('connection_failure', 1, 1234)
|
533
|
+
wait do
|
534
|
+
expect(server.commands.grep(/connection_failure/).size).to eq(1)
|
535
|
+
end
|
536
|
+
# Make the agent return the relevant exception on the next connection test
|
537
|
+
test_connection_fail = true
|
538
|
+
tc = agent.method(:test_connection)
|
539
|
+
allow(agent).to receive(:test_connection) do |*args, &block|
|
540
|
+
test_connection_fail ? raise(OpenSSL::SSL::SSLError.new) : tc.call(*args)
|
541
|
+
end
|
456
542
|
|
543
|
+
# Send one metric to the agent
|
544
|
+
agent.gauge('connection_failure', 1, 1234)
|
545
|
+
# The agent thread should have stopped running since the network write would
|
546
|
+
# have failed.
|
547
|
+
wait do
|
548
|
+
expect(agent.send(:running?)).to eq(false)
|
549
|
+
end
|
550
|
+
# The command is not in the queue
|
551
|
+
expect(agent.sender_queue.size).to eq(0)
|
552
|
+
# allow the agent to behave normally
|
553
|
+
test_connection_fail = false
|
554
|
+
# Sending another metric should kickstart the background worker thread
|
555
|
+
agent.gauge('connection_failure', 1, 1234)
|
556
|
+
# The agent should now be running the background thread, and the queue should be empty
|
557
|
+
wait do
|
558
|
+
expect(agent.send(:running?)).to eq(true)
|
559
|
+
expect(agent.sender_queue.size).to eq(0)
|
560
|
+
expect(server.commands.grep(/connection_failure/).size).to eq(2)
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
it "should accurately count failures so that backoff can work as intended" do
|
565
|
+
# Start the background agent thread and let it send one metric successfully
|
566
|
+
agent.gauge('connection_failure', 1, 1234)
|
567
|
+
wait do
|
568
|
+
expect(server.commands.grep(/connection_failure/).size).to eq(1)
|
569
|
+
end
|
570
|
+
|
571
|
+
# configure test_connection to fail in a way that won't kill the inner loop
|
572
|
+
test_connection_fail = true
|
573
|
+
tc = agent.method(:test_connection)
|
574
|
+
allow(agent).to receive(:test_connection) do |*args, &block|
|
575
|
+
test_connection_fail ? raise("test_connection_fail") : tc.call(*args)
|
576
|
+
end
|
577
|
+
|
578
|
+
# send some metrics
|
579
|
+
agent.gauge('connection_failure_1', 1, 1234)
|
580
|
+
agent.gauge('connection_failure_2', 1, 1234)
|
581
|
+
agent.gauge('connection_failure_3', 1, 1234)
|
582
|
+
wait do
|
583
|
+
expect(agent.instance_variable_get(:@failures)).to be > 0
|
584
|
+
expect(agent.sender_queue.size).to be > 0
|
585
|
+
end
|
586
|
+
|
587
|
+
# let the loop proceed
|
588
|
+
test_connection_fail = false
|
589
|
+
|
590
|
+
wait do
|
591
|
+
expect(agent.send(:running?)).to eq(true)
|
592
|
+
expect(agent.sender_queue.size).to eq(0)
|
593
|
+
end
|
594
|
+
end
|
595
|
+
end
|
457
596
|
|
458
597
|
context 'not authenticating' do
|
459
598
|
# Server will fail all authentication attempts
|
@@ -463,7 +602,7 @@ shared_examples "Instrumental Agent" do
|
|
463
602
|
agent.increment('reconnect_test', 1, 1234)
|
464
603
|
wait
|
465
604
|
# Metrics should not have been sent since all authentication failed
|
466
|
-
expect(agent.
|
605
|
+
expect(agent.sender_queue.pop(true).first.to_s).to eq("increment reconnect_test 1 1234 1")
|
467
606
|
end
|
468
607
|
end
|
469
608
|
|
@@ -500,7 +639,7 @@ shared_examples "Instrumental Agent" do
|
|
500
639
|
it "should not wait to exit a process if there are no commands queued" do
|
501
640
|
allow(agent).to receive(:open_socket) { |*args, &block| sleep(5) && block.call }
|
502
641
|
with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
|
503
|
-
if (pid = fork { agent.increment('foo', 1); agent.
|
642
|
+
if (pid = fork { agent.increment('foo', 1); agent.sender_queue.clear })
|
504
643
|
tm = Time.now.to_f
|
505
644
|
Process.wait(pid)
|
506
645
|
diff = Time.now.to_f - tm
|
@@ -512,11 +651,19 @@ shared_examples "Instrumental Agent" do
|
|
512
651
|
|
513
652
|
it "should not wait longer than EXIT_FLUSH_TIMEOUT to attempt flushing the socket when disconnecting" do
|
514
653
|
agent.increment('foo', 1)
|
515
|
-
wait
|
654
|
+
wait do
|
655
|
+
expect(server.commands.grep(/foo/).size).to eq(1)
|
656
|
+
end
|
516
657
|
expect(agent).to receive(:flush_socket) do
|
517
658
|
r, w = IO.pipe
|
518
|
-
Thread.new do
|
519
|
-
|
659
|
+
Thread.new do # JRuby requires extra thread here according to e9bb707e
|
660
|
+
begin
|
661
|
+
IO.select([r]) # mimic an endless blocking select poll
|
662
|
+
rescue Object => ex
|
663
|
+
# This rescue-raise prevents JRuby from printing a backtrace at
|
664
|
+
# the end of the run complaining about an exception in this thread.
|
665
|
+
raise
|
666
|
+
end
|
520
667
|
end.join
|
521
668
|
end
|
522
669
|
with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
|
@@ -578,7 +725,7 @@ shared_examples "Instrumental Agent" do
|
|
578
725
|
expect(agent.send(:running?)).to eq(true)
|
579
726
|
|
580
727
|
# Setup a failure for the next command so we'll break out of the inner
|
581
|
-
# loop in
|
728
|
+
# loop in run_sender_loop causing another call to open_socket
|
582
729
|
test_connection_fail = true
|
583
730
|
tc = agent.method(:test_connection)
|
584
731
|
allow(agent).to receive(:test_connection) { |*args, &block| test_connection_fail ? raise("fail") : tc.call(*args) }
|
@@ -629,6 +776,329 @@ shared_examples "Instrumental Agent" do
|
|
629
776
|
end
|
630
777
|
end
|
631
778
|
end
|
779
|
+
|
780
|
+
describe Instrumental::Agent, "metrician" do
|
781
|
+
context "enabled" do
|
782
|
+
let(:metrician) { true }
|
783
|
+
|
784
|
+
it "is enabled by default" do
|
785
|
+
a = agent
|
786
|
+
expect(Metrician.agent).to eq(a)
|
787
|
+
end
|
788
|
+
|
789
|
+
it "uses agent logger" do
|
790
|
+
new_logger = double
|
791
|
+
agent.logger = new_logger
|
792
|
+
expect(Metrician.logger).to eq(new_logger)
|
793
|
+
end
|
794
|
+
end
|
795
|
+
|
796
|
+
context "disabled" do
|
797
|
+
let(:metrician) { false }
|
798
|
+
|
799
|
+
it "can be disbaled" do
|
800
|
+
expect(Metrician).to_not receive(:activate)
|
801
|
+
agent = Instrumental::Agent.new('test-token', :metrician => false)
|
802
|
+
end
|
803
|
+
end
|
804
|
+
end
|
805
|
+
|
806
|
+
describe Instrumental::Agent, "aggregation" do
|
807
|
+
context "aggregation enabled" do
|
808
|
+
let(:frequency) { 2 }
|
809
|
+
|
810
|
+
it "can be enabled at Agent.new time" do
|
811
|
+
expect(agent.frequency).to eq(2)
|
812
|
+
end
|
813
|
+
|
814
|
+
it "can be modified by setting the agent frequency" do
|
815
|
+
agent.frequency = 15
|
816
|
+
expect(agent.frequency).to eq(15)
|
817
|
+
end
|
818
|
+
|
819
|
+
it "is disabled by default" do
|
820
|
+
agent = Instrumental::Agent.new('test_token')
|
821
|
+
expect(agent.frequency.to_f).to eq(0)
|
822
|
+
end
|
823
|
+
|
824
|
+
it "should only allow frequencies that align with minutes" do
|
825
|
+
(-5..100).each do |freq|
|
826
|
+
agent.frequency = freq
|
827
|
+
expect(Instrumental::Agent::VALID_FREQUENCIES).to include(agent.frequency)
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
it "bypasses aggregator queue entirely for most commands when frequency == 0" do
|
832
|
+
agent.frequency = 0 # this is red - 0 for green
|
833
|
+
expect(EventAggregator).not_to receive(:new)
|
834
|
+
agent.increment('a_metric')
|
835
|
+
end
|
836
|
+
|
837
|
+
it "adds data to the event aggregator and does not immediately send it" do
|
838
|
+
Timecop.travel start_of_minute
|
839
|
+
agent.increment('test')
|
840
|
+
wait do
|
841
|
+
expect(agent.instance_variable_get(:@event_aggregator).size).to eq(1)
|
842
|
+
expect(agent.instance_variable_get(:@event_aggregator).values.values.first.metric).to eq('test')
|
843
|
+
end
|
844
|
+
end
|
845
|
+
|
846
|
+
it "batches data before sending" do
|
847
|
+
Timecop.freeze do
|
848
|
+
agent.increment('a_metric')
|
849
|
+
agent.increment('a_metric')
|
850
|
+
agent.increment('another_metric')
|
851
|
+
end
|
852
|
+
agent.flush(true)
|
853
|
+
wait do
|
854
|
+
expect(server.commands.grep(/_metric/).size).to eq(2)
|
855
|
+
aggregated_metric = server.commands.grep(/a_metric/).first.split(" ")
|
856
|
+
expect(aggregated_metric[2].to_i).to eq(2) # value
|
857
|
+
expect(aggregated_metric[4].to_i).to eq(2) # count
|
858
|
+
end
|
859
|
+
end
|
860
|
+
|
861
|
+
it "aggregates to the specified frequency within the aggregator" do
|
862
|
+
Timecop.travel(start_of_minute)
|
863
|
+
agent.frequency = 15
|
864
|
+
expect(agent.frequency).not_to be(Instrumental::Agent::DEFAULT_FREQUENCY)
|
865
|
+
agent.increment('metric', 1, Time.at(0))
|
866
|
+
|
867
|
+
# will get aligned to the closest frequency (15)
|
868
|
+
agent.increment('metric', 1, Time.at(20))
|
869
|
+
wait do
|
870
|
+
expect(agent.instance_variable_get(:@event_aggregator).values.keys).to eq(["metric:0", "metric:15"])
|
871
|
+
end
|
872
|
+
agent.flush
|
873
|
+
wait do
|
874
|
+
expect(server.commands.grep(/metric 1 0/).size).to eq(1)
|
875
|
+
expect(server.commands.grep(/metric 1 15/).size).to eq(1)
|
876
|
+
end
|
877
|
+
end
|
878
|
+
|
879
|
+
it "flushes data from both queues before sending" do
|
880
|
+
Timecop.freeze do
|
881
|
+
100.times do |i|
|
882
|
+
agent.increment("test_metric_#{i}")
|
883
|
+
agent.increment("other_metric")
|
884
|
+
end
|
885
|
+
end
|
886
|
+
|
887
|
+
expect(agent.instance_variable_get(:@aggregator_queue).size).to be > 0
|
888
|
+
agent.flush
|
889
|
+
expect(agent.instance_variable_get(:@sender_queue).size).to eq(0)
|
890
|
+
expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(0)
|
891
|
+
|
892
|
+
wait do
|
893
|
+
expect(server.commands.grep(/test_metric/).size).to eq(100)
|
894
|
+
expect(server.commands.grep(/other_metric/).size).to eq(1)
|
895
|
+
end
|
896
|
+
end
|
897
|
+
|
898
|
+
it "does not batch notices" do
|
899
|
+
agent.frequency = 60
|
900
|
+
agent.notice "things are happening", 0, 100
|
901
|
+
agent.notice "things are happening", 0, 100
|
902
|
+
agent.notice "things are happening", 0, 100
|
903
|
+
wait do
|
904
|
+
expect(server.commands.grep(/things are happening/).size).to eq(3)
|
905
|
+
end
|
906
|
+
end
|
907
|
+
|
908
|
+
it "can be disabled by setting frequency to nil" do
|
909
|
+
agent.frequency = nil
|
910
|
+
expect(EventAggregator).not_to receive(:new)
|
911
|
+
agent.increment('metric')
|
912
|
+
wait do
|
913
|
+
expect(server.commands.grep(/metric/).size).to eq(1)
|
914
|
+
end
|
915
|
+
end
|
916
|
+
|
917
|
+
it "can be disabled by setting frequency to 0" do
|
918
|
+
agent.frequency = 0
|
919
|
+
expect(EventAggregator).not_to receive(:new)
|
920
|
+
agent.increment('metric')
|
921
|
+
wait do
|
922
|
+
expect(server.commands.grep(/metric/).size).to eq(1)
|
923
|
+
end
|
924
|
+
end
|
925
|
+
|
926
|
+
it "automatically uses the highest-without-going-over frequency for a bad frequency" do
|
927
|
+
agent.frequency = 17
|
928
|
+
expect(agent.frequency).to eq(15)
|
929
|
+
agent.frequency = 69420
|
930
|
+
expect(agent.frequency).to eq(60)
|
931
|
+
agent.frequency = 0
|
932
|
+
expect(agent.frequency).to eq(0)
|
933
|
+
agent.frequency = -1
|
934
|
+
expect(agent.frequency).to eq(0)
|
935
|
+
end
|
936
|
+
|
937
|
+
it "can take strings as frequency" do
|
938
|
+
agent = Instrumental::Agent.new('test_token', :frequency => "15")
|
939
|
+
expect(agent.frequency).to eq(15)
|
940
|
+
end
|
941
|
+
|
942
|
+
it "should not be enabled at the same time as synchronous" do
|
943
|
+
expect(Instrumental::Agent.logger).to receive(:warn).with(/Synchronous and Frequency should not be enabled at the same time! Defaulting to synchronous mode./)
|
944
|
+
agent = Instrumental::Agent.new('test_token', :synchronous => true, :frequency => 6)
|
945
|
+
expect(agent.synchronous).to eq(true)
|
946
|
+
expect(agent.frequency).to eq(0)
|
947
|
+
end
|
948
|
+
|
949
|
+
it "should use synchronous mode if it is enabled, even if turned on after frequency set at start" do
|
950
|
+
agent.increment('metric')
|
951
|
+
agent.increment('metric')
|
952
|
+
agent.synchronous = true
|
953
|
+
agent.increment('metric')
|
954
|
+
wait do
|
955
|
+
expect(server.commands.grep(/metric 1/).size).to eq(1)
|
956
|
+
end
|
957
|
+
agent.flush
|
958
|
+
wait do
|
959
|
+
expect(server.commands.grep(/metric 1/).size).to eq(1)
|
960
|
+
expect(server.commands.grep(/metric 2/).size).to eq(1)
|
961
|
+
end
|
962
|
+
end
|
963
|
+
|
964
|
+
it "sends aggregated metrics after specified frequency, even if no flush is sent" do
|
965
|
+
agent.frequency = 1
|
966
|
+
Timecop.travel(start_of_minute)
|
967
|
+
agent.increment('metric')
|
968
|
+
agent.increment('metric')
|
969
|
+
agent.gauge('other', 1)
|
970
|
+
agent.gauge('other', 1)
|
971
|
+
agent.gauge('other', 1)
|
972
|
+
sleep (0.5)
|
973
|
+
wait { expect(server.commands.grep(/metric/).size).to eq(0) }
|
974
|
+
sleep (0.51) # total sleep > 1 frequency
|
975
|
+
|
976
|
+
expect(server.commands.grep(/metric 2/).size).to eq(1)
|
977
|
+
expect(server.commands.grep(/other 3/).size).to eq(1)
|
978
|
+
end
|
979
|
+
|
980
|
+
# this test really relies on the worker threads not working unexpectedly
|
981
|
+
it "will overflow if the aggregator queue is full" do
|
982
|
+
Timecop.travel(start_of_minute)
|
983
|
+
with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
|
984
|
+
allow(agent.logger).to receive(:debug)
|
985
|
+
expect(agent.logger).to receive(:debug).with("Dropping command, queue full(3): increment overflow_test 4 300 1")
|
986
|
+
agent.increment('overflow_test', 4, 300, 1)
|
987
|
+
agent.increment('overflow_test', 4, 300, 1)
|
988
|
+
agent.increment('overflow_test', 4, 300, 1)
|
989
|
+
agent.increment('overflow_test', 4, 300, 1)
|
990
|
+
|
991
|
+
expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(3)
|
992
|
+
agent.flush
|
993
|
+
expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(0)
|
994
|
+
end
|
995
|
+
end
|
996
|
+
|
997
|
+
it "if aggregator is at max size, next command will force a forward to the sender thread" do
|
998
|
+
Timecop.travel(start_of_minute)
|
999
|
+
with_constants('Instrumental::Agent::MAX_AGGREGATOR_SIZE' => 3) do
|
1000
|
+
agent.increment('overflow_test1')
|
1001
|
+
agent.increment('overflow_test2')
|
1002
|
+
agent.increment('overflow_test3')
|
1003
|
+
agent.increment('overflow_test4')
|
1004
|
+
agent.increment('overflow_test5')
|
1005
|
+
|
1006
|
+
# only 1 because the 5th command triggers a forward of the first 4
|
1007
|
+
wait do
|
1008
|
+
expect(agent.instance_variable_get(:@event_aggregator).size).to eq(1)
|
1009
|
+
end
|
1010
|
+
agent.flush
|
1011
|
+
wait do
|
1012
|
+
expect(server.commands.grep(/overflow_test/).size).to eq(5)
|
1013
|
+
end
|
1014
|
+
end
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
context do
|
1018
|
+
let(:listen) { false }
|
1019
|
+
it "will not send aggregators to the sender queue if the sender thread is not ready" do
|
1020
|
+
Timecop.travel(start_of_minute)
|
1021
|
+
agent.frequency = 1
|
1022
|
+
|
1023
|
+
with_constants('Instrumental::Agent::MAX_BUFFER' => 3,
|
1024
|
+
'Instrumental::Agent::MAX_AGGREGATOR_SIZE' => 4) do
|
1025
|
+
|
1026
|
+
# fill the queue
|
1027
|
+
agent.increment('overflow_test1')
|
1028
|
+
agent.increment('overflow_test2')
|
1029
|
+
agent.increment('overflow_test3')
|
1030
|
+
|
1031
|
+
# wait until they are all in the aggregator
|
1032
|
+
wait do
|
1033
|
+
expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(0)
|
1034
|
+
expect(agent.instance_variable_get(:@event_aggregator).size).to eq(3)
|
1035
|
+
expect(agent.instance_variable_get(:@sender_queue).size).to eq(0)
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
# fill the queue again
|
1039
|
+
agent.increment('overflow_test1')
|
1040
|
+
agent.increment('overflow_test2')
|
1041
|
+
agent.increment('overflow_test3')
|
1042
|
+
|
1043
|
+
# wait until they are all in the aggregator
|
1044
|
+
wait do
|
1045
|
+
expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(0)
|
1046
|
+
expect(agent.instance_variable_get(:@event_aggregator).size).to eq(3)
|
1047
|
+
expect(agent.instance_variable_get(:@sender_queue).size).to eq(0)
|
1048
|
+
end
|
1049
|
+
|
1050
|
+
# wait for the aggregator to get forwarded and popped by the sender
|
1051
|
+
wait do
|
1052
|
+
expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(0)
|
1053
|
+
expect(agent.instance_variable_get(:@event_aggregator)).to eq(nil)
|
1054
|
+
expect(agent.instance_variable_get(:@sender_queue).size).to eq(1)
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
# fill the queue again
|
1058
|
+
agent.increment('overflow_test4')
|
1059
|
+
agent.increment('overflow_test5')
|
1060
|
+
agent.increment('overflow_test6')
|
1061
|
+
|
1062
|
+
# wait for them all to be in the aggregator
|
1063
|
+
wait do
|
1064
|
+
expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(0)
|
1065
|
+
expect(agent.instance_variable_get(:@event_aggregator).size).to eq(3)
|
1066
|
+
expect(agent.instance_variable_get(:@sender_queue).size).to eq(1)
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
# sleep until the next forward is done
|
1070
|
+
sleep(agent.frequency + 0.1)
|
1071
|
+
|
1072
|
+
# fill the queue again
|
1073
|
+
agent.increment('overflow_test7')
|
1074
|
+
agent.increment('overflow_test8')
|
1075
|
+
agent.increment('overflow_test9')
|
1076
|
+
|
1077
|
+
# because sending is blocked, the prevous aggregator never sent
|
1078
|
+
# when it hits max size, the aggregator queue starts backing up
|
1079
|
+
wait do
|
1080
|
+
expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(1)
|
1081
|
+
expect(agent.instance_variable_get(:@event_aggregator).size).to eq(5)
|
1082
|
+
expect(agent.instance_variable_get(:@sender_queue).size).to eq(1)
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
# send 3 more items, to overflow the aggregator queue
|
1086
|
+
allow(agent.logger).to receive(:debug)
|
1087
|
+
expect(agent.logger).to receive(:debug).with("Dropping command, queue full(3): increment overflow_testc 4 300 1")
|
1088
|
+
agent.increment('overflow_testa')
|
1089
|
+
agent.increment('overflow_testb')
|
1090
|
+
agent.increment('overflow_testc', 4, 300, 1) # will get dropped
|
1091
|
+
|
1092
|
+
wait do
|
1093
|
+
expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(3)
|
1094
|
+
expect(agent.instance_variable_get(:@event_aggregator).size).to eq(5)
|
1095
|
+
expect(agent.instance_variable_get(:@sender_queue).size).to eq(1)
|
1096
|
+
end
|
1097
|
+
end
|
1098
|
+
end
|
1099
|
+
end
|
1100
|
+
end
|
1101
|
+
end
|
632
1102
|
end
|
633
1103
|
end
|
634
1104
|
|