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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 32d9f3dab94080e4c8194ab8c71e0ddf4d83bb8a
4
- data.tar.gz: fa78452258ca2f450d5a6da975a5c1d0a114902a
3
+ metadata.gz: a89c1b56985adcb54ca4ece8434cdcf03dc4d6e9
4
+ data.tar.gz: 54781c1980d41308b91e2dab943ce1173a344caa
5
5
  SHA512:
6
- metadata.gz: 5174c7be573fab85d465dc2a6db07d35a62b7bcae160668fc4182da5fefc795930911392c556fa71af0a210723a5534c8548305f2de74c1ccbaae40d535ae448
7
- data.tar.gz: 69d4ad769e79161afa6d0c915c9a2b21835a010fc5452a975cfa438484f61906e4474e366c845ce1f46443b3effeba6b737c2f738dfaadfad9a7441604fc05f6
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
@@ -1,9 +1,8 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 1.8.7
5
- - 1.9.2
6
- - 1.9.3
7
- - 2.0.0
8
- - jruby-18mode
9
- - jruby-19mode
4
+ - 2.0.0-p648
5
+ - 2.1.5
6
+ - 2.2.3
7
+ - 2.3.0
8
+ - 2.4.0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ### 2.0.0.alpha [August 18, 2017]
2
+ * Add automatic tracking of common application metrics
3
+
1
4
  ### 1.0.1 [July 12, 2016]
2
5
  * Make agent initialization threadsafe
3
6
 
data/Gemfile CHANGED
@@ -6,3 +6,6 @@ if RUBY_VERSION < "1.9" && !%w{jruby rbx}.include?(ruby_engine)
6
6
  # Built and installed via ext/mkrf_conf.rb
7
7
  gem 'system_timer', '~> 1.2'
8
8
  end
9
+
10
+ # fixes 2.3.0 ffi bundle error
11
+ gem 'ffi', '~> 1.0.11'
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 key, found in the Docs section.
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('YOUR_API_KEY', :enabled => Rails.env.production?)
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 Stats
62
+ ## Server Metrics
63
63
 
64
- Want some general server stats (load, memory, etc.)? Check out the [instrumental_tools](https://github.com/instrumental/instrumental_tools) gem.
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
 
@@ -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
@@ -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)
@@ -1,3 +1,3 @@
1
1
  module Instrumental
2
- VERSION = "1.0.1"
2
+ VERSION = "2.0.0.alpha"
3
3
  end
data/script/setup CHANGED
@@ -2,7 +2,25 @@
2
2
  set -e
3
3
  cd "$(dirname "$0")/.."
4
4
 
5
- type rbenv || $(brew install rbenv; brew install ruby-build)
6
- rbenv which ruby || $(brew upgrade ruby-build || true; rbenv install)
7
- gem list -i bundler || gem install bundler
8
- bundle install
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
- script/setup
6
- bundle exec rspec
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
- sleep n # FIXME: hack
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(:agent) { Instrumental::Agent.new(token, :collector => address, :synchronous => synchronous, :enabled => enabled, :secure => secure?, :verify_cert => verify_cert?) }
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
- expect(server.connect_count).to eq(0)
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
- expect(server.commands).to be_empty
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
- expect(server.commands).to be_empty
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
- expect(server.commands).to be_empty
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
- expect(server.connect_count).to eq(1)
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
- expect(server.commands[0]).to match(/hello .*/)
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
- expect(server.commands[1]).to eq("authenticate test_token")
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
- expect(server.commands.last).to eq("gauge gauge_test 123 #{now.to_i} 1")
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
- expect(server.commands.last).to match(/gauge time_value_test .* #{now.to_i}/)
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
- expect(server.commands.last).to match(/gauge time_value_test 1000/)
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
- expect(server.commands.last).to eq("gauge gauge_test 123 555 1")
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
- expect(server.commands.last).to eq("gauge gauge_test 123 555 111")
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
- expect(server.commands.last).to eq("increment increment_test 1 #{now.to_i} 1")
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
- expect(server.commands.last).to eq("increment increment_test 2 #{now.to_i} 1")
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
- expect(server.commands.last).to eq("increment increment_test 1 555 1")
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
- expect(server.commands.last).to eq("increment increment_test 1 555 111")
217
+ wait do
218
+ expect(server.commands.last).to eq("increment increment_test 1 555 111")
219
+ end
189
220
  end
190
221
 
191
- it "should discard data that overflows the buffer" do
192
- with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
193
- 5.times do |i|
194
- agent.increment('overflow_test', i + 1, 300)
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
- expect(agent.increment('throws_exception', 234)).to eq(nil)
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
- expect(server.commands.join("\n")).to include("increment agent.invalid_metric")
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
- expect(server.commands.join("\n")).to_not include("increment agent.invalid_metric")
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
- expect(server.commands.join("\n")).to include("increment agent.invalid_value")
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
- expect(server.commands.join("\n")).to_not include("increment agent.invalid_value")
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
- expect(server.commands.join("\n")).to include("notice #{tm.to_i} 0 Test note")
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("Test note\n")).to eq(nil)
321
- wait
322
- expect(server.commands.join("\n")).to_not include("notice Test note")
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
- agent.increment("foo.baz", 1, tm)
388
+ server.listen
389
+
331
390
  wait
332
- expect(server.commands.join("\n")).to include("increment foo.baz 1 #{tm.to_i}")
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
- expect(server.commands.grep(/^gauge a /).size).to eq(100)
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
- expect(server.commands).to be_empty
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("reconnect_test", 1, 1234)
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(1)
360
- expect(server.connect_count).to eq(2)
361
- # Ensure the last command sent has been received after the reconnect attempt
362
- expect(server.commands.last).to eq("increment reconnect_test 1 5678 1")
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
- expect(agent.send(:running?)).to eq(false)
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
- expect(agent.send(:running?)).to eq(false)
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
- expect(agent.send(:running?)).to eq(true)
452
- expect(agent.queue.size).to eq(0)
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
- IO.select([r]) # mimic an endless blocking select poll
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 'instrumental_agent'
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 unless @stopping
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
- @main_thread.kill if @main_thread
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
- puts e.message
134
- puts e.backtrace.join("\n")
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: 1.0.1
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: 2016-07-12 00:00:00.000000000 Z
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
- - ".gitignore"
82
- - ".rspec"
83
- - ".travis.yml"
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: '0'
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: '0'
153
+ version: 1.3.1
125
154
  requirements: []
126
155
  rubyforge_project:
127
- rubygems_version: 2.5.1
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