appsignal 0.8.5 → 0.8.6.beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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