appsignal 0.4.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.
Files changed (59) hide show
  1. data/.gitignore +19 -0
  2. data/.rvmrc +1 -0
  3. data/.travis.yml +30 -0
  4. data/Gemfile +3 -0
  5. data/LICENCE +20 -0
  6. data/README.md +48 -0
  7. data/Rakefile +52 -0
  8. data/appsignal.gemspec +33 -0
  9. data/bin/appsignal +13 -0
  10. data/config/appsignal.yml +8 -0
  11. data/gemfiles/3.0.gemfile +16 -0
  12. data/gemfiles/3.1.gemfile +16 -0
  13. data/gemfiles/3.2.gemfile +16 -0
  14. data/gemfiles/edge.gemfile +16 -0
  15. data/lib/appsignal.rb +45 -0
  16. data/lib/appsignal/agent.rb +104 -0
  17. data/lib/appsignal/auth_check.rb +19 -0
  18. data/lib/appsignal/capistrano.rb +41 -0
  19. data/lib/appsignal/cli.rb +118 -0
  20. data/lib/appsignal/config.rb +30 -0
  21. data/lib/appsignal/exception_notification.rb +25 -0
  22. data/lib/appsignal/marker.rb +35 -0
  23. data/lib/appsignal/middleware.rb +30 -0
  24. data/lib/appsignal/railtie.rb +19 -0
  25. data/lib/appsignal/transaction.rb +77 -0
  26. data/lib/appsignal/transaction/faulty_request_formatter.rb +30 -0
  27. data/lib/appsignal/transaction/params_sanitizer.rb +36 -0
  28. data/lib/appsignal/transaction/regular_request_formatter.rb +11 -0
  29. data/lib/appsignal/transaction/slow_request_formatter.rb +34 -0
  30. data/lib/appsignal/transaction/transaction_formatter.rb +93 -0
  31. data/lib/appsignal/transmitter.rb +53 -0
  32. data/lib/appsignal/version.rb +3 -0
  33. data/lib/generators/appsignal/USAGE +8 -0
  34. data/lib/generators/appsignal/appsignal_generator.rb +70 -0
  35. data/lib/generators/appsignal/templates/appsignal.yml +4 -0
  36. data/log/.gitkeep +0 -0
  37. data/resources/cacert.pem +3849 -0
  38. data/spec/appsignal/agent_spec.rb +259 -0
  39. data/spec/appsignal/auth_check_spec.rb +36 -0
  40. data/spec/appsignal/capistrano_spec.rb +81 -0
  41. data/spec/appsignal/cli_spec.rb +124 -0
  42. data/spec/appsignal/config_spec.rb +40 -0
  43. data/spec/appsignal/exception_notification_spec.rb +12 -0
  44. data/spec/appsignal/inactive_railtie_spec.rb +30 -0
  45. data/spec/appsignal/marker_spec.rb +83 -0
  46. data/spec/appsignal/middleware_spec.rb +73 -0
  47. data/spec/appsignal/railtie_spec.rb +54 -0
  48. data/spec/appsignal/transaction/faulty_request_formatter_spec.rb +49 -0
  49. data/spec/appsignal/transaction/params_sanitizer_spec.rb +68 -0
  50. data/spec/appsignal/transaction/regular_request_formatter_spec.rb +14 -0
  51. data/spec/appsignal/transaction/slow_request_formatter_spec.rb +76 -0
  52. data/spec/appsignal/transaction/transaction_formatter_spec.rb +178 -0
  53. data/spec/appsignal/transaction_spec.rb +191 -0
  54. data/spec/appsignal/transmitter_spec.rb +64 -0
  55. data/spec/appsignal_spec.rb +66 -0
  56. data/spec/generators/appsignal/appsignal_generator_spec.rb +222 -0
  57. data/spec/spec_helper.rb +85 -0
  58. data/spec/support/delegate_matcher.rb +39 -0
  59. metadata +247 -0
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe Appsignal::Config do
4
+ subject { Appsignal::Config.new(Dir.pwd, 'test').load }
5
+
6
+ it {
7
+ should == {
8
+ :ignore_exceptions => [],
9
+ :endpoint => 'http://localhost:3000/1',
10
+ :slow_request_threshold => 200,
11
+ :api_key => 'abc',
12
+ :active => true
13
+ }
14
+ }
15
+
16
+ context 'when there is no config file' do
17
+ before { Dir.stub(:pwd => '/not/existing') }
18
+
19
+ it "should log error" do
20
+ Appsignal.logger.should_receive(:error).with(
21
+ "config not found at:"\
22
+ " /not/existing/config/appsignal.yml"
23
+ )
24
+ end
25
+
26
+ after { subject }
27
+ end
28
+
29
+ context "the env is not in the config" do
30
+ subject { Appsignal::Config.new(Dir.pwd, 'staging').load }
31
+
32
+ it "should generate error" do
33
+ Appsignal.logger.should_receive(:error).with(
34
+ "config for 'staging' not found"
35
+ )
36
+ end
37
+
38
+ after { subject }
39
+ end
40
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Appsignal::ExceptionNotification do
4
+ let(:error) { StandardError.new('moo') }
5
+ let(:notification) { Appsignal::ExceptionNotification.new({}, error) }
6
+ subject { notification }
7
+ before { Rails.stub(:respond_to? => false) }
8
+
9
+ its(:exception) { should == error }
10
+ its(:name) { should == 'StandardError' }
11
+ its(:message) { should == 'moo' }
12
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Inactive Appsignal::Railtie" do
4
+ it "should not insert itself into the middleware stack" do
5
+ # This uses a hack because Rails really dislikes you trying to
6
+ # start multiple applications in one process. This works decently
7
+ # on every platform except JRuby, so we're disabling this test on
8
+ # JRuby for now.
9
+ if RUBY_PLATFORM == "java"
10
+ pending "This spec cannot run on JRuby currently"
11
+ else
12
+ pid = fork do
13
+ Appsignal.stub(:active? => false)
14
+ Rails.application = nil
15
+ instance_eval do
16
+ module MyTempApp
17
+ class Application < Rails::Application
18
+ config.active_support.deprecation = proc { |message, stack| }
19
+ end
20
+ end
21
+ end
22
+ MyTempApp::Application.initialize!
23
+
24
+ MyTempApp::Application.middleware.to_a.should_not include Appsignal::Middleware
25
+ end
26
+ Process.wait(pid)
27
+ raise 'Example failed' unless $?.exitstatus == 0
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ describe Appsignal::Marker do
4
+ let(:marker) {
5
+ Appsignal::Marker.new({
6
+ :revision => '503ce0923ed177a3ce000005',
7
+ :repository => 'master',
8
+ :user => 'batman',
9
+ :rails_env => 'development'
10
+ },
11
+ Dir.pwd,
12
+ 'development',
13
+ logger
14
+ )
15
+ }
16
+ let(:log) { StringIO.new }
17
+ let(:logger) { Logger.new(log) }
18
+
19
+ context "transmit" do
20
+ before do
21
+ @transmitter = mock()
22
+ Appsignal::Transmitter.should_receive(:new).
23
+ with('http://localhost:3000/1', 'markers', 'abc').
24
+ and_return(@transmitter)
25
+ end
26
+
27
+ it "should transmit data" do
28
+ @transmitter.should_receive(:transmit).
29
+ with(
30
+ {
31
+ :revision => "503ce0923ed177a3ce000005",
32
+ :repository => "master",
33
+ :user => "batman",
34
+ :rails_env => "development"
35
+ }
36
+ )
37
+
38
+ marker.transmit
39
+ end
40
+
41
+ context "logs" do
42
+ shared_examples_for "logging info and errors" do
43
+ it "should log status 200" do
44
+ @transmitter.should_receive(:transmit).and_return('200')
45
+
46
+ marker.transmit
47
+
48
+ log.string.should include('Notifying Appsignal of deploy...')
49
+ log.string.should include(
50
+ 'Appsignal has been notified of this deploy!'
51
+ )
52
+ end
53
+
54
+ it "should log other status" do
55
+ @transmitter.should_receive(:transmit).and_return('500')
56
+ @transmitter.should_receive(:uri).and_return('http://localhost:3000/1/markers')
57
+
58
+ marker.transmit
59
+
60
+ log.string.should include('Notifying Appsignal of deploy...')
61
+ log.string.should include(
62
+ 'Something went wrong while trying to notify Appsignal: 500 at http://localhost:3000/1/markers'
63
+ )
64
+ log.string.should_not include(
65
+ 'Appsignal has been notified of this deploy!'
66
+ )
67
+ end
68
+ end
69
+
70
+ it_should_behave_like "logging info and errors"
71
+
72
+ context "with a Capistrano logger" do
73
+ let(:logger) {
74
+ Capistrano::Logger.new(:output => log).tap do |logger|
75
+ logger.level = Capistrano::Logger::MAX_LEVEL
76
+ end
77
+ }
78
+
79
+ it_should_behave_like "logging info and errors"
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ module Appsignal
4
+ IgnoreMeError = Class.new(StandardError)
5
+ end
6
+
7
+ class AppWithError
8
+ def self.call(env)
9
+ raise Appsignal::IgnoreMeError, 'the roof'
10
+ end
11
+ end
12
+
13
+ describe Appsignal::Middleware do
14
+ describe '#call' do
15
+ let(:app) { stub(:call => true) }
16
+ let(:env) { {'action_dispatch.request_id' => '1'} }
17
+ let(:middleware) { Appsignal::Middleware.new(app, {})}
18
+ let(:current) { stub(:complete! => true, :add_exception => true) }
19
+ before { Appsignal::Transaction.stub(:current => current) }
20
+
21
+ describe 'around call' do
22
+ it 'should call appsignal transaction' do
23
+ Appsignal::Transaction.should_receive(:create).with('1', env)
24
+ end
25
+
26
+ it 'should call complete! after the call' do
27
+ current.should_receive(:complete!)
28
+ end
29
+
30
+ after { middleware.call(env) }
31
+ end
32
+
33
+ describe 'with exception' do
34
+ let(:app) { AppWithError }
35
+
36
+ it 'should re-raise the exception' do
37
+ expect {
38
+ middleware.call(env)
39
+ }.to raise_error
40
+ end
41
+
42
+ it 'should catch the exception and notify the transaction of it' do
43
+ Appsignal::ExceptionNotification.should_receive(:new)
44
+ current.should_receive(:add_exception)
45
+ middleware.call(env) rescue nil
46
+ end
47
+
48
+ context 'when ignoring exception' do
49
+ before { Appsignal.stub(:config => {:ignore_exceptions => 'Appsignal::IgnoreMeError'})}
50
+
51
+ it 'should re-raise the exception' do
52
+ expect {
53
+ middleware.call(env)
54
+ }.to raise_error
55
+ end
56
+
57
+ it 'should ignore the error' do
58
+ Appsignal::ExceptionNotification.should_not_receive(:new)
59
+ current.should_not_receive(:add_exception)
60
+ middleware.call(env) rescue nil
61
+ end
62
+ end
63
+
64
+ describe 'after an error' do
65
+ it 'should call complete! after the call' do
66
+ current.should_receive(:complete!)
67
+ end
68
+
69
+ after { middleware.call(env) rescue nil }
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+ require 'action_controller/railtie'
3
+ require 'appsignal/railtie'
4
+
5
+ describe Appsignal::Railtie do
6
+
7
+ before(:all) { MyApp::Application.initialize! }
8
+
9
+ it "should have set the appsignal subscriber" do
10
+ Appsignal.subscriber.
11
+ should be_a ActiveSupport::Notifications::Fanout::Subscriber
12
+ end
13
+
14
+ it "should have added the middleware for exceptions" do
15
+ MyApp::Application.middleware.to_a.should include Appsignal::Middleware
16
+ end
17
+
18
+ context "non action_controller event" do
19
+ it "should call add_event for non action_controller event" do
20
+ current = stub
21
+ current.should_receive(:add_event)
22
+ Appsignal::Transaction.should_receive(:current).twice.
23
+ and_return(current)
24
+ end
25
+
26
+ after { ActiveSupport::Notifications.instrument 'query.mongoid' }
27
+ end
28
+
29
+ context "action_controller event" do
30
+ it "should call set_process_action_event for action_controller event" do
31
+ current = stub
32
+ current.should_receive(:set_process_action_event)
33
+ current.should_receive(:add_event)
34
+ Appsignal::Transaction.should_receive(:current).exactly(3).times.
35
+ and_return(current)
36
+ end
37
+
38
+ after do
39
+ ActiveSupport::Notifications.
40
+ instrument 'process_action.action_controller'
41
+ end
42
+ end
43
+
44
+ context "event that starts with a bang" do
45
+ it "should not be processed" do
46
+ Appsignal::Transaction.should_not_receive(:current)
47
+ end
48
+
49
+ after do
50
+ ActiveSupport::Notifications.
51
+ instrument '!render_template'
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe Appsignal::TransactionFormatter::FaultyRequestFormatter do
4
+ let(:parent) { Appsignal::TransactionFormatter }
5
+ let(:transaction) { transaction_with_exception }
6
+ let(:faulty) { parent::FaultyRequestFormatter.new(transaction) }
7
+ subject { faulty }
8
+
9
+ describe "#to_hash" do
10
+ it "can call #to_hash on its superclass" do
11
+ parent.new(transaction).respond_to?(:to_hash).should be_true
12
+ end
13
+
14
+ context "return value" do
15
+ subject { faulty.to_hash }
16
+ before { faulty.stub(:formatted_exception => :faulty_request) }
17
+
18
+ it "includes the exception" do
19
+ subject[:exception].should == :faulty_request
20
+ end
21
+ end
22
+ end
23
+
24
+ # protected
25
+
26
+ it { should delegate(:backtrace).to(:exception) }
27
+ it { should delegate(:name).to(:exception) }
28
+ it { should delegate(:message).to(:exception) }
29
+
30
+ describe "#formatted_exception" do
31
+ subject { faulty.send(:formatted_exception) }
32
+
33
+ its(:keys) { should include :backtrace }
34
+ its(:keys) { should include :exception }
35
+ its(:keys) { should include :message }
36
+ end
37
+
38
+ describe "#basic_process_action_event" do
39
+ subject { faulty.send(:basic_process_action_event) }
40
+
41
+ it "should return a hash with extra keys" do
42
+ subject[:environment].should == {
43
+ "HTTP_USER_AGENT" => "IE6",
44
+ "SERVER_NAME" => "localhost"
45
+ }
46
+ subject[:session_data].should == {}
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe Appsignal::ParamsSanitizer do
4
+ describe ".sanitize" do
5
+ let(:file) { ActionDispatch::Http::UploadedFile.new(:tempfile => '/tmp') }
6
+ let(:params) do
7
+ {
8
+ :text => 'string',
9
+ :file => file,
10
+ :hash => {
11
+ :nested_text => 'string',
12
+ :nested_array => [
13
+ 'something',
14
+ 'else',
15
+ file,
16
+ {
17
+ :key => 'value',
18
+ :nested_array_hash_file => file,
19
+ }
20
+ ]
21
+ }
22
+ }
23
+ end
24
+ let(:sanitized_params) { Appsignal::ParamsSanitizer.sanitize(params) }
25
+
26
+ subject { sanitized_params }
27
+
28
+ it { should be_instance_of Hash }
29
+ it('should have a text') { subject[:text].should == 'string' }
30
+ it('should have a file') do
31
+ subject[:file].should be_instance_of String
32
+ subject[:file].should include '#<ActionDispatch::Http::UploadedFile:'
33
+ end
34
+
35
+ context "hash" do
36
+ subject { sanitized_params[:hash] }
37
+
38
+ it { should be_instance_of Hash }
39
+ it('should have a nested text') { subject[:nested_text].should == 'string' }
40
+
41
+ context "nested_array" do
42
+ subject { sanitized_params[:hash][:nested_array] }
43
+
44
+ it { should be_instance_of Array }
45
+
46
+ it("should have two string items") do
47
+ subject.first.should == 'something'
48
+ subject.second.should == 'else'
49
+ end
50
+ it "should have a file" do
51
+ subject.third.should be_instance_of String
52
+ subject.third.should include '#<ActionDispatch::Http::UploadedFile:'
53
+ end
54
+
55
+ context "nested hash" do
56
+ subject { sanitized_params[:hash][:nested_array].fourth }
57
+
58
+ it { should be_instance_of Hash }
59
+ it('should have a text') { subject[:key].should == 'value' }
60
+ it('should have a file') do
61
+ subject[:nested_array_hash_file].should be_instance_of String
62
+ subject[:nested_array_hash_file].should include '#<ActionDispatch::Http::UploadedFile:'
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Appsignal::TransactionFormatter::RegularRequestFormatter do
4
+ let(:parent) { Appsignal::TransactionFormatter }
5
+ let(:transaction) { appsignal_transaction }
6
+ let(:klass) { parent::RegularRequestFormatter }
7
+ let(:regular) { klass.new(transaction) }
8
+
9
+ describe "#sanitized_event_payload" do
10
+ subject { regular.sanitized_event_payload(:whatever, :arguments) }
11
+
12
+ it { should == {} }
13
+ end
14
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe Appsignal::TransactionFormatter::SlowRequestFormatter do
5
+ let(:parent) { Appsignal::TransactionFormatter }
6
+ let(:transaction) { slow_transaction }
7
+ let(:klass) { parent::SlowRequestFormatter }
8
+ let(:slow) { klass.new(transaction) }
9
+
10
+ describe "#to_hash" do
11
+ subject { slow.to_hash }
12
+ before { slow.stub(:detailed_events => :startled) }
13
+
14
+ it "includes events" do
15
+ subject[:events].should == :startled
16
+ end
17
+ end
18
+
19
+ # protected
20
+
21
+ context "with an event" do
22
+ let(:start_time) { Time.at(2.71828182) }
23
+ let(:end_time) { Time.at(3.141592654) }
24
+ let(:event) do
25
+ mock(
26
+ :event,
27
+ :name => 'Startled',
28
+ :duration => 2,
29
+ :time => start_time,
30
+ :end => end_time,
31
+ :payload => {
32
+ :controller => 'controller',
33
+ :action => 'action',
34
+ :sensitive => 'data'
35
+ }
36
+ )
37
+ end
38
+
39
+ describe "#detailed_events" do
40
+ subject { slow.send(:detailed_events) }
41
+ before do
42
+ slow.stub(
43
+ :events => [event],
44
+ :format => :foo
45
+ )
46
+ end
47
+
48
+ it { should == [:foo] }
49
+ end
50
+
51
+ describe "#format" do
52
+ subject { slow.send(:format, event) }
53
+ before { slow.stub(:sanitized_event_payload => :sanitized) }
54
+
55
+ it { should == {
56
+ :name => 'Startled',
57
+ :duration => 2,
58
+ :time => start_time.to_f,
59
+ :end => end_time.to_f,
60
+ :payload => :sanitized
61
+ } }
62
+ end
63
+ end
64
+
65
+ describe "#basic_process_action_event" do
66
+ subject { slow.send(:basic_process_action_event) }
67
+
68
+ it "should return a hash with extra keys" do
69
+ subject[:environment].should == {
70
+ "HTTP_USER_AGENT" => "IE6",
71
+ "SERVER_NAME" => "localhost"
72
+ }
73
+ subject[:session_data].should == {}
74
+ end
75
+ end
76
+ end