appsignal 0.8.5 → 0.8.6.beta.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- OTNlMjI4MGEyYWZkNTRkZjg0Yjk2ZWQwOTA5NmIyNWVkYTU1OTZkOQ==
4
+ YTFjZWJlMmZlZDk5YzFiZGZmN2E4ODZiYWNlNDM4ZWM0MGUzODBjMQ==
5
5
  data.tar.gz: !binary |-
6
- N2VlMDdmMjcwMzA5MTUxZTM0ODk1YjA2NGVlNmUzZTM4ZTI3NDljYw==
6
+ ZDBlMDAxZjMwNDdiN2VlMjBiM2Y5ZTUxMjQ0ZjFhY2M0ZGQ2MWEwMg==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZDRiZTM2NjU1NjY5OGZkZDY2NDExYjEwZTU1YmRmMTQ2YTFhZmM0YWJhMDQ0
10
- ZmZlNWEzMTc0ZTc4N2M2ZTM0ZTU5NmQ3MjU0ZDNlM2I5MTE1ZmVmZWRhZmIy
11
- YTI2MjhhZTIyZDdmN2U4OWFmMjMxMmU2ZDY4MTA4OGZjNDdjMDA=
9
+ NTM0NGE3Y2JlNTQ1NzI3YTQ3MzIwYjRmYTkxZmVmZWFiY2YxODMyYmNjMmYw
10
+ OTM1YmYzODQzMGJiNWNhZWQ0OTMyZTRkNDkwOWNjNTRkMjczNGNkYWNmMTY4
11
+ NWUyYjU4YWNkMzU1NjYwOGNiNjA1OGM5YmZjN2ViMzM4ODQyMTM=
12
12
  data.tar.gz: !binary |-
13
- MWIxN2NlNjc3ZmEzZGQ1ZTEyMmMzYzdlYTUxMGI2MTc4NGFjN2Q2Mzk1YTE0
14
- MmYxYWMxNzlhMWY3MDdiNTkzODQxZjJiMTAyMThlYjA2NWE2YzZlNGFhMzMz
15
- NTEwMmVjMjg2ZWI5ZGM4Njc4OGNlOGY1ZjY1N2JhNThkNWE3MTg=
13
+ MzE1MDg5ODE2YjJmMDkzMGNjY2I0NTk1ODhhNzBkYzZjMzZkZmE2NzZmMGM1
14
+ NTgyZTZhNjk4MTZjOTA3NzU1NGI1MmMzMDBkMzUwNjIxOThjYTBkNjhhZTUy
15
+ YTA3NGE2OTAyN2FjYTcxNDgxZjRmMTA0MzhiODhkOWFlMTE1NzA=
@@ -1,3 +1,6 @@
1
+ # 0.8.6
2
+ * Resque support
3
+
1
4
  # 0.8.5
2
5
  * Don't require revision in CLI notify_of_deploy
3
6
 
@@ -12,6 +12,7 @@ module Appsignal
12
12
  require 'appsignal/integrations/passenger'
13
13
  require 'appsignal/integrations/unicorn'
14
14
  require 'appsignal/integrations/sidekiq'
15
+ require 'appsignal/integrations/resque'
15
16
  end
16
17
 
17
18
  def extensions
@@ -142,5 +143,6 @@ require 'appsignal/transaction'
142
143
  require 'appsignal/transaction/formatter'
143
144
  require 'appsignal/transaction/params_sanitizer'
144
145
  require 'appsignal/transmitter'
146
+ require 'appsignal/pipe'
145
147
  require 'appsignal/version'
146
148
  require 'appsignal/integrations/rails'
@@ -25,6 +25,7 @@ module Appsignal
25
25
  Appsignal.logger.debug('Starting agent thread')
26
26
  @thread = Thread.new do
27
27
  loop do
28
+ Appsignal.logger.debug aggregator.queue.inspect
28
29
  send_queue if aggregator.has_transactions?
29
30
  Appsignal.logger.debug("Sleeping #{sleep_time}")
30
31
  sleep(sleep_time)
@@ -33,11 +34,15 @@ module Appsignal
33
34
  end
34
35
 
35
36
  def restart_thread
37
+ stop_thread
38
+ start_thread
39
+ end
40
+
41
+ def stop_thread
36
42
  if @thread && @thread.alive?
37
43
  Appsignal.logger.debug 'Killing agent thread'
38
44
  Thread.kill(@thread)
39
45
  end
40
- start_thread
41
46
  end
42
47
 
43
48
  def subscribe
@@ -0,0 +1,44 @@
1
+ if defined?(::Resque)
2
+ Appsignal.logger.info('Loading Resque integration')
3
+
4
+ module Appsignal
5
+ module Integrations
6
+ module ResquePlugin
7
+
8
+ def around_perform_resque_plugin(*args)
9
+ Appsignal::Transaction.create(SecureRandom.uuid, ENV.to_hash)
10
+ ActiveSupport::Notifications.instrument(
11
+ 'perform_job.resque',
12
+ :class => self.to_s,
13
+ :method => 'perform'
14
+ ) do
15
+ yield
16
+ end
17
+ rescue Exception => exception
18
+ unless Appsignal.is_ignored_exception?(exception)
19
+ Appsignal::Transaction.current.add_exception(exception)
20
+ end
21
+ raise exception
22
+ ensure
23
+ Appsignal::Transaction.current.complete!
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+
30
+ # Create a pipe for the workers to write to
31
+ Resque.before_first_fork do
32
+ Appsignal::Pipe.init
33
+ end
34
+
35
+ # In the fork, stop the normal agent startup
36
+ # and stop listening to the pipe (we'll only use it for writing)
37
+ Resque.after_fork do |job|
38
+ Appsignal.agent.stop_thread
39
+ Appsignal::Pipe.current.stop_listening!
40
+ end
41
+
42
+ # Extend the default job class with AppSignal instrumentation
43
+ Resque::Job.send(:extend, Appsignal::Integrations::ResquePlugin)
44
+ end
@@ -0,0 +1,44 @@
1
+ module Appsignal
2
+ class Pipe
3
+ attr_reader :reader, :writer, :listener
4
+
5
+ def initialize
6
+ Appsignal.logger.debug "Initializing pipe in #{$$}"
7
+ @reader, @writer = IO.pipe
8
+ @listener = Thread.new do
9
+ loop do
10
+ Appsignal.agent.enqueue(Marshal::load(@reader))
11
+ end
12
+ end
13
+ @listening = true
14
+ end
15
+
16
+ def write(transaction)
17
+ Marshal::dump(transaction, @writer)
18
+ rescue IOError
19
+ Appsignal.logger.debug "Broken pipe in #{$$}"
20
+ Appsignal.agent.shutdown
21
+ end
22
+
23
+ def stop_listening!
24
+ Thread.kill(@listener)
25
+ @reader.close unless @reader.closed?
26
+ @listening = false
27
+ end
28
+
29
+ def listening?
30
+ !! @listening
31
+ end
32
+
33
+ class << self
34
+ def init
35
+ Thread.current[:appsignal_pipe] = Appsignal::Pipe.new
36
+ end
37
+
38
+ def current
39
+ Thread.current[:appsignal_pipe]
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -1,4 +1,4 @@
1
- module Appsignal
1
+ module Appsignal
2
2
  class Transaction
3
3
  # Based on what Rails uses + some variables we'd like to show
4
4
  ENV_METHODS = %w(CONTENT_LENGTH AUTH_TYPE GATEWAY_INTERFACE
@@ -117,7 +117,11 @@ module Appsignal
117
117
  Thread.current[:appsignal_transaction_id] = nil
118
118
  current_transaction = Appsignal.transactions.delete(@request_id)
119
119
  if process_action_event || exception?
120
- Appsignal.enqueue(current_transaction)
120
+ if Appsignal::Pipe.current
121
+ Appsignal::Pipe.current.write(self)
122
+ else
123
+ Appsignal.enqueue(current_transaction)
124
+ end
121
125
  else
122
126
  Appsignal.logger.debug("No process_action_event or exception: #{@request_id}")
123
127
  end
@@ -1,3 +1,3 @@
1
1
  module Appsignal
2
- VERSION = '0.8.5'
2
+ VERSION = '0.8.6.beta.0'
3
3
  end
@@ -41,14 +41,24 @@ describe Appsignal::Agent do
41
41
  end
42
42
 
43
43
  describe "#restart_thread" do
44
+
45
+ it "should stop thread" do
46
+ subject.should_receive(:stop_thread)
47
+ end
48
+
49
+ it "should start a thread" do
50
+ subject.should_receive(:start_thread)
51
+ end
52
+
53
+ after { subject.restart_thread }
54
+ end
55
+
56
+ describe "#stop_thread" do
44
57
  context "if there is no thread" do
45
58
  before { subject.thread = nil }
46
59
 
47
- it "should start a thread" do
48
- subject.restart_thread
49
-
50
- subject.thread.should be_a(Thread)
51
- subject.thread.should be_alive
60
+ it "should not do anything" do
61
+ Thread.should_not_receive(:kill)
52
62
  end
53
63
  end
54
64
 
@@ -59,28 +69,17 @@ describe Appsignal::Agent do
59
69
  end
60
70
 
61
71
  it "should start a thread" do
62
- subject.restart_thread
63
-
64
- subject.thread.should be_a(Thread)
65
- subject.thread.should be_alive
72
+ Thread.should_not_receive(:kill)
66
73
  end
67
74
  end
68
75
 
69
76
  context "if there is an active thread" do
70
- it "should kill the current thread and start a new one" do
71
- previous_thread = subject.thread
72
- previous_thread.should be_alive
73
-
74
- subject.restart_thread
75
-
76
- subject.thread.should be_a(Thread)
77
- subject.thread.should be_alive
78
- subject.thread.should_not == previous_thread
79
-
80
- sleep 0.1 # We need to wait for the thread to exit
81
- previous_thread.should_not be_alive
77
+ it "should kill the current thread " do
78
+ Thread.should_receive(:kill)
82
79
  end
83
80
  end
81
+
82
+ after { subject.stop_thread }
84
83
  end
85
84
 
86
85
  describe "#subscribe" do
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ describe "ResquePlugin" do
4
+ let(:file) { File.expand_path('lib/appsignal/integrations/resque.rb') }
5
+
6
+ context "with resque" do
7
+ before do
8
+ module Resque
9
+
10
+ def self.before_first_fork
11
+ end
12
+
13
+ def self.after_fork
14
+ end
15
+
16
+ class Job
17
+ end
18
+
19
+ class TestError < StandardError
20
+ end
21
+ end
22
+
23
+ load file
24
+ start_agent
25
+ end
26
+
27
+ describe :around_perform_resque_plugin do
28
+ let(:transaction) { Appsignal::Transaction.new(1, {}) }
29
+ let(:job) { Resque::Job }
30
+ let(:invoked_job) { nil }
31
+ before do
32
+ transaction.stub(:complete! => true)
33
+ Appsignal::Transaction.stub(:current => transaction)
34
+ end
35
+
36
+ context "without exception" do
37
+ it "should create a new transaction" do
38
+ Appsignal::Transaction.should_receive(:create).and_return(transaction)
39
+ end
40
+
41
+ it "should instrument with correct params" do
42
+ ActiveSupport::Notifications.should_receive(:instrument).with(
43
+ 'perform_job.resque',
44
+ :class => 'Resque::Job',
45
+ :method => 'perform'
46
+ )
47
+ end
48
+
49
+ it "should close the transaction" do
50
+ transaction.should_receive(:complete!)
51
+ end
52
+
53
+ after { job.around_perform_resque_plugin { invoked_job } }
54
+ end
55
+
56
+ context "with exception" do
57
+ it "should set the exception" do
58
+ transaction.should_receive(:add_exception)
59
+ end
60
+
61
+ after do
62
+ begin
63
+ job.around_perform_resque_plugin { raise(Resque::TestError.new('the roof')) }
64
+ rescue Resque::TestError
65
+ # Do nothing
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ context "without resque" do
73
+ before(:all) { Object.send(:remove_const, :Resque) }
74
+
75
+ specify { expect { ::Resque }.to raise_error(NameError) }
76
+ specify { expect { load file }.to_not raise_error }
77
+ end
78
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe Appsignal::Pipe do
4
+ before :all do
5
+ Appsignal::Pipe.init
6
+ end
7
+ let(:agent) { double }
8
+
9
+ subject { Appsignal::Pipe.current }
10
+
11
+ its(:reader) { should be_instance_of(IO) }
12
+ its(:writer) { should be_instance_of(IO) }
13
+ its(:listener) { should be_instance_of(Thread) }
14
+ its(:listening?) { should be_true }
15
+
16
+ describe "#write" do
17
+
18
+ context "with a regular request" do
19
+ let(:transaction) { regular_transaction }
20
+
21
+ it "should dump" do
22
+ Marshal.should_receive(:dump)
23
+ end
24
+ end
25
+
26
+ context "when the pipe is closed" do
27
+ let(:transaction) { regular_transaction }
28
+ before { Appsignal.stub(:agent => agent) }
29
+
30
+ it "should shutdown" do
31
+ Appsignal::Pipe.current.writer.close
32
+ agent.should_receive(:shutdown)
33
+ end
34
+ end
35
+
36
+ after { Appsignal::Pipe.current.write(transaction) }
37
+ end
38
+
39
+ describe "#stop_listening!" do
40
+ before do
41
+ subject.stop_listening!
42
+ sleep 0.1
43
+ end
44
+
45
+ it "should have closed the reader" do
46
+ subject.reader.closed?.should be_true
47
+ end
48
+
49
+ it "should have killed the listener thread" do
50
+ subject.listener.alive?.should be_false
51
+ end
52
+
53
+ it "should not crash when called twice" do
54
+ expect { subject.stop_listening! }.not_to raise_error
55
+ end
56
+
57
+ it "should know it's not listening anymore" do
58
+ subject.listening?.should be_false
59
+ end
60
+ end
61
+ end
@@ -282,7 +282,10 @@ describe Appsignal::Transaction do
282
282
 
283
283
  describe '#complete!' do
284
284
  let(:event) { double(:event) }
285
- before { transaction.set_process_action_event(notification_event) }
285
+ before do
286
+ Appsignal::Pipe.stub(:current => nil)
287
+ transaction.set_process_action_event(notification_event)
288
+ end
286
289
 
287
290
  it 'should remove transaction from the list' do
288
291
  expect { transaction.complete! }.
@@ -326,6 +329,17 @@ describe Appsignal::Transaction do
326
329
  Thread.current[:appsignal_transaction_id].should be_nil
327
330
  end
328
331
  end
332
+
333
+ context 'when using pipes' do
334
+ let(:pipe) { double }
335
+ before { Appsignal::Pipe.stub(:current => pipe) }
336
+
337
+ it "should send itself trough the pipe" do
338
+ pipe.should_receive(:write).with(transaction)
339
+ end
340
+
341
+ after { transaction.complete! }
342
+ end
329
343
  end
330
344
 
331
345
  describe "#set_background_queue_start" do
@@ -252,8 +252,10 @@ describe Appsignal do
252
252
  end
253
253
 
254
254
  describe ".send_exception" do
255
+ before { Appsignal::Pipe.stub(:current => false) }
256
+
255
257
  it "should send the exception to AppSignal" do
256
- agent = double
258
+ agent = double(:shutdown => true)
257
259
  Appsignal.stub(:agent).and_return(agent)
258
260
  agent.should_receive(:send_queue)
259
261
  agent.should_receive(:enqueue).with(kind_of(Appsignal::Transaction))
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appsignal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.5
4
+ version: 0.8.6.beta.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Beekman
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2014-01-17 00:00:00.000000000 Z
15
+ date: 2014-02-14 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: activesupport
@@ -169,10 +169,12 @@ files:
169
169
  - lib/appsignal/integrations/delayed_job.rb
170
170
  - lib/appsignal/integrations/passenger.rb
171
171
  - lib/appsignal/integrations/rails.rb
172
+ - lib/appsignal/integrations/resque.rb
172
173
  - lib/appsignal/integrations/sidekiq.rb
173
174
  - lib/appsignal/integrations/sinatra.rb
174
175
  - lib/appsignal/integrations/unicorn.rb
175
176
  - lib/appsignal/marker.rb
177
+ - lib/appsignal/pipe.rb
176
178
  - lib/appsignal/rack/instrumentation.rb
177
179
  - lib/appsignal/rack/listener.rb
178
180
  - lib/appsignal/transaction.rb
@@ -198,10 +200,12 @@ files:
198
200
  - spec/lib/appsignal/integrations/delayed_job_spec.rb
199
201
  - spec/lib/appsignal/integrations/passenger_spec.rb
200
202
  - spec/lib/appsignal/integrations/rails_spec.rb
203
+ - spec/lib/appsignal/integrations/resque_spec.rb
201
204
  - spec/lib/appsignal/integrations/sidekiq_spec.rb
202
205
  - spec/lib/appsignal/integrations/sinatra_spec.rb
203
206
  - spec/lib/appsignal/integrations/unicorn_spec.rb
204
207
  - spec/lib/appsignal/marker_spec.rb
208
+ - spec/lib/appsignal/pipe_spec.rb
205
209
  - spec/lib/appsignal/rack/instrumentation_spec.rb
206
210
  - spec/lib/appsignal/rack/listener_spec.rb
207
211
  - spec/lib/appsignal/transaction/formatter_spec.rb
@@ -237,9 +241,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
237
241
  version: 1.9.3
238
242
  required_rubygems_version: !ruby/object:Gem::Requirement
239
243
  requirements:
240
- - - ! '>='
244
+ - - ! '>'
241
245
  - !ruby/object:Gem::Version
242
- version: '0'
246
+ version: 1.3.1
243
247
  requirements: []
244
248
  rubyforge_project:
245
249
  rubygems_version: 2.0.3
@@ -261,10 +265,12 @@ test_files:
261
265
  - spec/lib/appsignal/integrations/delayed_job_spec.rb
262
266
  - spec/lib/appsignal/integrations/passenger_spec.rb
263
267
  - spec/lib/appsignal/integrations/rails_spec.rb
268
+ - spec/lib/appsignal/integrations/resque_spec.rb
264
269
  - spec/lib/appsignal/integrations/sidekiq_spec.rb
265
270
  - spec/lib/appsignal/integrations/sinatra_spec.rb
266
271
  - spec/lib/appsignal/integrations/unicorn_spec.rb
267
272
  - spec/lib/appsignal/marker_spec.rb
273
+ - spec/lib/appsignal/pipe_spec.rb
268
274
  - spec/lib/appsignal/rack/instrumentation_spec.rb
269
275
  - spec/lib/appsignal/rack/listener_spec.rb
270
276
  - spec/lib/appsignal/transaction/formatter_spec.rb