appsignal 0.12.rc.7 → 0.12.rc.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/ext/agent.yml +7 -7
  3. data/lib/appsignal.rb +3 -15
  4. data/lib/appsignal/hooks.rb +61 -0
  5. data/lib/appsignal/hooks/celluloid.rb +29 -0
  6. data/lib/appsignal/hooks/delayed_job.rb +18 -0
  7. data/lib/appsignal/hooks/net_http.rb +31 -0
  8. data/lib/appsignal/hooks/passenger.rb +21 -0
  9. data/lib/appsignal/hooks/puma.rb +20 -0
  10. data/lib/appsignal/hooks/rake.rb +42 -0
  11. data/lib/appsignal/hooks/redis.rb +24 -0
  12. data/lib/appsignal/hooks/resque.rb +28 -0
  13. data/lib/appsignal/hooks/sequel.rb +38 -0
  14. data/lib/appsignal/hooks/sidekiq.rb +68 -0
  15. data/lib/appsignal/hooks/unicorn.rb +39 -0
  16. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +27 -29
  17. data/lib/appsignal/integrations/delayed_job_plugin.rb +39 -0
  18. data/lib/appsignal/integrations/railtie.rb +41 -0
  19. data/lib/appsignal/transaction.rb +9 -4
  20. data/lib/appsignal/version.rb +1 -1
  21. data/spec/lib/appsignal/{integrations/capistrano2_spec.rb → capistrano2_spec.rb} +1 -1
  22. data/spec/lib/appsignal/{integrations/capistrano3_spec.rb → capistrano3_spec.rb} +0 -0
  23. data/spec/lib/appsignal/{integrations → hooks}/celluloid_spec.rb +8 -11
  24. data/spec/lib/appsignal/{integrations → hooks}/delayed_job_spec.rb +7 -10
  25. data/spec/lib/appsignal/hooks/net_http_spec.rb +55 -0
  26. data/spec/lib/appsignal/hooks/passenger_spec.rb +24 -0
  27. data/spec/lib/appsignal/hooks/puma_spec.rb +50 -0
  28. data/spec/lib/appsignal/{integrations → hooks}/rake_spec.rb +4 -3
  29. data/spec/lib/appsignal/hooks/redis_spec.rb +59 -0
  30. data/spec/lib/appsignal/{integrations → hooks}/resque_spec.rb +5 -11
  31. data/spec/lib/appsignal/{instrumentations → hooks}/sequel_spec.rb +1 -3
  32. data/spec/lib/appsignal/{integrations → hooks}/sidekiq_spec.rb +20 -20
  33. data/spec/lib/appsignal/hooks/unicorn_spec.rb +46 -0
  34. data/spec/lib/appsignal/hooks_spec.rb +76 -0
  35. data/spec/lib/appsignal/integrations/{rails_spec.rb → railtie_spec.rb} +0 -0
  36. data/spec/lib/appsignal/transaction_spec.rb +3 -3
  37. data/spec/lib/appsignal_spec.rb +0 -33
  38. metadata +46 -42
  39. data/lib/appsignal/instrumentations/net_http.rb +0 -17
  40. data/lib/appsignal/instrumentations/redis.rb +0 -13
  41. data/lib/appsignal/instrumentations/sequel.rb +0 -31
  42. data/lib/appsignal/integrations/celluloid.rb +0 -19
  43. data/lib/appsignal/integrations/delayed_job.rb +0 -44
  44. data/lib/appsignal/integrations/passenger.rb +0 -11
  45. data/lib/appsignal/integrations/puma.rb +0 -10
  46. data/lib/appsignal/integrations/rails.rb +0 -43
  47. data/lib/appsignal/integrations/rake.rb +0 -30
  48. data/lib/appsignal/integrations/resque.rb +0 -22
  49. data/lib/appsignal/integrations/sidekiq.rb +0 -62
  50. data/lib/appsignal/integrations/unicorn.rb +0 -28
  51. data/spec/lib/appsignal/instrumentations/net_http_spec.rb +0 -40
  52. data/spec/lib/appsignal/instrumentations/redis_spec.rb +0 -45
  53. data/spec/lib/appsignal/integrations/passenger_spec.rb +0 -22
  54. data/spec/lib/appsignal/integrations/puma_spec.rb +0 -52
  55. data/spec/lib/appsignal/integrations/unicorn_spec.rb +0 -48
@@ -0,0 +1,39 @@
1
+ module Appsignal
2
+ class Hooks
3
+ class UnicornHook < Appsignal::Hooks::Hook
4
+ register :unicorn
5
+
6
+ def dependencies_present?
7
+ defined?(::Unicorn::HttpServer) &&
8
+ defined?(::Unicorn::Worker)
9
+ end
10
+
11
+ def install
12
+ # Make sure that appsignal is started and the last transaction
13
+ # in a worker gets flushed.
14
+ #
15
+ # We'd love to be able to hook this into Unicorn in a less
16
+ # intrusive way, but this is the best we can do given the
17
+ # options we have.
18
+
19
+ ::Unicorn::HttpServer.class_eval do
20
+ alias worker_loop_without_appsignal worker_loop
21
+
22
+ def worker_loop(worker)
23
+ Appsignal.forked
24
+ worker_loop_without_appsignal(worker)
25
+ end
26
+ end
27
+
28
+ ::Unicorn::Worker.class_eval do
29
+ alias close_without_appsignal close
30
+
31
+ def close
32
+ Appsignal.stop
33
+ close_without_appsignal
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,36 +1,34 @@
1
1
  module Appsignal
2
- module Integrations
3
- class Capistrano
4
- def self.tasks(config)
5
- config.load do
6
- after 'deploy', 'appsignal:deploy'
7
- after 'deploy:migrations', 'appsignal:deploy'
2
+ class Capistrano
3
+ def self.tasks(config)
4
+ config.load do
5
+ after 'deploy', 'appsignal:deploy'
6
+ after 'deploy:migrations', 'appsignal:deploy'
8
7
 
9
- namespace :appsignal do
10
- task :deploy do
11
- env = fetch(:rails_env, fetch(:rack_env, 'production'))
12
- user = ENV['USER'] || ENV['USERNAME']
13
- revision = fetch(:appsignal_revision, fetch(:current_revision))
8
+ namespace :appsignal do
9
+ task :deploy do
10
+ env = fetch(:rails_env, fetch(:rack_env, 'production'))
11
+ user = ENV['USER'] || ENV['USERNAME']
12
+ revision = fetch(:appsignal_revision, fetch(:current_revision))
14
13
 
15
- appsignal_config = Appsignal::Config.new(
16
- ENV['PWD'],
17
- env,
18
- fetch(:appsignal_config, {}),
19
- logger
20
- )
14
+ appsignal_config = Appsignal::Config.new(
15
+ ENV['PWD'],
16
+ env,
17
+ fetch(:appsignal_config, {}),
18
+ logger
19
+ )
21
20
 
22
- if appsignal_config && appsignal_config.active?
23
- marker_data = {
24
- :revision => revision,
25
- :user => user
26
- }
21
+ if appsignal_config && appsignal_config.active?
22
+ marker_data = {
23
+ :revision => revision,
24
+ :user => user
25
+ }
27
26
 
28
- marker = Marker.new(marker_data, appsignal_config, logger)
29
- if config.dry_run
30
- logger.info('Dry run: Deploy marker not actually sent.')
31
- else
32
- marker.transmit
33
- end
27
+ marker = Marker.new(marker_data, appsignal_config, logger)
28
+ if config.dry_run
29
+ logger.info('Dry run: Deploy marker not actually sent.')
30
+ else
31
+ marker.transmit
34
32
  end
35
33
  end
36
34
  end
@@ -41,5 +39,5 @@ module Appsignal
41
39
  end
42
40
 
43
41
  if ::Capistrano::Configuration.instance
44
- Appsignal::Integrations::Capistrano.tasks(::Capistrano::Configuration.instance)
42
+ Appsignal::Capistrano.tasks(::Capistrano::Configuration.instance)
45
43
  end
@@ -0,0 +1,39 @@
1
+ module Appsignal
2
+ class Hooks
3
+ class DelayedJobPlugin < ::Delayed::Plugin
4
+ callbacks do |lifecycle|
5
+ lifecycle.around(:invoke_job) do |job, &block|
6
+ invoke_with_instrumentation(job, block)
7
+ end
8
+
9
+ lifecycle.after(:loop) do |loop|
10
+ Appsignal.stop
11
+ end
12
+ end
13
+
14
+ def self.invoke_with_instrumentation(job, block)
15
+ class_and_method_name = if job.payload_object.respond_to?(:appsignal_name)
16
+ job.payload_object.appsignal_name
17
+ else
18
+ job.name
19
+ end
20
+ class_name, method_name = class_and_method_name.split('#')
21
+
22
+ Appsignal.monitor_transaction(
23
+ 'perform_job.delayed_job',
24
+ :class => class_name,
25
+ :method => method_name,
26
+ :metadata => {
27
+ :id => job.id,
28
+ :queue => job.queue,
29
+ :priority => job.priority || 0,
30
+ :attempts => job.attempts || 0
31
+ },
32
+ :queue_start => job.created_at
33
+ ) do
34
+ block.call(job)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,41 @@
1
+ Appsignal.logger.info("Loading Rails (#{Rails.version}) integration")
2
+
3
+ require 'appsignal/rack/rails_instrumentation'
4
+
5
+ module Appsignal
6
+ module Integrations
7
+ class Railtie < ::Rails::Railtie
8
+ initializer 'appsignal.configure_rails_initialization' do |app|
9
+ Appsignal::Integrations::Railtie.initialize_appsignal(app)
10
+ end
11
+
12
+ def self.initialize_appsignal(app)
13
+ # Load config
14
+ Appsignal.config = Appsignal::Config.new(
15
+ Rails.root,
16
+ ENV.fetch('APPSIGNAL_APP_ENV', Rails.env),
17
+ :name => Rails.application.class.parent_name,
18
+ :log_file_path => Rails.root.join('log/appsignal.log')
19
+ )
20
+
21
+ # Start logger
22
+ Appsignal.start_logger
23
+
24
+ app.middleware.insert_before(
25
+ ActionDispatch::RemoteIp,
26
+ Appsignal::Rack::RailsInstrumentation
27
+ )
28
+
29
+ if Appsignal.config.active? &&
30
+ Appsignal.config[:enable_frontend_error_catching] == true
31
+ app.middleware.insert_before(
32
+ Appsignal::Rack::RailsInstrumentation,
33
+ Appsignal::Rack::JSExceptionCatcher,
34
+ )
35
+ end
36
+
37
+ Appsignal.start
38
+ end
39
+ end
40
+ end
41
+ end
@@ -156,11 +156,16 @@ module Appsignal
156
156
  cleaned_value = env_var.tr('^0-9'.freeze, ''.freeze)
157
157
  return if cleaned_value.empty?
158
158
  value = cleaned_value.to_i
159
- [1_000_000.0, 1_000.0].each do |factor|
160
- queue_start = (value / factor).to_i
161
- return queue_start if queue_start > 946_681_200 # Ok if it's later than 2000
159
+ if value > 4_102_441_200_000
160
+ # Value is in microseconds
161
+ value / 1_000
162
+ elsif value < 946_681_200_000
163
+ # Value is to low to be plausible
164
+ nil
165
+ else
166
+ # Value is in milliseconds
167
+ value
162
168
  end
163
- nil
164
169
  end
165
170
 
166
171
  def sanitized_params
@@ -1,5 +1,5 @@
1
1
  require 'yaml'
2
2
 
3
3
  module Appsignal
4
- VERSION = '0.12.rc.7'
4
+ VERSION = '0.12.rc.8'
5
5
  end
@@ -10,7 +10,7 @@ if capistrano2_present?
10
10
 
11
11
  before :all do
12
12
  @capistrano_config = Capistrano::Configuration.new
13
- Appsignal::Integrations::Capistrano.tasks(@capistrano_config)
13
+ Appsignal::Capistrano.tasks(@capistrano_config)
14
14
  end
15
15
 
16
16
  it "should have a deploy task" do
@@ -1,20 +1,20 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Celluloid integration" do
4
- let(:file) { File.expand_path('lib/appsignal/integrations/celluloid.rb') }
5
-
3
+ describe Appsignal::Hooks::CelluloidHook do
6
4
  context "with celluloid" do
7
- before(:all) do
5
+ before :all do
8
6
  module Celluloid
9
7
  def self.shutdown
10
8
  end
11
9
  end
10
+ Appsignal::Hooks::CelluloidHook.new.install
12
11
  end
13
-
14
- before do
15
- load file
12
+ after :all do
13
+ Object.send(:remove_const, :Celluloid)
16
14
  end
17
15
 
16
+ its(:dependencies_present?) { should be_true }
17
+
18
18
  specify { expect(Appsignal).to receive(:stop) }
19
19
  specify { expect(Celluloid).to receive(:shutdown_without_appsignal) }
20
20
 
@@ -24,9 +24,6 @@ describe "Celluloid integration" do
24
24
  end
25
25
 
26
26
  context "without celluloid" do
27
- before(:all) { Object.send(:remove_const, :Celluloid) }
28
-
29
- specify { expect { ::Celluloid }.to raise_error(NameError) }
30
- specify { expect { load file }.to_not raise_error }
27
+ its(:dependencies_present?) { should be_false }
31
28
  end
32
29
  end
@@ -1,8 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Delayed Job integration" do
4
- let(:file) { File.expand_path('lib/appsignal/integrations/delayed_job.rb') }
5
-
3
+ describe Appsignal::Hooks::DelayedJobHook do
6
4
  context "with delayed job" do
7
5
  before(:all) do
8
6
  module Delayed
@@ -18,15 +16,17 @@ describe "Delayed Job integration" do
18
16
  end
19
17
  end
20
18
  end
19
+ after(:all) { Object.send(:remove_const, :Delayed) }
21
20
  before do
22
- load file
23
21
  start_agent
24
22
  end
25
23
 
24
+ its(:dependencies_present?) { should be_true }
25
+
26
26
  # We haven't found a way to test the hooks, we'll have to do that manually
27
27
 
28
28
  describe ".invoke_with_instrumentation" do
29
- let(:plugin) { Appsignal::Integrations::DelayedPlugin }
29
+ let(:plugin) { Appsignal::Hooks::DelayedJobPlugin }
30
30
  let(:time) { Time.parse('01-01-2001 10:01:00UTC') }
31
31
  let(:job) do
32
32
  double(
@@ -112,14 +112,11 @@ describe "Delayed Job integration" do
112
112
  end
113
113
 
114
114
  it "should add the plugin" do
115
- ::Delayed::Worker.plugins.should include Appsignal::Integrations::DelayedPlugin
115
+ ::Delayed::Worker.plugins.should include Appsignal::Hooks::DelayedJobPlugin
116
116
  end
117
117
  end
118
118
 
119
119
  context "without delayed job" do
120
- before(:all) { Object.send(:remove_const, :Delayed) }
121
-
122
- specify { expect { ::Delayed }.to raise_error(NameError) }
123
- specify { expect { load file }.to_not raise_error }
120
+ its(:dependencies_present?) { should be_false }
124
121
  end
125
122
  end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Appsignal::Hooks::NetHttpHook do
4
+ before :all do
5
+ Appsignal.config = project_fixture_config
6
+ end
7
+
8
+ context "with Net::HTTP instrumentation enabled" do
9
+ let(:events) { [] }
10
+ before :all do
11
+ Appsignal.config.config_hash[:instrument_net_http] = true
12
+ end
13
+ before do
14
+ ActiveSupport::Notifications.subscribe(/^[^!]/) do |*args|
15
+ events << ActiveSupport::Notifications::Event.new(*args)
16
+ end
17
+ end
18
+ after(:all) { Appsignal.config.config_hash[:instrument_net_http] = false }
19
+
20
+ its(:dependencies_present?) { should be_true }
21
+
22
+ it "should generate an event for a http request" do
23
+ stub_request(:any, 'http://www.google.com/')
24
+
25
+ Net::HTTP.get_response(URI.parse('http://www.google.com'))
26
+
27
+ event = events.last
28
+ event.name.should == 'request.net_http'
29
+ event.payload[:protocol].should == 'http'
30
+ event.payload[:domain].should == 'www.google.com'
31
+ event.payload[:path].should == '/'
32
+ event.payload[:method].should == 'GET'
33
+ end
34
+
35
+ it "should generate an event for a https request" do
36
+ stub_request(:any, 'https://www.google.com/')
37
+
38
+ uri = URI.parse('https://www.google.com')
39
+ http = Net::HTTP.new(uri.host, uri.port)
40
+ http.use_ssl = true
41
+ http.get(uri.request_uri)
42
+
43
+ event = events.last
44
+ event.name.should == 'request.net_http'
45
+ event.payload[:protocol].should == 'https'
46
+ event.payload[:domain].should == 'www.google.com'
47
+ event.payload[:path].should == '/'
48
+ event.payload[:method].should == 'GET'
49
+ end
50
+ end
51
+
52
+ context "with Net::HTTP instrumentation disabled" do
53
+ its(:dependencies_present?) { should be_false }
54
+ end
55
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Appsignal::Hooks::PassengerHook do
4
+ context "with passenger" do
5
+ before(:all) do
6
+ module PhusionPassenger
7
+ end
8
+ end
9
+ after(:all) { Object.send(:remove_const, :PhusionPassenger) }
10
+
11
+ its(:dependencies_present?) { should be_true }
12
+
13
+ it "adds behavior to stopping_worker_process and starting_worker_process" do
14
+ PhusionPassenger.should_receive(:on_event).with(:starting_worker_process)
15
+ PhusionPassenger.should_receive(:on_event).with(:stopping_worker_process)
16
+
17
+ Appsignal::Hooks::PassengerHook.new.install
18
+ end
19
+ end
20
+
21
+ context "without passenger" do
22
+ its(:dependencies_present?) { should be_false }
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe Appsignal::Hooks::PumaHook do
4
+ context "with puma" do
5
+ before(:all) do
6
+ class Puma
7
+ def self.cli_config
8
+ @cli_config ||= CliConfig.new
9
+ end
10
+ end
11
+
12
+ class CliConfig
13
+ attr_accessor :options
14
+
15
+ def initialize
16
+ @options = {}
17
+ end
18
+ end
19
+ end
20
+ after(:all) { Object.send(:remove_const, :Puma) }
21
+
22
+ its(:dependencies_present?) { should be_true }
23
+
24
+ context "with a nil before worker shutdown" do
25
+ before do
26
+ Puma.cli_config.options.delete(:before_worker_shutdown)
27
+ Appsignal::Hooks::PumaHook.new.install
28
+ end
29
+
30
+ it "should add a before shutdown worker callback" do
31
+ Puma.cli_config.options[:before_worker_shutdown].first.should be_a(Proc)
32
+ end
33
+ end
34
+
35
+ context "with an existing before worker shutdown" do
36
+ before do
37
+ Puma.cli_config.options[:before_worker_shutdown] = []
38
+ Appsignal::Hooks::PumaHook.new.install
39
+ end
40
+
41
+ it "should add a before shutdown worker callback" do
42
+ Puma.cli_config.options[:before_worker_shutdown].first.should be_a(Proc)
43
+ end
44
+ end
45
+ end
46
+
47
+ context "without puma" do
48
+ its(:dependencies_present?) { should be_false }
49
+ end
50
+ end