instrumental_agent 1.0.1 → 2.0.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -0
- data/.travis.yml +5 -6
- data/CHANGELOG.md +3 -0
- data/Gemfile +3 -0
- data/README.md +19 -10
- data/instrumental_agent.gemspec +5 -1
- data/lib/instrumental/agent.rb +7 -0
- data/lib/instrumental/version.rb +1 -1
- data/script/setup +22 -4
- data/script/test +34 -2
- data/spec/agent_spec.rb +192 -86
- data/spec/spec_helper.rb +8 -1
- data/spec/test_server.rb +13 -6
- metadata +47 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a89c1b56985adcb54ca4ece8434cdcf03dc4d6e9
|
4
|
+
data.tar.gz: 54781c1980d41308b91e2dab943ce1173a344caa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7616d931a95c446071b4971518cacea08bfd2227d6c13192158de0d979285586bfae0e752b887484d768a9edcc20ee78fa64ee0e7ce16668f358481c2c30632
|
7
|
+
data.tar.gz: d8f5775a27509c6c958772a7aa80e6d99a9778dda00d5c02a94b5fb4e198ebcbe98bde63b15fb1d36d67dab5051772a0e05b77ec94a46063ee2ca7a9e97cb926
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p648
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Instrumental is a [application monitoring platform](https://instrumentalapp.com) built for developers who want a better understanding of their production software. Powerful tools, like the [Instrumental Query Language](https://instrumentalapp.com/docs/query-language), combined with an exploration-focused interface allow you to get real answers to complex questions, in real-time.
|
4
4
|
|
5
|
-
This agent supports custom metric monitoring for Ruby applications. It provides high-data reliability at high scale, without ever blocking your process or causing an exception.
|
5
|
+
This agent supports custom metric monitoring for Ruby applications. It provides high-data reliability at high scale, without ever blocking your process or causing an exception.
|
6
6
|
|
7
7
|
## Setup & Usage
|
8
8
|
|
@@ -12,10 +12,10 @@ Add the gem to your Gemfile.
|
|
12
12
|
gem 'instrumental_agent'
|
13
13
|
```
|
14
14
|
|
15
|
-
Visit [instrumentalapp.com](https://instrumentalapp.com) and create an account, then initialize the agent with your API
|
15
|
+
Visit [instrumentalapp.com](https://instrumentalapp.com) and create an account, then initialize the agent with your [project API token](https://instrumentalapp.com/docs/tokens).
|
16
16
|
|
17
17
|
```ruby
|
18
|
-
I = Instrumental::Agent.new('
|
18
|
+
I = Instrumental::Agent.new('PROJECT_API_TOKEN', :enabled => Rails.env.production?)
|
19
19
|
```
|
20
20
|
|
21
21
|
You'll probably want something like the above, only enabling the agent in production mode so you don't have development and production data writing to the same value. Or you can setup two projects, so that you can verify stats in one, and release them to production in another.
|
@@ -59,14 +59,9 @@ User.find_each do |user|
|
|
59
59
|
end
|
60
60
|
```
|
61
61
|
|
62
|
-
## Server
|
62
|
+
## Server Metrics
|
63
63
|
|
64
|
-
Want
|
65
|
-
|
66
|
-
```sh
|
67
|
-
gem install instrumental_tools
|
68
|
-
instrument_server
|
69
|
-
```
|
64
|
+
Want server stats like load, memory, etc.? Check out [InstrumentalD](https://github.com/instrumental/instrumentald).
|
70
65
|
|
71
66
|
## Agent Control
|
72
67
|
|
@@ -99,6 +94,20 @@ If you plan on tracking metrics in Resque jobs, you will need to explicitly clea
|
|
99
94
|
|
100
95
|
You're required to do this because Resque calls `exit!` when a worker has finished processing, which bypasses Ruby's `at_exit` hooks. The Instrumental Agent installs an `at_exit` hook to flush any pending metrics to the servers, but this hook is bypassed by the `exit!` call; any other code you rely that uses `exit!` should call `I.cleanup` to ensure any pending metrics are correctly sent to the server before exiting the process.
|
101
96
|
|
97
|
+
## Automated Metric Collection
|
98
|
+
|
99
|
+
v2.x+ of the Instrumental Agent introduced automated metric collection for your application by way of the [Metrician gem](https://github.com/Instrumental/metrician-ruby). You can read more about the metrics it collects in the [Instrumental documentation](https://instrumentalapp.com/docs/metrician/installation).
|
100
|
+
|
101
|
+
### Upgrading from 1.x
|
102
|
+
|
103
|
+
If you are upgrading from the pre-2.x version of instrumental and **do not** want automated metric collection, you can disable it by setting the following in your agent setup:
|
104
|
+
|
105
|
+
```
|
106
|
+
I = Instrumental::Agent.new('PROJECT_API_TOKEN',
|
107
|
+
:enabled => Rails.env.production?,
|
108
|
+
:metrician => false
|
109
|
+
)
|
110
|
+
```
|
102
111
|
|
103
112
|
## Troubleshooting & Help
|
104
113
|
|
data/instrumental_agent.gemspec
CHANGED
@@ -10,14 +10,18 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.summary = %q{Custom metric monitoring for Ruby applications via Instrumental}
|
11
11
|
s.description = %q{This agent supports Instrumental custom metric monitoring for Ruby applications. It provides high-data reliability at high scale, without ever blocking your process or causing an exception.}
|
12
12
|
s.license = "MIT"
|
13
|
-
|
13
|
+
s.required_ruby_version = '>= 2.0.0'
|
14
14
|
|
15
15
|
s.files = `git ls-files`.split("\n")
|
16
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_runtime_dependency("metrician", [">= 0"])
|
21
|
+
|
19
22
|
s.add_development_dependency("pry", [">= 0"])
|
20
23
|
s.add_development_dependency("rake", [">= 0"])
|
21
24
|
s.add_development_dependency("rspec", ["~> 3.0"])
|
22
25
|
s.add_development_dependency("fuubar", [">= 0"])
|
26
|
+
s.add_development_dependency("timecop", [">= 0"])
|
23
27
|
end
|
data/lib/instrumental/agent.rb
CHANGED
@@ -5,6 +5,7 @@ require 'openssl' rescue nil
|
|
5
5
|
require 'resolv'
|
6
6
|
require 'thread'
|
7
7
|
require 'socket'
|
8
|
+
require 'metrician'
|
8
9
|
|
9
10
|
|
10
11
|
module Instrumental
|
@@ -77,10 +78,15 @@ module Instrumental
|
|
77
78
|
@certs = certificates
|
78
79
|
@dns_resolutions = 0
|
79
80
|
@last_connect_at = 0
|
81
|
+
@metrician = options[:metrician].nil? ? true : !!options[:metrician]
|
80
82
|
@start_worker_mutex = Mutex.new
|
81
83
|
@queue = Queue.new
|
82
84
|
|
83
85
|
setup_cleanup_at_exit if @enabled
|
86
|
+
|
87
|
+
if @metrician
|
88
|
+
Metrician.activate(self)
|
89
|
+
end
|
84
90
|
end
|
85
91
|
|
86
92
|
# Store a gauge for a metric, optionally at a specific time.
|
@@ -473,6 +479,7 @@ module Instrumental
|
|
473
479
|
# or we cannot reach the server
|
474
480
|
# or the connection state of this socket is in a race
|
475
481
|
logger.error "unable to connect to Instrumental, hanging up with #{@queue.size} messages remaining"
|
482
|
+
logger.debug "Exception: #{err.inspect}\n#{err.backtrace.join("\n")}"
|
476
483
|
allow_reconnect = false
|
477
484
|
else
|
478
485
|
report_exception(err)
|
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,19 @@
|
|
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
|
+
end
|
13
|
+
end
|
14
|
+
else
|
15
|
+
sleep n
|
16
|
+
end
|
5
17
|
end
|
6
18
|
|
7
19
|
FORK_SUPPORTED = begin
|
@@ -24,7 +36,8 @@ shared_examples "Instrumental Agent" do
|
|
24
36
|
let(:synchronous) { false }
|
25
37
|
let(:token) { 'test_token' }
|
26
38
|
let(:address) { server.host_and_port }
|
27
|
-
let(:
|
39
|
+
let(:metrician) { false }
|
40
|
+
let(:agent) { Instrumental::Agent.new(token, :collector => address, :synchronous => synchronous, :enabled => enabled, :secure => secure?, :verify_cert => verify_cert?, :metrician => metrician) }
|
28
41
|
|
29
42
|
# Server options
|
30
43
|
let(:listen) { true }
|
@@ -52,31 +65,36 @@ shared_examples "Instrumental Agent" do
|
|
52
65
|
|
53
66
|
it "should not connect to the server after receiving a metric" do
|
54
67
|
agent.gauge('disabled_test', 1)
|
55
|
-
wait
|
56
|
-
|
68
|
+
wait do
|
69
|
+
expect(server.connect_count).to eq(0)
|
70
|
+
end
|
57
71
|
end
|
58
72
|
|
59
73
|
it "should no op on flush without reconnect" do
|
60
74
|
1.upto(100) { agent.gauge('disabled_test', 1) }
|
61
75
|
agent.flush(false)
|
62
|
-
wait
|
63
|
-
|
76
|
+
wait do
|
77
|
+
expect(server.commands).to be_empty
|
78
|
+
end
|
64
79
|
end
|
65
80
|
|
66
81
|
it "should no op on flush with reconnect" do
|
67
82
|
1.upto(100) { agent.gauge('disabled_test', 1) }
|
68
83
|
agent.flush(true)
|
69
|
-
wait
|
70
|
-
|
84
|
+
wait do
|
85
|
+
expect(server.commands).to be_empty
|
86
|
+
end
|
71
87
|
end
|
72
88
|
|
73
89
|
it "should no op on an empty flush" do
|
74
90
|
agent.flush(true)
|
75
|
-
wait
|
76
|
-
|
91
|
+
wait do
|
92
|
+
expect(server.commands).to be_empty
|
93
|
+
end
|
77
94
|
end
|
78
95
|
|
79
96
|
it "should send metrics to logger" do
|
97
|
+
Timecop.freeze
|
80
98
|
now = Time.now
|
81
99
|
expect(agent.logger).to receive(:debug).with("gauge metric 1 #{now.to_i} 1")
|
82
100
|
agent.gauge("metric", 1)
|
@@ -91,14 +109,16 @@ shared_examples "Instrumental Agent" do
|
|
91
109
|
|
92
110
|
it "should connect to the server after sending a metric" do
|
93
111
|
agent.increment("test.foo")
|
94
|
-
wait
|
95
|
-
|
112
|
+
wait do
|
113
|
+
expect(server.connect_count).to eq(1)
|
114
|
+
end
|
96
115
|
end
|
97
116
|
|
98
117
|
it "should announce itself, and include version" do
|
99
118
|
agent.increment("test.foo")
|
100
|
-
wait
|
101
|
-
|
119
|
+
wait do
|
120
|
+
expect(server.commands[0]).to match(/hello .*/)
|
121
|
+
end
|
102
122
|
expect(server.commands[0]).to match(/ version /)
|
103
123
|
expect(server.commands[0]).to match(/ hostname /)
|
104
124
|
expect(server.commands[0]).to match(/ pid /)
|
@@ -108,15 +128,18 @@ shared_examples "Instrumental Agent" do
|
|
108
128
|
|
109
129
|
it "should authenticate using the token" do
|
110
130
|
agent.increment("test.foo")
|
111
|
-
wait
|
112
|
-
|
131
|
+
wait do
|
132
|
+
expect(server.commands[1]).to eq("authenticate test_token")
|
133
|
+
end
|
113
134
|
end
|
114
135
|
|
115
136
|
it "should report a gauge" do
|
137
|
+
Timecop.freeze
|
116
138
|
now = Time.now
|
117
139
|
agent.gauge('gauge_test', 123)
|
118
|
-
wait
|
119
|
-
|
140
|
+
wait do
|
141
|
+
expect(server.commands.last).to eq("gauge gauge_test 123 #{now.to_i} 1")
|
142
|
+
end
|
120
143
|
end
|
121
144
|
|
122
145
|
it "should report a time as gauge and return the block result" do
|
@@ -125,8 +148,9 @@ shared_examples "Instrumental Agent" do
|
|
125
148
|
1 + 1
|
126
149
|
end
|
127
150
|
expect(return_value).to eq(2)
|
128
|
-
wait
|
129
|
-
|
151
|
+
wait do
|
152
|
+
expect(server.commands.last).to match(/gauge time_value_test .* #{now.to_i}/)
|
153
|
+
end
|
130
154
|
end
|
131
155
|
|
132
156
|
it "should report a time_ms as gauge and return the block result" do
|
@@ -136,8 +160,9 @@ shared_examples "Instrumental Agent" do
|
|
136
160
|
1 + 1
|
137
161
|
end
|
138
162
|
expect(return_value).to eq(2)
|
139
|
-
wait
|
140
|
-
|
163
|
+
wait do
|
164
|
+
expect(server.commands.last).to match(/gauge time_value_test 1000/)
|
165
|
+
end
|
141
166
|
end
|
142
167
|
|
143
168
|
it "should return the value gauged" do
|
@@ -147,21 +172,24 @@ shared_examples "Instrumental Agent" do
|
|
147
172
|
|
148
173
|
it "should report a gauge with a set time" do
|
149
174
|
agent.gauge('gauge_test', 123, 555)
|
150
|
-
wait
|
151
|
-
|
175
|
+
wait do
|
176
|
+
expect(server.commands.last).to eq("gauge gauge_test 123 555 1")
|
177
|
+
end
|
152
178
|
end
|
153
179
|
|
154
180
|
it "should report a gauge with a set time and count" do
|
155
181
|
agent.gauge('gauge_test', 123, 555, 111)
|
156
|
-
wait
|
157
|
-
|
182
|
+
wait do
|
183
|
+
expect(server.commands.last).to eq("gauge gauge_test 123 555 111")
|
184
|
+
end
|
158
185
|
end
|
159
186
|
|
160
187
|
it "should report an increment" do
|
161
188
|
now = Time.now
|
162
189
|
agent.increment("increment_test")
|
163
|
-
wait
|
164
|
-
|
190
|
+
wait do
|
191
|
+
expect(server.commands.last).to eq("increment increment_test 1 #{now.to_i} 1")
|
192
|
+
end
|
165
193
|
end
|
166
194
|
|
167
195
|
it "should return the value incremented by" do
|
@@ -172,33 +200,43 @@ shared_examples "Instrumental Agent" do
|
|
172
200
|
it "should report an increment a value" do
|
173
201
|
now = Time.now
|
174
202
|
agent.increment("increment_test", 2)
|
175
|
-
wait
|
176
|
-
|
203
|
+
wait do
|
204
|
+
expect(server.commands.last).to eq("increment increment_test 2 #{now.to_i} 1")
|
205
|
+
end
|
177
206
|
end
|
178
207
|
|
179
208
|
it "should report an increment with a set time" do
|
180
209
|
agent.increment('increment_test', 1, 555)
|
181
|
-
wait
|
182
|
-
|
210
|
+
wait do
|
211
|
+
expect(server.commands.last).to eq("increment increment_test 1 555 1")
|
212
|
+
end
|
183
213
|
end
|
184
214
|
|
185
215
|
it "should report an increment with a set time and count" do
|
186
216
|
agent.increment('increment_test', 1, 555, 111)
|
187
|
-
wait
|
188
|
-
|
217
|
+
wait do
|
218
|
+
expect(server.commands.last).to eq("increment increment_test 1 555 111")
|
219
|
+
end
|
189
220
|
end
|
190
221
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
222
|
+
context do
|
223
|
+
let(:listen) { false }
|
224
|
+
it "should discard data that overflows the buffer" do
|
225
|
+
with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
|
226
|
+
allow(agent.logger).to receive(:debug)
|
227
|
+
expect(agent.logger).to receive(:debug).with("Dropping command, queue full(3): increment overflow_test 4 300 1")
|
228
|
+
expect(agent.logger).to receive(:debug).with("Dropping command, queue full(3): increment overflow_test 5 300 1")
|
229
|
+
5.times do |i|
|
230
|
+
agent.increment('overflow_test', i + 1, 300)
|
231
|
+
end
|
232
|
+
wait do
|
233
|
+
expect(server.commands).to include("increment overflow_test 1 300 1")
|
234
|
+
expect(server.commands).to include("increment overflow_test 2 300 1")
|
235
|
+
expect(server.commands).to include("increment overflow_test 3 300 1")
|
236
|
+
expect(server.commands).to_not include("increment overflow_test 4 300 1")
|
237
|
+
expect(server.commands).to_not include("increment overflow_test 5 300 1")
|
238
|
+
end
|
195
239
|
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
240
|
end
|
203
241
|
end
|
204
242
|
|
@@ -257,8 +295,9 @@ shared_examples "Instrumental Agent" do
|
|
257
295
|
it "should never let an exception reach the user" do
|
258
296
|
expect(agent).to receive(:send_command).twice { raise(Exception.new("Test Exception")) }
|
259
297
|
expect(agent.increment('throws_exception', 2)).to eq(nil)
|
260
|
-
wait
|
261
|
-
|
298
|
+
wait do
|
299
|
+
expect(agent.increment('throws_exception', 234)).to eq(nil)
|
300
|
+
end
|
262
301
|
end
|
263
302
|
|
264
303
|
it "should let exceptions in time bubble up" do
|
@@ -276,8 +315,9 @@ shared_examples "Instrumental Agent" do
|
|
276
315
|
it "should track invalid metrics" do
|
277
316
|
expect(agent.logger).to receive(:warn).with(/%%/)
|
278
317
|
agent.increment(' %% .!#@$%^&*', 1, 1)
|
279
|
-
wait
|
280
|
-
|
318
|
+
wait do
|
319
|
+
expect(server.commands.join("\n")).to include("increment agent.invalid_metric")
|
320
|
+
end
|
281
321
|
end
|
282
322
|
|
283
323
|
it "should allow reasonable metric names" do
|
@@ -285,15 +325,17 @@ shared_examples "Instrumental Agent" do
|
|
285
325
|
agent.increment('a.b')
|
286
326
|
agent.increment('hello.world')
|
287
327
|
agent.increment('ThisIsATest.Of.The.Emergency.Broadcast.System.12345')
|
288
|
-
wait
|
289
|
-
|
328
|
+
wait do
|
329
|
+
expect(server.commands.join("\n")).to_not include("increment agent.invalid_metric")
|
330
|
+
end
|
290
331
|
end
|
291
332
|
|
292
333
|
it "should track invalid values" do
|
293
334
|
expect(agent.logger).to receive(:warn).with(/hello.*testington/)
|
294
335
|
agent.increment('testington', 'hello')
|
295
|
-
wait
|
296
|
-
|
336
|
+
wait do
|
337
|
+
expect(server.commands.join("\n")).to include("increment agent.invalid_value")
|
338
|
+
end
|
297
339
|
end
|
298
340
|
|
299
341
|
it "should allow reasonable values" do
|
@@ -305,31 +347,51 @@ shared_examples "Instrumental Agent" do
|
|
305
347
|
agent.increment('a', 2.2)
|
306
348
|
agent.increment('a', 333.333)
|
307
349
|
agent.increment('a', Float::EPSILON)
|
308
|
-
wait
|
309
|
-
|
350
|
+
wait do
|
351
|
+
expect(server.commands.join("\n")).to_not include("increment agent.invalid_value")
|
352
|
+
end
|
310
353
|
end
|
311
354
|
|
312
355
|
it "should send notices to the server" do
|
356
|
+
Timecop.freeze
|
313
357
|
tm = Time.now
|
314
358
|
agent.notice("Test note", tm)
|
315
|
-
wait
|
316
|
-
|
359
|
+
wait do
|
360
|
+
expect(server.commands.join("\n")).to include("notice #{tm.to_i} 0 Test note")
|
361
|
+
end
|
317
362
|
end
|
318
363
|
|
319
364
|
it "should prevent a note w/ newline characters from being sent to the server" do
|
320
|
-
expect(agent.notice("
|
321
|
-
|
322
|
-
|
365
|
+
expect(agent.notice("Test_bad_note\n")).to eq(nil)
|
366
|
+
|
367
|
+
# Send a note that make it through so we're sure the bad note would have
|
368
|
+
# arrived if it was going to.
|
369
|
+
Timecop.freeze
|
370
|
+
tm = Time.now
|
371
|
+
agent.notice("Test_good_note", tm)
|
372
|
+
wait do
|
373
|
+
expect(server.commands.join("\n")).to include("Test_good_note")
|
374
|
+
end
|
375
|
+
|
376
|
+
expect(server.commands.join("\n")).to_not include("Test_bad_note")
|
323
377
|
end
|
324
378
|
|
325
379
|
it "should allow outgoing metrics to be stopped" do
|
326
380
|
tm = Time.now
|
327
381
|
agent.increment("foo.bar", 1, tm)
|
328
382
|
agent.stop
|
383
|
+
|
384
|
+
# In Java the test server hangs sometimes when the agent disconnects so
|
385
|
+
# this cleans up the server.
|
386
|
+
server.stop
|
329
387
|
wait
|
330
|
-
|
388
|
+
server.listen
|
389
|
+
|
331
390
|
wait
|
332
|
-
|
391
|
+
agent.increment("foo.baz", 1, tm)
|
392
|
+
wait do
|
393
|
+
expect(server.commands.join("\n")).to include("increment foo.baz 1 #{tm.to_i}")
|
394
|
+
end
|
333
395
|
expect(server.commands.join("\n")).to_not include("increment foo.bar 1 #{tm.to_i}")
|
334
396
|
end
|
335
397
|
|
@@ -338,28 +400,33 @@ shared_examples "Instrumental Agent" do
|
|
338
400
|
expect(agent.instance_variable_get(:@queue).size).to be > 0
|
339
401
|
agent.flush
|
340
402
|
expect(agent.instance_variable_get(:@queue).size).to eq(0)
|
341
|
-
wait
|
342
|
-
|
403
|
+
wait do
|
404
|
+
expect(server.commands.grep(/^gauge a /).size).to eq(100)
|
405
|
+
end
|
343
406
|
end
|
344
407
|
|
345
408
|
it "should no op on an empty flush" do
|
346
409
|
agent.flush(true)
|
347
|
-
wait
|
348
|
-
|
410
|
+
wait do
|
411
|
+
expect(server.commands).to be_empty
|
412
|
+
end
|
349
413
|
end
|
350
414
|
end
|
351
415
|
|
352
416
|
describe Instrumental::Agent, "connection problems" do
|
353
417
|
it "should automatically reconnect on disconnect" do
|
354
|
-
agent.increment("
|
355
|
-
wait
|
418
|
+
agent.increment("reconnect_test1", 1, 1234)
|
419
|
+
wait do
|
420
|
+
expect(server.commands.grep(/reconnect_test1/).size).to eq(1)
|
421
|
+
end
|
356
422
|
server.disconnect_all
|
357
423
|
wait(1)
|
358
424
|
agent.increment('reconnect_test', 1, 5678) # triggers reconnect
|
359
|
-
wait
|
360
|
-
|
361
|
-
|
362
|
-
|
425
|
+
wait do
|
426
|
+
expect(server.connect_count).to eq(2)
|
427
|
+
# Ensure the last command sent has been received after the reconnect attempt
|
428
|
+
expect(server.commands.last).to eq("increment reconnect_test 1 5678 1")
|
429
|
+
end
|
363
430
|
end
|
364
431
|
|
365
432
|
context 'not listening' do
|
@@ -413,43 +480,48 @@ shared_examples "Instrumental Agent" do
|
|
413
480
|
it "should cancel the worker thread when the host has hung up" do
|
414
481
|
# Start the background agent thread and let it send one metric successfully
|
415
482
|
agent.gauge('connection_failure', 1, 1234)
|
416
|
-
wait
|
483
|
+
wait do
|
484
|
+
expect(server.commands.grep(/connection_failure/).size).to eq(1)
|
485
|
+
end
|
417
486
|
# Stop the server
|
418
487
|
server.stop
|
419
488
|
wait
|
420
489
|
# Send one metric to the stopped server
|
421
490
|
agent.gauge('connection_failure', 1, 1234)
|
422
|
-
wait
|
423
491
|
# The agent thread should have stopped running since the network write would
|
424
492
|
# have failed. The queue will still contain the metric that has yet to be sent
|
425
|
-
|
493
|
+
wait do
|
494
|
+
expect(agent.send(:running?)).to eq(false)
|
495
|
+
end
|
426
496
|
expect(agent.queue.size).to eq(1)
|
427
497
|
end
|
428
498
|
|
429
499
|
it "should restart the worker thread after hanging it up during an unreachable host event" do
|
430
500
|
# Start the background agent thread and let it send one metric successfully
|
431
501
|
agent.gauge('connection_failure', 1, 1234)
|
432
|
-
wait
|
502
|
+
wait do
|
503
|
+
expect(server.commands.grep(/connection_failure/).size).to eq(1)
|
504
|
+
end
|
433
505
|
# Stop the server
|
434
506
|
server.stop
|
435
507
|
wait
|
436
508
|
# Send one metric to the stopped server
|
437
509
|
agent.gauge('connection_failure', 1, 1234)
|
438
|
-
wait
|
439
510
|
# The agent thread should have stopped running since the network write would
|
440
511
|
# have failed. The queue will still contain the metric that has yet to be sent
|
441
|
-
|
512
|
+
wait do
|
513
|
+
expect(agent.send(:running?)).to eq(false)
|
514
|
+
end
|
442
515
|
expect(agent.queue.size).to eq(1)
|
443
|
-
wait
|
444
516
|
# Start the server back up again
|
445
517
|
server.listen
|
446
|
-
wait
|
447
518
|
# Sending another metric should kickstart the background worker thread
|
448
519
|
agent.gauge('connection_failure', 1, 1234)
|
449
|
-
wait
|
450
520
|
# The agent should now be running the background thread, and the queue should be empty
|
451
|
-
|
452
|
-
|
521
|
+
wait do
|
522
|
+
expect(agent.send(:running?)).to eq(true)
|
523
|
+
expect(agent.queue.size).to eq(0)
|
524
|
+
end
|
453
525
|
end
|
454
526
|
|
455
527
|
end
|
@@ -512,11 +584,19 @@ shared_examples "Instrumental Agent" do
|
|
512
584
|
|
513
585
|
it "should not wait longer than EXIT_FLUSH_TIMEOUT to attempt flushing the socket when disconnecting" do
|
514
586
|
agent.increment('foo', 1)
|
515
|
-
wait
|
587
|
+
wait do
|
588
|
+
expect(server.commands.grep(/foo/).size).to eq(1)
|
589
|
+
end
|
516
590
|
expect(agent).to receive(:flush_socket) do
|
517
591
|
r, w = IO.pipe
|
518
|
-
Thread.new do
|
519
|
-
|
592
|
+
Thread.new do # JRuby requires extra thread here according to e9bb707e
|
593
|
+
begin
|
594
|
+
IO.select([r]) # mimic an endless blocking select poll
|
595
|
+
rescue Object => ex
|
596
|
+
# This rescue-raise prevents JRuby from printing a backtrace at
|
597
|
+
# the end of the run complaining about an exception in this thread.
|
598
|
+
raise
|
599
|
+
end
|
520
600
|
end.join
|
521
601
|
end
|
522
602
|
with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
|
@@ -629,6 +709,32 @@ shared_examples "Instrumental Agent" do
|
|
629
709
|
end
|
630
710
|
end
|
631
711
|
end
|
712
|
+
|
713
|
+
describe Instrumental::Agent, "metrician" do
|
714
|
+
context "enabled" do
|
715
|
+
let(:metrician) { true }
|
716
|
+
|
717
|
+
it "is enabled by default" do
|
718
|
+
a = agent
|
719
|
+
expect(Metrician.agent).to eq(a)
|
720
|
+
end
|
721
|
+
|
722
|
+
it "uses agent logger" do
|
723
|
+
new_logger = double
|
724
|
+
agent.logger = new_logger
|
725
|
+
expect(Metrician.logger).to eq(new_logger)
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
context "disabled" do
|
730
|
+
let(:metrician) { false }
|
731
|
+
|
732
|
+
it "can be disbaled" do
|
733
|
+
expect(Metrician).to_not receive(:activate)
|
734
|
+
agent = Instrumental::Agent.new('test-token', :metrician => false)
|
735
|
+
end
|
736
|
+
end
|
737
|
+
end
|
632
738
|
end
|
633
739
|
end
|
634
740
|
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
$: << File.join(File.dirname(__FILE__), "..", "lib")
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
Bundler.require(:default, :development)
|
6
|
+
|
4
7
|
require 'test_server'
|
5
8
|
|
6
9
|
RSpec.configure do |config|
|
@@ -8,6 +11,10 @@ RSpec.configure do |config|
|
|
8
11
|
config.before(:all) do
|
9
12
|
end
|
10
13
|
|
14
|
+
config.before(:each) do
|
15
|
+
Timecop.return
|
16
|
+
end
|
17
|
+
|
11
18
|
config.after(:all) do
|
12
19
|
end
|
13
20
|
|
data/spec/test_server.rb
CHANGED
@@ -77,15 +77,14 @@ class TestServer
|
|
77
77
|
end
|
78
78
|
rescue Exception => err
|
79
79
|
unless @stopping
|
80
|
-
puts "EXCEPTION:", err
|
80
|
+
puts "EXCEPTION:", err.inspect, err.backtrace.join("\n")
|
81
81
|
retry
|
82
82
|
end
|
83
83
|
end
|
84
84
|
end
|
85
85
|
# puts "server up"
|
86
86
|
rescue Errno::EADDRINUSE => err
|
87
|
-
puts "#{err.inspect} failed to get port #{@port}"
|
88
|
-
puts err.message
|
87
|
+
puts "#{err.inspect} failed to get port #{@port}", err.message
|
89
88
|
@port += 1
|
90
89
|
retry
|
91
90
|
end
|
@@ -97,7 +96,10 @@ class TestServer
|
|
97
96
|
def stop
|
98
97
|
@stopping = true
|
99
98
|
disconnect_all
|
100
|
-
|
99
|
+
if @main_thread
|
100
|
+
@main_thread.kill
|
101
|
+
@main_thread.join # wait for thread to die
|
102
|
+
end
|
101
103
|
@main_thread = nil
|
102
104
|
@client_threads.each { |thread| thread.kill }
|
103
105
|
@client_threads = []
|
@@ -105,6 +107,8 @@ class TestServer
|
|
105
107
|
@server.close if @server
|
106
108
|
rescue Exception => e
|
107
109
|
end
|
110
|
+
@server = nil
|
111
|
+
@stopping = false
|
108
112
|
end
|
109
113
|
|
110
114
|
def fd_for_socket(socket)
|
@@ -130,8 +134,11 @@ class TestServer
|
|
130
134
|
c.flush
|
131
135
|
c.close
|
132
136
|
rescue Exception => e
|
133
|
-
|
134
|
-
|
137
|
+
unless @stopping
|
138
|
+
puts "Error in TestServer#disconnect_all"
|
139
|
+
puts e.inspect
|
140
|
+
puts e.backtrace.join("\n")
|
141
|
+
end
|
135
142
|
end
|
136
143
|
}
|
137
144
|
@connections = []
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: instrumental_agent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0.alpha
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Elijah Miller
|
@@ -11,62 +11,90 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2017-08-18 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: metrician
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
16
30
|
- !ruby/object:Gem::Dependency
|
17
31
|
name: pry
|
18
32
|
requirement: !ruby/object:Gem::Requirement
|
19
33
|
requirements:
|
20
|
-
- -
|
34
|
+
- - '>='
|
21
35
|
- !ruby/object:Gem::Version
|
22
36
|
version: '0'
|
23
37
|
type: :development
|
24
38
|
prerelease: false
|
25
39
|
version_requirements: !ruby/object:Gem::Requirement
|
26
40
|
requirements:
|
27
|
-
- -
|
41
|
+
- - '>='
|
28
42
|
- !ruby/object:Gem::Version
|
29
43
|
version: '0'
|
30
44
|
- !ruby/object:Gem::Dependency
|
31
45
|
name: rake
|
32
46
|
requirement: !ruby/object:Gem::Requirement
|
33
47
|
requirements:
|
34
|
-
- -
|
48
|
+
- - '>='
|
35
49
|
- !ruby/object:Gem::Version
|
36
50
|
version: '0'
|
37
51
|
type: :development
|
38
52
|
prerelease: false
|
39
53
|
version_requirements: !ruby/object:Gem::Requirement
|
40
54
|
requirements:
|
41
|
-
- -
|
55
|
+
- - '>='
|
42
56
|
- !ruby/object:Gem::Version
|
43
57
|
version: '0'
|
44
58
|
- !ruby/object:Gem::Dependency
|
45
59
|
name: rspec
|
46
60
|
requirement: !ruby/object:Gem::Requirement
|
47
61
|
requirements:
|
48
|
-
- -
|
62
|
+
- - ~>
|
49
63
|
- !ruby/object:Gem::Version
|
50
64
|
version: '3.0'
|
51
65
|
type: :development
|
52
66
|
prerelease: false
|
53
67
|
version_requirements: !ruby/object:Gem::Requirement
|
54
68
|
requirements:
|
55
|
-
- -
|
69
|
+
- - ~>
|
56
70
|
- !ruby/object:Gem::Version
|
57
71
|
version: '3.0'
|
58
72
|
- !ruby/object:Gem::Dependency
|
59
73
|
name: fuubar
|
60
74
|
requirement: !ruby/object:Gem::Requirement
|
61
75
|
requirements:
|
62
|
-
- -
|
76
|
+
- - '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
type: :development
|
80
|
+
prerelease: false
|
81
|
+
version_requirements: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
- !ruby/object:Gem::Dependency
|
87
|
+
name: timecop
|
88
|
+
requirement: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - '>='
|
63
91
|
- !ruby/object:Gem::Version
|
64
92
|
version: '0'
|
65
93
|
type: :development
|
66
94
|
prerelease: false
|
67
95
|
version_requirements: !ruby/object:Gem::Requirement
|
68
96
|
requirements:
|
69
|
-
- -
|
97
|
+
- - '>='
|
70
98
|
- !ruby/object:Gem::Version
|
71
99
|
version: '0'
|
72
100
|
description: This agent supports Instrumental custom metric monitoring for Ruby applications.
|
@@ -78,9 +106,10 @@ executables: []
|
|
78
106
|
extensions: []
|
79
107
|
extra_rdoc_files: []
|
80
108
|
files:
|
81
|
-
-
|
82
|
-
-
|
83
|
-
-
|
109
|
+
- .gitignore
|
110
|
+
- .rspec
|
111
|
+
- .ruby-version
|
112
|
+
- .travis.yml
|
84
113
|
- CHANGELOG.md
|
85
114
|
- Gemfile
|
86
115
|
- Guardfile
|
@@ -114,17 +143,17 @@ require_paths:
|
|
114
143
|
- lib
|
115
144
|
required_ruby_version: !ruby/object:Gem::Requirement
|
116
145
|
requirements:
|
117
|
-
- -
|
146
|
+
- - '>='
|
118
147
|
- !ruby/object:Gem::Version
|
119
|
-
version:
|
148
|
+
version: 2.0.0
|
120
149
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
150
|
requirements:
|
122
|
-
- -
|
151
|
+
- - '>'
|
123
152
|
- !ruby/object:Gem::Version
|
124
|
-
version:
|
153
|
+
version: 1.3.1
|
125
154
|
requirements: []
|
126
155
|
rubyforge_project:
|
127
|
-
rubygems_version: 2.
|
156
|
+
rubygems_version: 2.0.14.1
|
128
157
|
signing_key:
|
129
158
|
specification_version: 4
|
130
159
|
summary: Custom metric monitoring for Ruby applications via Instrumental
|