appsignal 0.6.7 → 0.7.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +3 -2
  3. data/.travis.yml +11 -11
  4. data/CHANGELOG.md +6 -0
  5. data/README.md +26 -0
  6. data/Rakefile +32 -3
  7. data/appsignal.gemspec +25 -16
  8. data/gemfiles/no_dependencies.gemfile +3 -0
  9. data/gemfiles/{3.0.gemfile → rails-3.0.gemfile} +2 -1
  10. data/gemfiles/{3.1.gemfile → rails-3.1.gemfile} +2 -1
  11. data/gemfiles/{3.2.gemfile → rails-3.2.gemfile} +2 -1
  12. data/gemfiles/{4.0.gemfile → rails-4.0.gemfile} +2 -1
  13. data/gemfiles/sinatra.gemfile +5 -0
  14. data/lib/appsignal.rb +30 -25
  15. data/lib/appsignal/agent.rb +37 -17
  16. data/lib/appsignal/aggregator.rb +2 -4
  17. data/lib/appsignal/aggregator/middleware.rb +4 -0
  18. data/lib/appsignal/aggregator/middleware/action_view_sanitizer.rb +23 -0
  19. data/lib/appsignal/aggregator/middleware/active_record_sanitizer.rb +64 -0
  20. data/lib/appsignal/aggregator/middleware/chain.rb +101 -0
  21. data/lib/appsignal/aggregator/middleware/delete_blanks.rb +16 -0
  22. data/lib/appsignal/aggregator/post_processor.rb +21 -18
  23. data/lib/appsignal/auth_check.rb +7 -21
  24. data/lib/appsignal/capistrano.rb +1 -36
  25. data/lib/appsignal/cli.rb +30 -46
  26. data/lib/appsignal/config.rb +46 -61
  27. data/lib/appsignal/integrations/capistrano.rb +44 -0
  28. data/lib/appsignal/{careful_logger.rb → integrations/capistrano/careful_logger.rb} +2 -0
  29. data/lib/appsignal/integrations/passenger.rb +6 -6
  30. data/lib/appsignal/integrations/rails.rb +33 -0
  31. data/lib/appsignal/integrations/sinatra.rb +20 -0
  32. data/lib/appsignal/marker.rb +9 -10
  33. data/lib/appsignal/rack/instrumentation.rb +28 -0
  34. data/lib/appsignal/rack/listener.rb +33 -0
  35. data/lib/appsignal/transaction.rb +18 -12
  36. data/lib/appsignal/transaction/formatter.rb +96 -0
  37. data/lib/appsignal/transaction/params_sanitizer.rb +80 -78
  38. data/lib/appsignal/transmitter.rb +10 -9
  39. data/lib/appsignal/version.rb +1 -1
  40. data/lib/generators/appsignal/appsignal_generator.rb +20 -21
  41. data/lib/generators/appsignal/templates/appsignal.yml +15 -21
  42. data/spec/{appsignal → lib/appsignal}/agent_spec.rb +69 -1
  43. data/spec/lib/appsignal/aggregator/middleware/action_view_sanitizer_spec.rb +32 -0
  44. data/spec/lib/appsignal/aggregator/middleware/active_record_sanitizer_spec.rb +215 -0
  45. data/spec/{appsignal → lib/appsignal/aggregator}/middleware/chain_spec.rb +5 -5
  46. data/spec/{appsignal → lib/appsignal/aggregator}/middleware/delete_blanks_spec.rb +2 -2
  47. data/spec/{appsignal → lib/appsignal}/aggregator/post_processor_spec.rb +15 -6
  48. data/spec/{appsignal → lib/appsignal}/aggregator_spec.rb +4 -1
  49. data/spec/{appsignal → lib/appsignal}/auth_check_spec.rb +8 -23
  50. data/spec/{appsignal → lib/appsignal}/cli_spec.rb +65 -66
  51. data/spec/lib/appsignal/config_spec.rb +132 -0
  52. data/spec/lib/appsignal/integrations/capistrano_spec.rb +123 -0
  53. data/spec/{appsignal → lib/appsignal}/integrations/passenger_spec.rb +0 -1
  54. data/spec/lib/appsignal/integrations/rails_spec.rb +38 -0
  55. data/spec/lib/appsignal/integrations/sinatra_spec.rb +43 -0
  56. data/spec/{appsignal → lib/appsignal}/marker_spec.rb +20 -23
  57. data/spec/lib/appsignal/rack/instrumentation_spec.rb +49 -0
  58. data/spec/{appsignal → lib/appsignal/rack}/listener_spec.rb +39 -6
  59. data/spec/{appsignal/transaction/transaction_formatter_spec.rb → lib/appsignal/transaction/formatter_spec.rb} +29 -6
  60. data/spec/{appsignal → lib/appsignal}/transaction/params_sanitizer_spec.rb +13 -12
  61. data/spec/{appsignal → lib/appsignal}/transaction_spec.rb +52 -7
  62. data/spec/{appsignal → lib/appsignal}/transmitter_spec.rb +27 -20
  63. data/spec/lib/appsignal_spec.rb +230 -0
  64. data/spec/lib/generators/appsignal/appsignal_generator_spec.rb +166 -0
  65. data/spec/lib/tmp/config/appsignal.yml +2 -0
  66. data/spec/spec_helper.rb +29 -20
  67. data/spec/support/delegate_matcher.rb +0 -1
  68. data/spec/support/fixtures/generated_config.yml +20 -0
  69. data/{log/.gitkeep → spec/support/fixtures/uploaded_file.txt} +0 -0
  70. data/spec/support/helpers/config_helpers.rb +24 -0
  71. data/spec/support/helpers/notification_helpers.rb +0 -2
  72. data/spec/support/helpers/transaction_helpers.rb +17 -2
  73. data/spec/support/project_fixture/config/appsignal.yml +18 -0
  74. data/spec/support/project_fixture/log/.gitkeep +0 -0
  75. data/spec/support/rails/my_app.rb +6 -0
  76. metadata +99 -83
  77. data/config/appsignal.yml +0 -10
  78. data/lib/appsignal/listener.rb +0 -21
  79. data/lib/appsignal/middleware.rb +0 -3
  80. data/lib/appsignal/middleware/action_view_sanitizer.rb +0 -21
  81. data/lib/appsignal/middleware/active_record_sanitizer.rb +0 -62
  82. data/lib/appsignal/middleware/chain.rb +0 -99
  83. data/lib/appsignal/middleware/delete_blanks.rb +0 -12
  84. data/lib/appsignal/railtie.rb +0 -37
  85. data/lib/appsignal/to_appsignal_hash.rb +0 -21
  86. data/lib/appsignal/transaction/transaction_formatter.rb +0 -67
  87. data/spec/appsignal/capistrano_spec.rb +0 -81
  88. data/spec/appsignal/config_spec.rb +0 -177
  89. data/spec/appsignal/inactive_railtie_spec.rb +0 -32
  90. data/spec/appsignal/middleware/action_view_sanitizer_spec.rb +0 -27
  91. data/spec/appsignal/middleware/active_record_sanitizer_spec.rb +0 -212
  92. data/spec/appsignal/railtie_spec.rb +0 -74
  93. data/spec/appsignal/to_appsignal_hash_spec.rb +0 -29
  94. data/spec/appsignal_spec.rb +0 -195
  95. data/spec/generators/appsignal/appsignal_generator_spec.rb +0 -181
data/config/appsignal.yml DELETED
@@ -1,10 +0,0 @@
1
- development: &development
2
- endpoint: "http://localhost:3000/1"
3
- api_key: "abc"
4
- active: <%= 'true' %>
5
- production:
6
- <<: *development
7
- api_key: "def"
8
- test:
9
- <<: *development
10
- api_key: "ghi"
@@ -1,21 +0,0 @@
1
- require 'action_dispatch'
2
-
3
- module Appsignal
4
- class Listener
5
- def initialize(app, options = {})
6
- @app, @options = app, options
7
- end
8
-
9
- def call(env)
10
- Appsignal::Transaction.create(env['action_dispatch.request_id'], env)
11
- @app.call(env)
12
- rescue Exception => exception
13
- unless Appsignal.is_ignored_exception?(exception)
14
- Appsignal::Transaction.current.add_exception(exception)
15
- end
16
- raise exception
17
- ensure
18
- Appsignal::Transaction.current.complete!
19
- end
20
- end
21
- end
@@ -1,3 +0,0 @@
1
- require 'appsignal/middleware/chain'
2
- require 'appsignal/middleware/delete_blanks'
3
- require 'appsignal/middleware/action_view_sanitizer'
@@ -1,21 +0,0 @@
1
- module Appsignal
2
- module Middleware
3
- class ActionViewSanitizer
4
- TARGET_EVENT_CATEGORY = 'action_view'.freeze
5
-
6
- def call(event)
7
- if event.name.end_with?(TARGET_EVENT_CATEGORY)
8
- identifier = event.payload[:identifier]
9
- if identifier
10
- identifier.gsub!(root_path, '')
11
- end
12
- end
13
- yield
14
- end
15
-
16
- def root_path
17
- @root_path ||= "#{Rails.root.to_s}/"
18
- end
19
- end
20
- end
21
- end
@@ -1,62 +0,0 @@
1
- module Appsignal
2
- module Middleware
3
- class ActiveRecordSanitizer
4
- TARGET_EVENT_NAME = 'sql.active_record'.freeze
5
-
6
- SINGLE_QUOTE = /\\'/.freeze
7
- DOUBLE_QUOTE = /\\"/.freeze
8
- QUOTED_DATA = /(?:"[^"]+"|'[^']+')/.freeze
9
- SINGLE_QUOTED_DATA = /(?:'[^']+')/.freeze
10
- IN_ARRAY = /(IN \()[^\)]+(\))/.freeze
11
- NUMERIC_DATA = /\b\d+\b/.freeze
12
-
13
- SANITIZED_VALUE = '\1?\2'.freeze
14
-
15
- def call(event)
16
- if event.name == TARGET_EVENT_NAME
17
- unless schema_query?(event) || adapter_uses_prepared_statements?
18
- query_string = event.payload[:sql]
19
- if query_string
20
- if adapter_uses_double_quoted_table_names?
21
- query_string.gsub!(SINGLE_QUOTE, SANITIZED_VALUE)
22
- query_string.gsub!(SINGLE_QUOTED_DATA, SANITIZED_VALUE)
23
- else
24
- query_string.gsub!(SINGLE_QUOTE, SANITIZED_VALUE)
25
- query_string.gsub!(DOUBLE_QUOTE, SANITIZED_VALUE)
26
- query_string.gsub!(QUOTED_DATA, SANITIZED_VALUE)
27
- end
28
- query_string.gsub!(IN_ARRAY, SANITIZED_VALUE)
29
- query_string.gsub!(NUMERIC_DATA, SANITIZED_VALUE)
30
- end
31
- end
32
- event.payload.delete(:connection_id)
33
- event.payload.delete(:binds)
34
- end
35
- yield
36
- end
37
-
38
- def schema_query?(event)
39
- event.payload[:name] == 'SCHEMA'
40
- end
41
-
42
- def connection_config
43
- @connection_config ||= if ActiveRecord::Base.respond_to?(:connection_config)
44
- ActiveRecord::Base.connection_config
45
- else
46
- ActiveRecord::Base.connection_pool.spec.config
47
- end
48
- end
49
-
50
- def adapter_uses_double_quoted_table_names?
51
- adapter = connection_config[:adapter]
52
- adapter =~ /postgres/ || adapter =~ /sqlite/
53
- end
54
-
55
- def adapter_uses_prepared_statements?
56
- return false unless adapter_uses_double_quoted_table_names?
57
- return true if connection_config[:prepared_statements].nil?
58
- connection_config[:prepared_statements]
59
- end
60
- end
61
- end
62
- end
@@ -1,99 +0,0 @@
1
- module Appsignal
2
- # Middleware is code configured to run before/after a message is processed.
3
- # It is patterned after Rack middleware.
4
- #
5
- # @example To add middleware:
6
- #
7
- # Appsignal.post_processing_middleware do |chain|
8
- # chain.add MyPostProcessingHook
9
- # end
10
- #
11
- # @example To insert immediately preceding another entry:
12
- #
13
- # Appsignal.post_process_middleware do |chain|
14
- # chain.insert_before ActiveRecord, MyPostProcessingHook
15
- # end
16
- #
17
- # @example To insert immediately after another entry:
18
- #
19
- # Appsignal.post_process_middleware do |chain|
20
- # chain.insert_after ActiveRecord, MyPostProcessingHook
21
- # end
22
- #
23
- # @example This is an example of a minimal middleware class:
24
- #
25
- # class MySHook
26
- # def call(transaction)
27
- # puts "Before post processing"
28
- # yield
29
- # puts "After post processing"
30
- # end
31
- # end
32
- #
33
- module Middleware
34
- class Chain
35
- attr_reader :entries
36
-
37
- def initialize
38
- @entries = []
39
- yield self if block_given?
40
- end
41
-
42
- def remove(klass)
43
- entries.delete_if { |entry| entry.klass == klass }
44
- end
45
-
46
- def add(klass, *args)
47
- entries << Entry.new(klass, *args) unless exists?(klass)
48
- end
49
-
50
- def insert_before(oldklass, newklass, *args)
51
- i = entries.index { |entry| entry.klass == newklass }
52
- new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
53
- i = entries.find_index { |entry| entry.klass == oldklass } || 0
54
- entries.insert(i, new_entry)
55
- end
56
-
57
- def insert_after(oldklass, newklass, *args)
58
- i = entries.index { |entry| entry.klass == newklass }
59
- new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
60
- i = entries.find_index { |entry| entry.klass == oldklass } || entries.count - 1
61
- entries.insert(i+1, new_entry)
62
- end
63
-
64
- def exists?(klass)
65
- entries.any? { |entry| entry.klass == klass }
66
- end
67
-
68
- def retrieve
69
- @retrieve ||= entries.map(&:make_new)
70
- end
71
-
72
- def clear
73
- entries.clear
74
- end
75
-
76
- def invoke(*args)
77
- chain = retrieve.dup
78
- traverse_chain = lambda do
79
- unless chain.empty?
80
- chain.shift.call(*args, &traverse_chain)
81
- end
82
- end
83
- traverse_chain.call
84
- end
85
- end
86
-
87
- class Entry
88
- attr_reader :klass
89
- def initialize(klass, *args)
90
- @klass = klass
91
- @args = args
92
- end
93
-
94
- def make_new
95
- @klass.new(*@args)
96
- end
97
- end
98
- end
99
- end
@@ -1,12 +0,0 @@
1
- module Appsignal
2
- module Middleware
3
- class DeleteBlanks
4
- def call(event)
5
- event.payload.each do |key, value|
6
- event.payload.delete(key) if value.blank?
7
- end
8
- yield
9
- end
10
- end
11
- end
12
- end
@@ -1,37 +0,0 @@
1
- module Appsignal
2
- class Railtie < Rails::Railtie
3
- initializer "appsignal.configure_rails_initialization" do |app|
4
- # Some apps when run from the console do not have Rails.root set, there's
5
- # currently no way to spec this.
6
- if Rails.root
7
- if File.writable?('log')
8
- output = Rails.root.join('log/appsignal.log')
9
- else
10
- output = STDOUT
11
- end
12
- Appsignal.logger = Logger.new(output).tap do |l|
13
- l.level = Logger::INFO
14
- end
15
- Appsignal.flush_in_memory_log
16
- end
17
-
18
- if Appsignal.active?
19
- Appsignal.logger.info("Activating appsignal-#{Appsignal::VERSION}")
20
- at_exit { Appsignal.agent.shutdown(true) }
21
- app.middleware.insert_before(ActionDispatch::RemoteIp, Appsignal::Listener)
22
-
23
- Appsignal.subscriber = ActiveSupport::Notifications.subscribe(/^[^!]/) do |*args|
24
- if Appsignal::Transaction.current
25
- event = ActiveSupport::Notifications::Event.new(*args)
26
- if event.name == 'process_action.action_controller'
27
- Appsignal::Transaction.current.set_process_action_event(event)
28
- end
29
- Appsignal::Transaction.current.add_event(event)
30
- end
31
- end
32
- end
33
- end
34
- end
35
- end
36
-
37
- require 'appsignal/to_appsignal_hash'
@@ -1,21 +0,0 @@
1
- module Appsignal
2
- module ToAppsignalHash
3
- def to_appsignal_hash
4
- {
5
- :name => name,
6
- :duration => duration,
7
- :time => time.to_f,
8
- :end => self.end.to_f,
9
- :payload => payload
10
- }
11
- end
12
- end
13
- end
14
-
15
- module ActiveSupport
16
- module Notifications
17
- class Event
18
- include Appsignal::ToAppsignalHash
19
- end
20
- end
21
- end
@@ -1,67 +0,0 @@
1
- require 'delegate'
2
-
3
- module Appsignal
4
- class TransactionFormatter < SimpleDelegator
5
-
6
- def initialize(transaction)
7
- super(transaction)
8
- end
9
-
10
- def hash
11
- @hash ||= default_hash
12
- end
13
-
14
- def to_hash
15
- merge_process_action_event_with_log_entry! if process_action_event
16
- if exception?
17
- add_exception_to_hash!
18
- add_tags_to_hash!
19
- end
20
- add_events_to_hash! if slow_request?
21
- hash
22
- end
23
-
24
- protected
25
-
26
- def default_hash
27
- {
28
- :request_id => request_id,
29
- :log_entry => {
30
- :path => fullpath,
31
- :kind => 'http_request',
32
- :time => time,
33
- :environment => sanitized_environment,
34
- :session_data => sanitized_session_data
35
- },
36
- :failed => exception?
37
- }
38
- end
39
-
40
- def merge_process_action_event_with_log_entry!
41
- hash[:log_entry].merge!(process_action_event.to_appsignal_hash)
42
- hash[:log_entry].tap do |o|
43
- o.merge!(o.delete(:payload))
44
- o.delete(:action)
45
- o.delete(:controller)
46
- o.delete(:name)
47
- o[:action] = action
48
- end
49
- end
50
-
51
- def add_tags_to_hash!
52
- hash[:log_entry][:tags] = tags
53
- end
54
-
55
- def add_exception_to_hash!
56
- hash[:exception] = {
57
- :exception => exception.class.name,
58
- :message => exception.message,
59
- :backtrace => Rails.backtrace_cleaner.clean(exception.backtrace, nil)
60
- }
61
- end
62
-
63
- def add_events_to_hash!
64
- hash[:events] = events.map(&:to_appsignal_hash)
65
- end
66
- end
67
- end
@@ -1,81 +0,0 @@
1
- require 'spec_helper'
2
- require 'appsignal/capistrano'
3
- require 'capistrano/configuration'
4
-
5
- describe Appsignal::Capistrano do
6
- before :all do
7
- @config = Capistrano::Configuration.new
8
- Appsignal::Capistrano.tasks(@config)
9
- end
10
-
11
- it "should have a deploy task" do
12
- @config.find_task('appsignal:deploy').should_not be_nil
13
- end
14
-
15
- describe "appsignal:deploy task" do
16
- before :all do
17
- @config.set(:rails_env, 'development')
18
- @config.set(:repository, 'master')
19
- @config.set(:deploy_to, '/home/username/app')
20
- @config.set(:current_release, '')
21
- @config.set(:current_revision, '503ce0923ed177a3ce000005')
22
- ENV['USER'] = 'batman'
23
- end
24
-
25
- context "send marker" do
26
- let(:marker_data) {
27
- {
28
- :revision => "503ce0923ed177a3ce000005",
29
- :repository => "master",
30
- :user => "batman"
31
- }
32
- }
33
- before :all do
34
- @io = StringIO.new
35
- @logger = Capistrano::Logger.new(:output => @io)
36
- @logger.level = Capistrano::Logger::MAX_LEVEL
37
- @config.logger = @logger
38
- end
39
- before do
40
- @marker = Appsignal::Marker.new(marker_data, Rails.root.to_s,
41
- 'development', @logger)
42
- Appsignal::Marker.should_receive(:new).
43
- with(marker_data, Rails.root.to_s, 'development', anything()).
44
- and_return(@marker)
45
- end
46
-
47
- context "proper setup" do
48
- before do
49
- @transmitter = double
50
- Appsignal::Transmitter.should_receive(:new).and_return(@transmitter)
51
- end
52
-
53
- it "should transmit data" do
54
- @transmitter.should_receive(:transmit).and_return('200')
55
- @config.find_and_execute_task('appsignal:deploy')
56
- @io.string.should include('** Notifying Appsignal of deploy...')
57
- @io.string.should include('** Appsignal has been notified of this '\
58
- 'deploy!')
59
- end
60
- end
61
-
62
- it "should not transmit data" do
63
- @config.find_and_execute_task('appsignal:deploy')
64
- @io.string.should include('** Notifying Appsignal of deploy...')
65
- @io.string.should include('** Something went wrong while trying to '\
66
- 'notify Appsignal:')
67
- end
68
-
69
- context "dry run" do
70
- before(:all) { @config.dry_run = true }
71
-
72
- it "should not send deploy marker" do
73
- @marker.should_not_receive(:transmit)
74
- @config.find_and_execute_task('appsignal:deploy')
75
- @io.string.should include('** Dry run: Deploy marker not actually '\
76
- 'sent.')
77
- end
78
- end
79
- end
80
- end
81
- end