instrumental_agent 0.13.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -2
- data/LICENSE +1 -1
- data/README.md +34 -17
- data/instrumental_agent.gemspec +7 -6
- data/lib/instrumental/version.rb +1 -1
- data/script/setup +8 -0
- data/script/test +6 -0
- data/spec/agent_spec.rb +138 -132
- data/spec/spec_helper.rb +2 -0
- metadata +26 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5faedb2800827d3604f4a6c0b13bd33234d55ec
|
4
|
+
data.tar.gz: a0115cc25b12d9749dcfeef580bfb26734001e21
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 282bb711342714bdb4be0a1afec481f39bb6fd9fce58f001a9ba662ae10e17ddade912743b85bcf87a8f026b8db0c873d420e9ecffcb213cb5e047a018794886
|
7
|
+
data.tar.gz: a3114f1773d7442e74d9775906324eb8717ad799980668e0dc0be7a1f754196c34331f9859603249c115f748a7934248762329f275f6022135837019524d8f5d
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
### 1.0.0 [May 13, 2016]
|
2
|
+
* No significant code changes
|
3
|
+
|
1
4
|
### 0.13.4 [October 6, 2015]
|
2
5
|
* Fixes for when agent times out communicating with server
|
3
6
|
|
@@ -68,7 +71,7 @@
|
|
68
71
|
* Fix for dead lock issuew
|
69
72
|
|
70
73
|
### 0.9.5 [March 23rd, 2012]
|
71
|
-
* Defer startup of agent thread until metrics are submitted - this update is strongly recommended for anyone using Ruby Enterprise Edition in concert w/ a preforking application server (like Phusion Passenger).
|
74
|
+
* Defer startup of agent thread until metrics are submitted - this update is strongly recommended for anyone using Ruby Enterprise Edition in concert w/ a preforking application server (like Phusion Passenger).
|
72
75
|
* Add .stop method for cancelling agent processing
|
73
76
|
* Changes to how defaults are processed at initialization
|
74
77
|
* Documentation for usage w/ Resque and Resque like scenarios
|
@@ -115,7 +118,7 @@
|
|
115
118
|
* Code cleanup
|
116
119
|
|
117
120
|
### 0.5.1 [December 12, 2011]
|
118
|
-
* instrument_server moved to instrumental_tools gem (https://github.com/
|
121
|
+
* instrument_server moved to instrumental_tools gem (https://github.com/instrumental/instrumental_tools)
|
119
122
|
|
120
123
|
### 0.5 [December 9, 2011]
|
121
124
|
* Allow negative numbers to be submitted
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
# Instrumental Agent
|
1
|
+
# Instrumental Ruby Agent
|
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
|
+
|
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.
|
4
6
|
|
5
7
|
## Setup & Usage
|
6
8
|
|
@@ -10,17 +12,17 @@ Add the gem to your Gemfile.
|
|
10
12
|
gem 'instrumental_agent'
|
11
13
|
```
|
12
14
|
|
13
|
-
Visit [instrumentalapp.com](https://instrumentalapp.com) and create an account, then
|
15
|
+
Visit [instrumentalapp.com](https://instrumentalapp.com) and create an account, then initialize the agent with your API key, found in the Docs section.
|
14
16
|
|
15
|
-
```
|
17
|
+
```ruby
|
16
18
|
I = Instrumental::Agent.new('YOUR_API_KEY', :enabled => Rails.env.production?)
|
17
19
|
```
|
18
20
|
|
19
|
-
You'll
|
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.
|
20
22
|
|
21
23
|
Now you can begin to use Instrumental to track your application.
|
22
24
|
|
23
|
-
```
|
25
|
+
```ruby
|
24
26
|
I.gauge('load', 1.23) # value at a point in time
|
25
27
|
|
26
28
|
I.increment('signups') # increasing value, think "events"
|
@@ -37,20 +39,20 @@ end
|
|
37
39
|
|
38
40
|
Want to track an event (like an application deploy, or downtime)? You can capture events that are instantaneous, or events that happen over a period of time.
|
39
41
|
|
40
|
-
```
|
42
|
+
```ruby
|
41
43
|
I.notice('Jeffy deployed rev ef3d6a') # instantaneous event
|
42
44
|
I.notice('Testing socket buffer increase', 3.days.ago, 20.minutes) # an event with a duration
|
43
45
|
```
|
44
46
|
|
45
47
|
## Backfilling
|
46
48
|
|
47
|
-
Streaming data is better with a little historical context. Instrumental lets you
|
49
|
+
Streaming data is better with a little historical context. Instrumental lets you backfill data, allowing you to see deep into your project's past.
|
48
50
|
|
49
51
|
When backfilling, you may send tens of thousands of metrics per second, and the command buffer may start discarding data it isn't able to send fast enough. We provide a synchronous mode that will ensure every stat makes it to Instrumental before continuing on to the next.
|
50
52
|
|
51
53
|
**Warning**: You should only enable synchronous mode for backfilling data as any issues with the Instrumental service issues will cause this code to halt until it can reconnect.
|
52
54
|
|
53
|
-
```
|
55
|
+
```ruby
|
54
56
|
I.synchronous = true # every command sends immediately
|
55
57
|
User.find_each do |user|
|
56
58
|
I.increment('signups', 1, user.created_at)
|
@@ -59,7 +61,7 @@ end
|
|
59
61
|
|
60
62
|
## Server Stats
|
61
63
|
|
62
|
-
Want some general server stats (load, memory, etc.)? Check out the [instrumental_tools](https://github.com/
|
64
|
+
Want some general server stats (load, memory, etc.)? Check out the [instrumental_tools](https://github.com/instrumental/instrumental_tools) gem.
|
63
65
|
|
64
66
|
```sh
|
65
67
|
gem install instrumental_tools
|
@@ -73,7 +75,7 @@ Need to quickly disable the agent? set :enabled to false on initialization and y
|
|
73
75
|
|
74
76
|
## Capistrano Integration
|
75
77
|
|
76
|
-
Add `require "instrumental/capistrano"` to your capistrano configuration and your deploys will be tracked by Instrumental.
|
78
|
+
Add `require "instrumental/capistrano"` to your capistrano configuration and your deploys will be tracked by Instrumental. Add the API token for the project you want to track to by setting the following Capistrano var:
|
77
79
|
|
78
80
|
```ruby
|
79
81
|
set :instrumental_key, "MY_API_KEY"
|
@@ -93,15 +95,30 @@ The default message sent is "USER deployed COMMIT_HASH". If you need to customiz
|
|
93
95
|
|
94
96
|
## Tracking metrics in Resque jobs (and Resque-like scenarios)
|
95
97
|
|
96
|
-
If you plan on tracking metrics in Resque jobs, you will need to explicitly cleanup after the agent when the jobs are finished.
|
98
|
+
If you plan on tracking metrics in Resque jobs, you will need to explicitly cleanup after the agent when the jobs are finished. You can accomplish this by adding `after_perform` and `on_failure` hooks to your Resque jobs. See the Resque [hooks documentation](https://github.com/resque/resque/blob/master/docs/HOOKS.md) for more information.
|
97
99
|
|
98
|
-
You're required to do this because Resque calls `exit!` when a worker has finished processing, which bypasses Ruby's `at_exit` hooks.
|
99
|
-
|
100
|
-
## Using with Ruby Enterprise Edition
|
101
|
-
|
102
|
-
Users of Ruby Enterprise Edition should plan on using version 0.9.5 of the Instrumental Agent or greater. Please see the [REE wiki page](https://github.com/fastestforward/instrumental_agent/wiki/Using-with-Ruby-Enterprise-Edition) for more information.
|
100
|
+
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.
|
103
101
|
|
104
102
|
|
105
103
|
## Troubleshooting & Help
|
106
104
|
|
107
105
|
We are here to help. Email us at [support@instrumentalapp.com](mailto:support@instrumentalapp.com).
|
106
|
+
|
107
|
+
|
108
|
+
## Release Process
|
109
|
+
|
110
|
+
1. Pull latest master
|
111
|
+
2. Merge feature branch(es) into master
|
112
|
+
3. `script/test`
|
113
|
+
4. Increment version in:
|
114
|
+
- `lib/instrumental/version.rb`
|
115
|
+
5. Update [CHANGELOG.md](CHANGELOG.md)
|
116
|
+
6. Commit "Release vX.Y.Z"
|
117
|
+
7. Push to GitHub
|
118
|
+
8. Release packages: `rake release`
|
119
|
+
9. Update documentation on instrumentalapp.com
|
120
|
+
|
121
|
+
|
122
|
+
## Version Policy
|
123
|
+
|
124
|
+
This library follows [Semantic Versioning 2.0.0](http://semver.org).
|
data/instrumental_agent.gemspec
CHANGED
@@ -6,9 +6,9 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.version = Instrumental::VERSION
|
7
7
|
s.authors = ["Elijah Miller", "Christopher Zelenak", "Kristopher Chambers", "Matthew Hassfurder"]
|
8
8
|
s.email = ["support@instrumentalapp.com"]
|
9
|
-
s.homepage = "http://github.com/
|
10
|
-
s.summary = %q{
|
11
|
-
s.description = %q{
|
9
|
+
s.homepage = "http://github.com/instrumental/instrumental_agent-ruby"
|
10
|
+
s.summary = %q{Custom metric monitoring for Ruby applications via Instrumental}
|
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
|
|
14
14
|
|
@@ -16,7 +16,8 @@ Gem::Specification.new do |s|
|
|
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
|
-
s.add_development_dependency(
|
20
|
-
s.add_development_dependency(
|
21
|
-
s.add_development_dependency(
|
19
|
+
s.add_development_dependency("pry", [">= 0"])
|
20
|
+
s.add_development_dependency("rake", [">= 0"])
|
21
|
+
s.add_development_dependency("rspec", ["~> 3.0"])
|
22
|
+
s.add_development_dependency("fuubar", [">= 0"])
|
22
23
|
end
|
data/lib/instrumental/version.rb
CHANGED
data/script/setup
ADDED
data/script/test
ADDED
data/spec/agent_spec.rb
CHANGED
@@ -47,38 +47,38 @@ shared_examples "Instrumental Agent" do
|
|
47
47
|
let(:enabled) { false }
|
48
48
|
|
49
49
|
it "should not connect to the server" do
|
50
|
-
server.connect_count.
|
50
|
+
expect(server.connect_count).to eq(0)
|
51
51
|
end
|
52
52
|
|
53
53
|
it "should not connect to the server after receiving a metric" do
|
54
54
|
agent.gauge('disabled_test', 1)
|
55
55
|
wait
|
56
|
-
server.connect_count.
|
56
|
+
expect(server.connect_count).to eq(0)
|
57
57
|
end
|
58
58
|
|
59
59
|
it "should no op on flush without reconnect" do
|
60
60
|
1.upto(100) { agent.gauge('disabled_test', 1) }
|
61
61
|
agent.flush(false)
|
62
62
|
wait
|
63
|
-
server.commands.
|
63
|
+
expect(server.commands).to be_empty
|
64
64
|
end
|
65
65
|
|
66
66
|
it "should no op on flush with reconnect" do
|
67
67
|
1.upto(100) { agent.gauge('disabled_test', 1) }
|
68
68
|
agent.flush(true)
|
69
69
|
wait
|
70
|
-
server.commands.
|
70
|
+
expect(server.commands).to be_empty
|
71
71
|
end
|
72
72
|
|
73
73
|
it "should no op on an empty flush" do
|
74
74
|
agent.flush(true)
|
75
75
|
wait
|
76
|
-
server.commands.
|
76
|
+
expect(server.commands).to be_empty
|
77
77
|
end
|
78
78
|
|
79
79
|
it "should send metrics to logger" do
|
80
80
|
now = Time.now
|
81
|
-
agent.logger.
|
81
|
+
expect(agent.logger).to receive(:debug).with("gauge metric 1 #{now.to_i} 1")
|
82
82
|
agent.gauge("metric", 1)
|
83
83
|
end
|
84
84
|
end
|
@@ -86,97 +86,106 @@ shared_examples "Instrumental Agent" do
|
|
86
86
|
describe Instrumental::Agent, "enabled" do
|
87
87
|
|
88
88
|
it "should not connect to the server" do
|
89
|
-
server.connect_count.
|
89
|
+
expect(server.connect_count).to eq(0)
|
90
90
|
end
|
91
91
|
|
92
92
|
it "should connect to the server after sending a metric" do
|
93
93
|
agent.increment("test.foo")
|
94
94
|
wait
|
95
|
-
server.connect_count.
|
95
|
+
expect(server.connect_count).to eq(1)
|
96
96
|
end
|
97
97
|
|
98
98
|
it "should announce itself, and include version" do
|
99
99
|
agent.increment("test.foo")
|
100
100
|
wait
|
101
|
-
server.commands[0].
|
102
|
-
server.commands[0].
|
103
|
-
server.commands[0].
|
104
|
-
server.commands[0].
|
105
|
-
server.commands[0].
|
106
|
-
server.commands[0].
|
101
|
+
expect(server.commands[0]).to match(/hello .*/)
|
102
|
+
expect(server.commands[0]).to match(/ version /)
|
103
|
+
expect(server.commands[0]).to match(/ hostname /)
|
104
|
+
expect(server.commands[0]).to match(/ pid /)
|
105
|
+
expect(server.commands[0]).to match(/ runtime /)
|
106
|
+
expect(server.commands[0]).to match(/ platform /)
|
107
107
|
end
|
108
108
|
|
109
109
|
it "should authenticate using the token" do
|
110
110
|
agent.increment("test.foo")
|
111
111
|
wait
|
112
|
-
server.commands[1].
|
112
|
+
expect(server.commands[1]).to eq("authenticate test_token")
|
113
113
|
end
|
114
114
|
|
115
115
|
it "should report a gauge" do
|
116
116
|
now = Time.now
|
117
117
|
agent.gauge('gauge_test', 123)
|
118
118
|
wait
|
119
|
-
server.commands.last.
|
119
|
+
expect(server.commands.last).to eq("gauge gauge_test 123 #{now.to_i} 1")
|
120
120
|
end
|
121
121
|
|
122
122
|
it "should report a time as gauge and return the block result" do
|
123
123
|
now = Time.now
|
124
|
-
agent.time("time_value_test") do
|
124
|
+
return_value = agent.time("time_value_test") do
|
125
125
|
1 + 1
|
126
|
-
end
|
126
|
+
end
|
127
|
+
expect(return_value).to eq(2)
|
127
128
|
wait
|
128
|
-
server.commands.last.
|
129
|
+
expect(server.commands.last).to match(/gauge time_value_test .* #{now.to_i}/)
|
129
130
|
end
|
130
131
|
|
131
|
-
it "should return the
|
132
|
-
|
133
|
-
agent.
|
134
|
-
|
132
|
+
it "should report a time_ms as gauge and return the block result" do
|
133
|
+
allow(Time).to receive(:now).and_return(100)
|
134
|
+
return_value = agent.time_ms("time_value_test") do
|
135
|
+
allow(Time).to receive(:now).and_return(101)
|
136
|
+
1 + 1
|
137
|
+
end
|
138
|
+
expect(return_value).to eq(2)
|
135
139
|
wait
|
140
|
+
expect(server.commands.last).to match(/gauge time_value_test 1000/)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should return the value gauged" do
|
144
|
+
expect(agent.gauge('gauge_test', 123)).to eq(123)
|
145
|
+
expect(agent.gauge('gauge_test', 989)).to eq(989)
|
136
146
|
end
|
137
147
|
|
138
148
|
it "should report a gauge with a set time" do
|
139
149
|
agent.gauge('gauge_test', 123, 555)
|
140
150
|
wait
|
141
|
-
server.commands.last.
|
151
|
+
expect(server.commands.last).to eq("gauge gauge_test 123 555 1")
|
142
152
|
end
|
143
153
|
|
144
154
|
it "should report a gauge with a set time and count" do
|
145
155
|
agent.gauge('gauge_test', 123, 555, 111)
|
146
156
|
wait
|
147
|
-
server.commands.last.
|
157
|
+
expect(server.commands.last).to eq("gauge gauge_test 123 555 111")
|
148
158
|
end
|
149
159
|
|
150
160
|
it "should report an increment" do
|
151
161
|
now = Time.now
|
152
162
|
agent.increment("increment_test")
|
153
163
|
wait
|
154
|
-
server.commands.last.
|
164
|
+
expect(server.commands.last).to eq("increment increment_test 1 #{now.to_i} 1")
|
155
165
|
end
|
156
166
|
|
157
167
|
it "should return the value incremented by" do
|
158
|
-
|
159
|
-
agent.increment("increment_test").
|
160
|
-
agent.increment("increment_test", 5).should == 5
|
168
|
+
expect(agent.increment("increment_test")).to eq(1)
|
169
|
+
expect(agent.increment("increment_test", 5)).to eq(5)
|
161
170
|
end
|
162
171
|
|
163
172
|
it "should report an increment a value" do
|
164
173
|
now = Time.now
|
165
174
|
agent.increment("increment_test", 2)
|
166
175
|
wait
|
167
|
-
server.commands.last.
|
176
|
+
expect(server.commands.last).to eq("increment increment_test 2 #{now.to_i} 1")
|
168
177
|
end
|
169
178
|
|
170
179
|
it "should report an increment with a set time" do
|
171
180
|
agent.increment('increment_test', 1, 555)
|
172
181
|
wait
|
173
|
-
server.commands.last.
|
182
|
+
expect(server.commands.last).to eq("increment increment_test 1 555 1")
|
174
183
|
end
|
175
184
|
|
176
185
|
it "should report an increment with a set time and count" do
|
177
186
|
agent.increment('increment_test', 1, 555, 111)
|
178
187
|
wait
|
179
|
-
server.commands.last.
|
188
|
+
expect(server.commands.last).to eq("increment increment_test 1 555 111")
|
180
189
|
end
|
181
190
|
|
182
191
|
it "should discard data that overflows the buffer" do
|
@@ -185,12 +194,11 @@ shared_examples "Instrumental Agent" do
|
|
185
194
|
agent.increment('overflow_test', i + 1, 300)
|
186
195
|
end
|
187
196
|
wait
|
188
|
-
server.commands.
|
189
|
-
server.commands.
|
190
|
-
server.commands.
|
191
|
-
|
192
|
-
server.commands.
|
193
|
-
server.commands.should_not include("increment overflow_test 5 300 1")
|
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")
|
194
202
|
end
|
195
203
|
end
|
196
204
|
|
@@ -200,13 +208,13 @@ shared_examples "Instrumental Agent" do
|
|
200
208
|
5.times do |i|
|
201
209
|
agent.increment('overflow_test', i + 1, 300)
|
202
210
|
end
|
203
|
-
agent.instance_variable_get(:@queue).size.
|
211
|
+
expect(agent.instance_variable_get(:@queue).size).to eq(0)
|
204
212
|
wait # let the server receive the commands
|
205
|
-
server.commands.
|
206
|
-
server.commands.
|
207
|
-
server.commands.
|
208
|
-
server.commands.
|
209
|
-
server.commands.
|
213
|
+
expect(server.commands).to include("increment overflow_test 1 300 1")
|
214
|
+
expect(server.commands).to include("increment overflow_test 2 300 1")
|
215
|
+
expect(server.commands).to include("increment overflow_test 3 300 1")
|
216
|
+
expect(server.commands).to include("increment overflow_test 4 300 1")
|
217
|
+
expect(server.commands).to include("increment overflow_test 5 300 1")
|
210
218
|
end
|
211
219
|
end
|
212
220
|
|
@@ -219,38 +227,38 @@ shared_examples "Instrumental Agent" do
|
|
219
227
|
wait(1)
|
220
228
|
agent.increment('fork_reconnect_test', 1, 4) # triggers reconnect
|
221
229
|
wait(1)
|
222
|
-
server.connect_count.
|
223
|
-
|
224
|
-
server.commands.
|
225
|
-
server.commands.
|
230
|
+
expect(server.connect_count).to eq(2)
|
231
|
+
|
232
|
+
expect(server.commands).to include("increment fork_reconnect_test 1 2 1")
|
233
|
+
expect(server.commands).to include("increment fork_reconnect_test 1 3 1")
|
234
|
+
expect(server.commands).to include("increment fork_reconnect_test 1 4 1")
|
226
235
|
end
|
227
236
|
end
|
228
237
|
|
229
238
|
it "should never let an exception reach the user" do
|
230
|
-
agent.
|
231
|
-
agent.increment('throws_exception', 2).
|
232
|
-
wait
|
233
|
-
agent.gauge('throws_exception', 234).should be_nil
|
239
|
+
expect(agent).to receive(:send_command).twice { raise(Exception.new("Test Exception")) }
|
240
|
+
expect(agent.increment('throws_exception', 2)).to eq(nil)
|
234
241
|
wait
|
242
|
+
expect(agent.increment('throws_exception', 234)).to eq(nil)
|
235
243
|
end
|
236
244
|
|
237
245
|
it "should let exceptions in time bubble up" do
|
238
|
-
expect { agent.time('za') { raise "fail" } }.to
|
246
|
+
expect { agent.time('za') { raise "fail" } }.to raise_exception(StandardError)
|
239
247
|
end
|
240
248
|
|
241
249
|
it "should return nil if the user overflows the MAX_BUFFER" do
|
242
|
-
Queue.
|
250
|
+
allow_any_instance_of(Queue).to receive(:pop).and_return(nil)
|
243
251
|
1.upto(Instrumental::Agent::MAX_BUFFER) do
|
244
|
-
agent.increment("test").
|
252
|
+
expect(agent.increment("test")).to eq(1)
|
245
253
|
end
|
246
|
-
agent.increment("test").
|
254
|
+
expect(agent.increment("test")).to eq(nil)
|
247
255
|
end
|
248
256
|
|
249
257
|
it "should track invalid metrics" do
|
250
|
-
agent.logger.
|
258
|
+
expect(agent.logger).to receive(:warn).with(/%%/)
|
251
259
|
agent.increment(' %% .!#@$%^&*', 1, 1)
|
252
260
|
wait
|
253
|
-
server.commands.join("\n").
|
261
|
+
expect(server.commands.join("\n")).to include("increment agent.invalid_metric")
|
254
262
|
end
|
255
263
|
|
256
264
|
it "should allow reasonable metric names" do
|
@@ -259,14 +267,14 @@ shared_examples "Instrumental Agent" do
|
|
259
267
|
agent.increment('hello.world')
|
260
268
|
agent.increment('ThisIsATest.Of.The.Emergency.Broadcast.System.12345')
|
261
269
|
wait
|
262
|
-
server.commands.join("\n").
|
270
|
+
expect(server.commands.join("\n")).to_not include("increment agent.invalid_metric")
|
263
271
|
end
|
264
272
|
|
265
273
|
it "should track invalid values" do
|
266
|
-
agent.logger.
|
274
|
+
expect(agent.logger).to receive(:warn).with(/hello.*testington/)
|
267
275
|
agent.increment('testington', 'hello')
|
268
276
|
wait
|
269
|
-
server.commands.join("\n").
|
277
|
+
expect(server.commands.join("\n")).to include("increment agent.invalid_value")
|
270
278
|
end
|
271
279
|
|
272
280
|
it "should allow reasonable values" do
|
@@ -279,20 +287,20 @@ shared_examples "Instrumental Agent" do
|
|
279
287
|
agent.increment('a', 333.333)
|
280
288
|
agent.increment('a', Float::EPSILON)
|
281
289
|
wait
|
282
|
-
server.commands.join("\n").
|
290
|
+
expect(server.commands.join("\n")).to_not include("increment agent.invalid_value")
|
283
291
|
end
|
284
292
|
|
285
293
|
it "should send notices to the server" do
|
286
294
|
tm = Time.now
|
287
295
|
agent.notice("Test note", tm)
|
288
296
|
wait
|
289
|
-
server.commands.join("\n").
|
297
|
+
expect(server.commands.join("\n")).to include("notice #{tm.to_i} 0 Test note")
|
290
298
|
end
|
291
299
|
|
292
300
|
it "should prevent a note w/ newline characters from being sent to the server" do
|
293
|
-
agent.notice("Test note\n").
|
301
|
+
expect(agent.notice("Test note\n")).to eq(nil)
|
294
302
|
wait
|
295
|
-
server.commands.join("\n").
|
303
|
+
expect(server.commands.join("\n")).to_not include("notice Test note")
|
296
304
|
end
|
297
305
|
|
298
306
|
it "should allow outgoing metrics to be stopped" do
|
@@ -302,23 +310,23 @@ shared_examples "Instrumental Agent" do
|
|
302
310
|
wait
|
303
311
|
agent.increment("foo.baz", 1, tm)
|
304
312
|
wait
|
305
|
-
server.commands.join("\n").
|
306
|
-
server.commands.join("\n").
|
313
|
+
expect(server.commands.join("\n")).to include("increment foo.baz 1 #{tm.to_i}")
|
314
|
+
expect(server.commands.join("\n")).to_not include("increment foo.bar 1 #{tm.to_i}")
|
307
315
|
end
|
308
316
|
|
309
317
|
it "should allow flushing pending values to the server" do
|
310
318
|
1.upto(100) { agent.gauge('a', rand(50)) }
|
311
|
-
agent.instance_variable_get(:@queue).size.
|
319
|
+
expect(agent.instance_variable_get(:@queue).size).to be > 0
|
312
320
|
agent.flush
|
313
|
-
agent.instance_variable_get(:@queue).size.
|
321
|
+
expect(agent.instance_variable_get(:@queue).size).to eq(0)
|
314
322
|
wait
|
315
|
-
server.commands.grep(/^gauge a /).size.
|
323
|
+
expect(server.commands.grep(/^gauge a /).size).to eq(100)
|
316
324
|
end
|
317
325
|
|
318
326
|
it "should no op on an empty flush" do
|
319
327
|
agent.flush(true)
|
320
328
|
wait
|
321
|
-
server.commands.
|
329
|
+
expect(server.commands).to be_empty
|
322
330
|
end
|
323
331
|
end
|
324
332
|
|
@@ -330,9 +338,9 @@ shared_examples "Instrumental Agent" do
|
|
330
338
|
wait(1)
|
331
339
|
agent.increment('reconnect_test', 1, 5678) # triggers reconnect
|
332
340
|
wait(1)
|
333
|
-
server.connect_count.
|
341
|
+
expect(server.connect_count).to eq(2)
|
334
342
|
# Ensure the last command sent has been received after the reconnect attempt
|
335
|
-
server.commands.last.
|
343
|
+
expect(server.commands.last).to eq("increment reconnect_test 1 5678 1")
|
336
344
|
end
|
337
345
|
|
338
346
|
context 'not listening' do
|
@@ -343,13 +351,13 @@ shared_examples "Instrumental Agent" do
|
|
343
351
|
agent.increment('reconnect_test', 1, 1234)
|
344
352
|
wait
|
345
353
|
# The agent should not have sent the metric yet, the server is not responding
|
346
|
-
agent.queue.pop(true).
|
354
|
+
expect(agent.queue.pop(true)).to include("increment reconnect_test 1 1234 1\n")
|
347
355
|
end
|
348
356
|
|
349
357
|
it "should warn once when buffer is full" do
|
350
358
|
with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
|
351
359
|
wait
|
352
|
-
agent.logger.
|
360
|
+
expect(agent.logger).to receive(:warn).with(/Queue full/).once
|
353
361
|
|
354
362
|
agent.increment('buffer_full_warn_test', 1, 1234)
|
355
363
|
agent.increment('buffer_full_warn_test', 1, 1234)
|
@@ -361,13 +369,12 @@ shared_examples "Instrumental Agent" do
|
|
361
369
|
end
|
362
370
|
|
363
371
|
context 'bad address' do
|
364
|
-
let(:address) { "
|
372
|
+
let(:address) { "bad-address:9999" }
|
365
373
|
|
366
374
|
it "should not be running if it cannot connect" do
|
375
|
+
expect(Resolv).to receive(:getaddresses).with("bad-address").and_raise Resolv::ResolvError
|
367
376
|
agent.gauge('connection_test', 1, 1234)
|
368
|
-
|
369
|
-
# the address and refuse to start a worker thread
|
370
|
-
agent.should_not be_running
|
377
|
+
expect(agent.send(:running?)).to eq(false)
|
371
378
|
end
|
372
379
|
end
|
373
380
|
|
@@ -379,7 +386,7 @@ shared_examples "Instrumental Agent" do
|
|
379
386
|
agent.increment('reconnect_test', 1, 1234)
|
380
387
|
wait
|
381
388
|
# Since server hasn't responded to hello or authenticate, worker thread will not send data
|
382
|
-
agent.queue.pop(true).
|
389
|
+
expect(agent.queue.pop(true)).to include("increment reconnect_test 1 1234 1\n")
|
383
390
|
end
|
384
391
|
end
|
385
392
|
|
@@ -396,9 +403,8 @@ shared_examples "Instrumental Agent" do
|
|
396
403
|
wait
|
397
404
|
# The agent thread should have stopped running since the network write would
|
398
405
|
# have failed. The queue will still contain the metric that has yet to be sent
|
399
|
-
agent.
|
400
|
-
agent.queue.size.
|
401
|
-
|
406
|
+
expect(agent.send(:running?)).to eq(false)
|
407
|
+
expect(agent.queue.size).to eq(1)
|
402
408
|
end
|
403
409
|
|
404
410
|
it "should restart the worker thread after hanging it up during an unreachable host event" do
|
@@ -413,8 +419,8 @@ shared_examples "Instrumental Agent" do
|
|
413
419
|
wait
|
414
420
|
# The agent thread should have stopped running since the network write would
|
415
421
|
# have failed. The queue will still contain the metric that has yet to be sent
|
416
|
-
agent.
|
417
|
-
agent.queue.size.
|
422
|
+
expect(agent.send(:running?)).to eq(false)
|
423
|
+
expect(agent.queue.size).to eq(1)
|
418
424
|
wait
|
419
425
|
# Start the server back up again
|
420
426
|
server.listen
|
@@ -423,8 +429,8 @@ shared_examples "Instrumental Agent" do
|
|
423
429
|
agent.gauge('connection_failure', 1, 1234)
|
424
430
|
wait
|
425
431
|
# The agent should now be running the background thread, and the queue should be empty
|
426
|
-
agent.
|
427
|
-
agent.queue.size.
|
432
|
+
expect(agent.send(:running?)).to eq(true)
|
433
|
+
expect(agent.queue.size).to eq(0)
|
428
434
|
end
|
429
435
|
|
430
436
|
end
|
@@ -438,7 +444,7 @@ shared_examples "Instrumental Agent" do
|
|
438
444
|
agent.increment('reconnect_test', 1, 1234)
|
439
445
|
wait
|
440
446
|
# Metrics should not have been sent since all authentication failed
|
441
|
-
agent.queue.pop(true).
|
447
|
+
expect(agent.queue.pop(true)).to include("increment reconnect_test 1 1234 1\n")
|
442
448
|
end
|
443
449
|
end
|
444
450
|
|
@@ -447,7 +453,7 @@ shared_examples "Instrumental Agent" do
|
|
447
453
|
if pid = fork { agent.increment('foo', 1, 1234) }
|
448
454
|
Process.wait(pid)
|
449
455
|
# The forked process should have flushed and waited on at_exit
|
450
|
-
server.commands.last.
|
456
|
+
expect(server.commands.last).to eq("increment foo 1 1234 1")
|
451
457
|
end
|
452
458
|
end
|
453
459
|
|
@@ -455,31 +461,31 @@ shared_examples "Instrumental Agent" do
|
|
455
461
|
if pid = fork { agent.increment('foo', 1, 1234); agent.cleanup; exit! }
|
456
462
|
Process.wait(pid)
|
457
463
|
# The forked process should have flushed and waited on at_exit since cleanup was called explicitly
|
458
|
-
server.commands.last.
|
464
|
+
expect(server.commands.last).to eq("increment foo 1 1234 1")
|
459
465
|
end
|
460
466
|
end
|
461
467
|
|
462
468
|
it "should not wait longer than EXIT_FLUSH_TIMEOUT seconds to exit a process" do
|
463
|
-
agent.
|
469
|
+
allow(agent).to receive(:open_socket) { |*args, &block| sleep(5) && block.call }
|
464
470
|
with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
|
465
471
|
if (pid = fork { agent.increment('foo', 1) })
|
466
472
|
tm = Time.now.to_f
|
467
473
|
Process.wait(pid)
|
468
474
|
diff = Time.now.to_f - tm
|
469
|
-
diff.abs.
|
470
|
-
diff.abs.
|
475
|
+
expect(diff.abs).to be >= 3
|
476
|
+
expect(diff.abs).to be < 5
|
471
477
|
end
|
472
478
|
end
|
473
479
|
end
|
474
480
|
|
475
481
|
it "should not wait to exit a process if there are no commands queued" do
|
476
|
-
agent.
|
482
|
+
allow(agent).to receive(:open_socket) { |*args, &block| sleep(5) && block.call }
|
477
483
|
with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
|
478
484
|
if (pid = fork { agent.increment('foo', 1); agent.queue.clear })
|
479
485
|
tm = Time.now.to_f
|
480
486
|
Process.wait(pid)
|
481
487
|
diff = Time.now.to_f - tm
|
482
|
-
diff.
|
488
|
+
expect(diff).to be < 1
|
483
489
|
end
|
484
490
|
end
|
485
491
|
end
|
@@ -488,7 +494,7 @@ shared_examples "Instrumental Agent" do
|
|
488
494
|
it "should not wait longer than EXIT_FLUSH_TIMEOUT to attempt flushing the socket when disconnecting" do
|
489
495
|
agent.increment('foo', 1)
|
490
496
|
wait
|
491
|
-
agent.
|
497
|
+
expect(agent).to receive(:flush_socket) do
|
492
498
|
r, w = IO.pipe
|
493
499
|
Thread.new do
|
494
500
|
IO.select([r]) # mimic an endless blocking select poll
|
@@ -498,7 +504,7 @@ shared_examples "Instrumental Agent" do
|
|
498
504
|
tm = Time.now.to_f
|
499
505
|
agent.cleanup
|
500
506
|
diff = Time.now.to_f - tm
|
501
|
-
diff.
|
507
|
+
expect(diff).to be <= 3
|
502
508
|
end
|
503
509
|
end
|
504
510
|
|
@@ -507,13 +513,13 @@ shared_examples "Instrumental Agent" do
|
|
507
513
|
'Instrumental::Agent::RESOLUTION_WAIT' => 2,
|
508
514
|
'Instrumental::Agent::RESOLVE_TIMEOUT' => 0.1) do
|
509
515
|
attempted_resolutions = 0
|
510
|
-
Resolv.
|
516
|
+
allow(Resolv).to receive(:getaddresses) { attempted_resolutions +=1 ; sleep 1 }
|
511
517
|
agent.gauge('test', 1)
|
512
|
-
attempted_resolutions.
|
513
|
-
agent.
|
518
|
+
expect(attempted_resolutions).to eq(1)
|
519
|
+
expect(agent.send(:running?)).to eq(false)
|
514
520
|
agent.gauge('test', 1)
|
515
|
-
attempted_resolutions.
|
516
|
-
agent.
|
521
|
+
expect(attempted_resolutions).to eq(1)
|
522
|
+
expect(agent.send(:running?)).to eq(false)
|
517
523
|
end
|
518
524
|
end
|
519
525
|
|
@@ -522,16 +528,16 @@ shared_examples "Instrumental Agent" do
|
|
522
528
|
'Instrumental::Agent::RESOLUTION_WAIT' => 2,
|
523
529
|
'Instrumental::Agent::RESOLVE_TIMEOUT' => 0.1) do
|
524
530
|
attempted_resolutions = 0
|
525
|
-
Resolv.
|
531
|
+
allow(Resolv).to receive(:getaddresses) { attempted_resolutions +=1 ; sleep 1 }
|
526
532
|
agent.gauge('test', 1)
|
527
|
-
attempted_resolutions.
|
528
|
-
agent.
|
533
|
+
expect(attempted_resolutions).to eq(1)
|
534
|
+
expect(agent.send(:running?)).to eq(false)
|
529
535
|
agent.gauge('test', 1)
|
530
|
-
attempted_resolutions.
|
531
|
-
agent.
|
536
|
+
expect(attempted_resolutions).to eq(1)
|
537
|
+
expect(agent.send(:running?)).to eq(false)
|
532
538
|
sleep 2
|
533
539
|
agent.gauge('test', 1)
|
534
|
-
attempted_resolutions.
|
540
|
+
expect(attempted_resolutions).to eq(2)
|
535
541
|
end
|
536
542
|
end
|
537
543
|
|
@@ -540,23 +546,23 @@ shared_examples "Instrumental Agent" do
|
|
540
546
|
attempted_opens = 0
|
541
547
|
open_sleep = 0
|
542
548
|
os = agent.method(:open_socket)
|
543
|
-
agent.
|
549
|
+
allow(agent).to receive(:open_socket) { |*args, &block| attempted_opens +=1 ; sleep(open_sleep) && os.call(*args) }
|
544
550
|
|
545
551
|
# Connect normally and start running worker loop
|
546
552
|
attempted_resolutions = 0
|
547
553
|
ga = Resolv.method(:getaddresses)
|
548
|
-
Resolv.
|
554
|
+
allow(Resolv).to receive(:getaddresses) { |*args, &block| attempted_resolutions +=1 ; ga.call(*args) }
|
549
555
|
agent.gauge('test', 1)
|
550
556
|
wait 2
|
551
|
-
attempted_resolutions.
|
552
|
-
attempted_opens.
|
553
|
-
agent.
|
557
|
+
expect(attempted_resolutions).to eq(1)
|
558
|
+
expect(attempted_opens).to eq(1)
|
559
|
+
expect(agent.send(:running?)).to eq(true)
|
554
560
|
|
555
561
|
# Setup a failure for the next command so we'll break out of the inner
|
556
562
|
# loop in run_worker_loop causing another call to open_socket
|
557
563
|
test_connection_fail = true
|
558
564
|
tc = agent.method(:test_connection)
|
559
|
-
agent.
|
565
|
+
allow(agent).to receive(:test_connection) { |*args, &block| test_connection_fail ? raise("fail") : tc.call(*args) }
|
560
566
|
|
561
567
|
# Setup a timeout failure in open_socket for the next command
|
562
568
|
open_sleep = 5
|
@@ -566,10 +572,10 @@ shared_examples "Instrumental Agent" do
|
|
566
572
|
agent.gauge('test', 1)
|
567
573
|
wait 5
|
568
574
|
# On retry we attempt to open_socket, but this times out
|
569
|
-
attempted_opens.
|
575
|
+
expect(attempted_opens).to eq(2)
|
570
576
|
# We don't resolve again yet, we just disconnect
|
571
|
-
attempted_resolutions.
|
572
|
-
agent.
|
577
|
+
expect(attempted_resolutions).to eq(1)
|
578
|
+
expect(agent.send(:running?)).to eq(false)
|
573
579
|
|
574
580
|
# Make test_connection succeed on the next command
|
575
581
|
test_connection_fail = false
|
@@ -580,9 +586,9 @@ shared_examples "Instrumental Agent" do
|
|
580
586
|
# The reconnect causes a new DNS resolution
|
581
587
|
agent.gauge('test', 1)
|
582
588
|
wait 5
|
583
|
-
attempted_resolutions.
|
584
|
-
attempted_opens.
|
585
|
-
agent.
|
589
|
+
expect(attempted_resolutions).to eq(2)
|
590
|
+
expect(attempted_opens).to eq(3)
|
591
|
+
expect(agent.send(:running?)).to eq(true)
|
586
592
|
end
|
587
593
|
end
|
588
594
|
end
|
@@ -596,11 +602,11 @@ shared_examples "Instrumental Agent" do
|
|
596
602
|
agent.increment('overflow_test', i + 1, 300)
|
597
603
|
end
|
598
604
|
wait # let the server receive the commands
|
599
|
-
server.commands.
|
600
|
-
server.commands.
|
601
|
-
server.commands.
|
602
|
-
server.commands.
|
603
|
-
server.commands.
|
605
|
+
expect(server.commands).to include("increment overflow_test 1 300 1")
|
606
|
+
expect(server.commands).to include("increment overflow_test 2 300 1")
|
607
|
+
expect(server.commands).to include("increment overflow_test 3 300 1")
|
608
|
+
expect(server.commands).to include("increment overflow_test 4 300 1")
|
609
|
+
expect(server.commands).to include("increment overflow_test 5 300 1")
|
604
610
|
end
|
605
611
|
end
|
606
612
|
end
|
@@ -619,16 +625,16 @@ describe "Secure without cert verify" do
|
|
619
625
|
it_behaves_like "Instrumental Agent"
|
620
626
|
|
621
627
|
it "should be disabled if the system does not allow secure connections but the user specifically requested secure" do
|
622
|
-
Instrumental::Agent.
|
628
|
+
allow_any_instance_of(Instrumental::Agent).to receive(:allows_secure?).and_return(false)
|
623
629
|
agent = Instrumental::Agent.new('test-token', :enabled => true, :secure => true)
|
624
|
-
agent.secure.
|
625
|
-
agent.enabled.
|
630
|
+
expect(agent.secure).to eq(false)
|
631
|
+
expect(agent.enabled).to eq(false)
|
626
632
|
end
|
627
633
|
|
628
|
-
it "should be fallback to insecure if the system does not allow secure connections but the user did not specifically request secure" do
|
629
|
-
Instrumental::Agent.
|
634
|
+
it "should be fallback to insecure if the system does not allow secure connections but the user did not specifically request secure" do
|
635
|
+
allow_any_instance_of(Instrumental::Agent).to receive(:allows_secure?) { false }
|
630
636
|
agent = Instrumental::Agent.new('test-token', :enabled => true)
|
631
|
-
agent.secure.
|
632
|
-
agent.enabled.
|
637
|
+
expect(agent.secure).to eq(false)
|
638
|
+
expect(agent.enabled).to eq(true)
|
633
639
|
end
|
634
640
|
end
|
data/spec/spec_helper.rb
CHANGED
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: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Elijah Miller
|
@@ -11,8 +11,22 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2016-05-13 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: pry
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :development
|
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: rake
|
18
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -33,14 +47,14 @@ dependencies:
|
|
33
47
|
requirements:
|
34
48
|
- - "~>"
|
35
49
|
- !ruby/object:Gem::Version
|
36
|
-
version: '
|
50
|
+
version: '3.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
|
-
version: '
|
57
|
+
version: '3.0'
|
44
58
|
- !ruby/object:Gem::Dependency
|
45
59
|
name: fuubar
|
46
60
|
requirement: !ruby/object:Gem::Requirement
|
@@ -55,7 +69,9 @@ dependencies:
|
|
55
69
|
- - ">="
|
56
70
|
- !ruby/object:Gem::Version
|
57
71
|
version: '0'
|
58
|
-
description:
|
72
|
+
description: This agent supports Instrumental custom metric monitoring for Ruby applications.
|
73
|
+
It provides high-data reliability at high scale, without ever blocking your process
|
74
|
+
or causing an exception.
|
59
75
|
email:
|
60
76
|
- support@instrumentalapp.com
|
61
77
|
executables: []
|
@@ -80,13 +96,15 @@ files:
|
|
80
96
|
- lib/instrumental/system_timer.rb
|
81
97
|
- lib/instrumental/version.rb
|
82
98
|
- lib/instrumental_agent.rb
|
99
|
+
- script/setup
|
100
|
+
- script/test
|
83
101
|
- spec/agent_spec.rb
|
84
102
|
- spec/spec_helper.rb
|
85
103
|
- spec/test.crt
|
86
104
|
- spec/test.csr
|
87
105
|
- spec/test.key
|
88
106
|
- spec/test_server.rb
|
89
|
-
homepage: http://github.com/
|
107
|
+
homepage: http://github.com/instrumental/instrumental_agent-ruby
|
90
108
|
licenses:
|
91
109
|
- MIT
|
92
110
|
metadata: {}
|
@@ -106,10 +124,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
124
|
version: '0'
|
107
125
|
requirements: []
|
108
126
|
rubyforge_project:
|
109
|
-
rubygems_version: 2.
|
127
|
+
rubygems_version: 2.5.1
|
110
128
|
signing_key:
|
111
129
|
specification_version: 4
|
112
|
-
summary:
|
130
|
+
summary: Custom metric monitoring for Ruby applications via Instrumental
|
113
131
|
test_files:
|
114
132
|
- spec/agent_spec.rb
|
115
133
|
- spec/spec_helper.rb
|