appsignal 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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,19 @@
1
+ *.rbc
2
+ *.sassc
3
+ .sass-cache
4
+ capybara-*.html
5
+ .rspec
6
+ /log/*.log
7
+ /.bundle
8
+ /vendor/bundle
9
+ /tmp/*
10
+ /db/*.sqlite3
11
+ /public/system/*
12
+ /coverage/
13
+ /spec/tmp/*
14
+ **.orig
15
+ rerun.txt
16
+ pickle-email-*.html
17
+ *.gem
18
+ Gemfile.lock
19
+ .DS_Store
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3
@@ -0,0 +1,30 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ # - 1.9.2
5
+ - 1.9.3
6
+ - 1.8.7
7
+ - ree
8
+ # - ruby-head
9
+ # - jruby-18mode
10
+ - jruby-19mode
11
+ # - jruby-head
12
+
13
+ gemfile:
14
+ - gemfiles/3.0.gemfile
15
+ - gemfiles/3.1.gemfile
16
+ - gemfiles/3.2.gemfile
17
+ - gemfiles/edge.gemfile
18
+
19
+ matrix:
20
+ exclude:
21
+ - rvm: 1.8.7
22
+ gemfile: gemfiles/edge.gemfile
23
+ allow_failures:
24
+ - gemfile: gemfiles/edge.gemfile
25
+
26
+ script: RAILS_ENV=test bundle exec rspec spec
27
+
28
+ notifications:
29
+ campfire: 80beans:d43f43244263cafb088182aa79e7d96419da0372@188927
30
+ email: false
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENCE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 80beans
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,48 @@
1
+ appsignal
2
+ =================
3
+
4
+
5
+ ## Pull requests / issues
6
+
7
+ New features should be made in an issue or pullrequest. Title format is as follows:
8
+
9
+
10
+ name [request_count]
11
+
12
+ example
13
+
14
+ tagging [2]
15
+
16
+ ## Event payload sanitizer
17
+ Appsignal logs Rails
18
+ [ActiveSupport::Notification](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html)-events
19
+ to appsignal.com over SSL. These events contain basic metadata such as a name
20
+ and timestamps, and additional 'payload' log data. By default,
21
+ appsignal will transmit all payload data. If you want to restrict the amount of
22
+ payload data that gets sent to <https://appsignal.com>, you can define your own
23
+ event payload sanitizer in `config/environment/my_env.rb`. The
24
+ `event_payload_sanitizer` needs to be a callable object that returns a
25
+ JSON-serializable hash.
26
+
27
+ ### Examples
28
+
29
+ #### Pass through the entire payload unmodified (default)
30
+ ```ruby
31
+ Appsignal.event_payload_sanitizer = proc { |event| event.payload }
32
+ ```
33
+
34
+ #### Delete the entire event payload
35
+ ```ruby
36
+ Appsignal.event_payload_sanitizer = proc { {} }
37
+ ```
38
+
39
+ #### Conditional modification of the payload
40
+ ```ruby
41
+ Appsignal.event_payload_sanitizer = proc do |event|
42
+ if event.name == 'interesting'
43
+ event.payload
44
+ else
45
+ {}
46
+ end
47
+ end
48
+ ```
@@ -0,0 +1,52 @@
1
+ task :publish do
2
+ NAME = 'appsignal'
3
+ VERSION_FILE = 'lib/appsignal/version.rb'
4
+
5
+ raise '$EDITOR should be set' unless ENV['EDITOR']
6
+
7
+ def build_and_push_gem
8
+ puts '# Building gem'
9
+ puts `gem build #{NAME}.gemspec`
10
+ puts '# Publishing Gem'
11
+ puts `gem push #{NAME}-#{gem_version}.gem`
12
+ end
13
+
14
+ def create_and_push_tag
15
+ begin
16
+ puts `git commit -am 'Bump to #{version} [ci skip]'`
17
+ puts "# Creating tag #{version}"
18
+ puts `git tag #{version}`
19
+ puts `git push origin #{version}`
20
+ puts `git push origin master`
21
+ rescue
22
+ raise "Tag: '#{version}' already exists"
23
+ end
24
+ end
25
+
26
+ def changes
27
+ git_status_to_array(`git status -s -u `)
28
+ end
29
+
30
+ def gem_version
31
+ Appsignal::VERSION
32
+ end
33
+
34
+ def version
35
+ @version ||= 'v' << gem_version
36
+ end
37
+
38
+ def git_status_to_array(changes)
39
+ changes.split("\n").each { |change| change.gsub!(/^.. /,'') }
40
+ end
41
+
42
+ raise "Branch should hold no uncommitted file change)" unless changes.empty?
43
+
44
+ system("$EDITOR #{VERSION_FILE}")
45
+ if changes.member?(VERSION_FILE)
46
+ load File.expand_path(VERSION_FILE)
47
+ build_and_push_gem
48
+ create_and_push_tag
49
+ else
50
+ raise "Actually change the version in: #{VERSION_FILE}"
51
+ end
52
+ end
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/appsignal/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = [
6
+ 'Robert Beekman',
7
+ 'Steven Weller',
8
+ 'Thijs Cadier',
9
+ 'Ron Cadier',
10
+ 'Jacob Vosmaer'
11
+ ]
12
+ gem.email = ['contact@appsignal.com']
13
+ gem.description = 'The official appsignal.com gem'
14
+ gem.summary = 'Logs performance and exception data from your app to'\
15
+ 'appsignal.com'
16
+ gem.homepage = 'http://github.com/80beans/appsignal'
17
+
18
+ gem.files = `git ls-files`.split($\)
19
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
20
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
21
+ gem.name = 'appsignal'
22
+ gem.require_paths = ['lib']
23
+ gem.version = Appsignal::VERSION
24
+
25
+ gem.add_dependency 'rails', '~>3'
26
+ gem.add_dependency 'rake'
27
+ gem.add_dependency 'json'
28
+ gem.add_dependency 'rack'
29
+
30
+ gem.add_development_dependency 'rspec'
31
+ gem.add_development_dependency 'capistrano'
32
+ gem.add_development_dependency 'generator_spec'
33
+ end
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..','lib'))
4
+ require 'appsignal/cli'
5
+
6
+ begin
7
+ Appsignal::CLI.run
8
+ rescue => e
9
+ raise e if $DEBUG
10
+ STDERR.puts e.message
11
+ STDERR.puts e.backtrace.join("\n")
12
+ exit 1
13
+ end
@@ -0,0 +1,8 @@
1
+ development: &development
2
+ endpoint: "http://localhost:3000/1"
3
+ api_key: "abc"
4
+ active: true
5
+ production:
6
+ <<: *development
7
+ test:
8
+ <<: *development
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+ source :rubygems
3
+
4
+ gem 'rails', '~> 3.0.17'
5
+ gem 'rspec'
6
+ gem 'rake'
7
+ gem 'json'
8
+
9
+ group :development, :test do
10
+ gem 'capistrano'
11
+ gem 'generator_spec'
12
+ end
13
+
14
+ platforms :jruby do
15
+ gem 'jruby-openssl'
16
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+ source :rubygems
3
+
4
+ gem 'rails', '~> 3.1.8'
5
+ gem 'rspec'
6
+ gem 'rake'
7
+ gem 'json'
8
+
9
+ group :development, :test do
10
+ gem 'capistrano'
11
+ gem 'generator_spec'
12
+ end
13
+
14
+ platforms :jruby do
15
+ gem 'jruby-openssl'
16
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+ source :rubygems
3
+
4
+ gem 'rails', '~> 3.2.8'
5
+ gem 'rspec'
6
+ gem 'rake'
7
+ gem 'json'
8
+
9
+ group :development, :test do
10
+ gem 'capistrano'
11
+ gem 'generator_spec'
12
+ end
13
+
14
+ platforms :jruby do
15
+ gem 'jruby-openssl'
16
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+ source :rubygems
3
+
4
+ gem 'rails', :git => 'git://github.com/rails/rails.git'
5
+ gem 'rspec'
6
+ gem 'rake'
7
+ gem 'json'
8
+
9
+ group :development, :test do
10
+ gem 'capistrano'
11
+ gem 'generator_spec'
12
+ end
13
+
14
+ platforms :jruby do
15
+ gem 'jruby-openssl'
16
+ end
@@ -0,0 +1,45 @@
1
+ raise 'This appsignal gem only works with rails' unless defined?(Rails)
2
+
3
+ module Appsignal
4
+ class << self
5
+ attr_accessor :subscriber, :event_payload_sanitizer
6
+
7
+ def active?
8
+ config && config[:active] == true
9
+ end
10
+
11
+ def logger
12
+ @logger ||= Logger.new("#{Rails.root}/log/appsignal.log").tap do |l|
13
+ l.level = Logger::INFO
14
+ end
15
+ end
16
+
17
+ def transactions
18
+ @transactions ||= {}
19
+ end
20
+
21
+ def agent
22
+ @agent ||= Appsignal::Agent.new
23
+ end
24
+
25
+ def config
26
+ @config ||= Appsignal::Config.new(Rails.root, Rails.env).load
27
+ end
28
+
29
+ def event_payload_sanitizer
30
+ @event_payload_sanitizer ||= proc { |event| event.payload }
31
+ end
32
+ end
33
+ end
34
+
35
+ require 'appsignal/cli'
36
+ require 'appsignal/config'
37
+ require 'appsignal/transmitter'
38
+ require 'appsignal/agent'
39
+ require 'appsignal/marker'
40
+ require 'appsignal/middleware'
41
+ require 'appsignal/transaction'
42
+ require 'appsignal/exception_notification'
43
+ require 'appsignal/auth_check'
44
+ require 'appsignal/version'
45
+ require 'appsignal/railtie'
@@ -0,0 +1,104 @@
1
+ module Appsignal
2
+ class Agent
3
+ attr_reader :queue, :active, :sleep_time, :slowest_transactions, :transmitter
4
+ ACTION = 'log_entries'
5
+
6
+ def initialize
7
+ return unless Appsignal.active?
8
+ @sleep_time = 60.0
9
+ @slowest_transactions = {}
10
+ @queue = []
11
+ @retry_request = true
12
+ @thread = Thread.new do
13
+ while true do
14
+ send_queue if @queue.any?
15
+ sleep @sleep_time
16
+ end
17
+ end
18
+ @transmitter = Transmitter.new(
19
+ Appsignal.config[:endpoint],
20
+ ACTION,
21
+ Appsignal.config[:api_key]
22
+ )
23
+ Appsignal.logger.info 'Started the Appsignal agent'
24
+ end
25
+
26
+ def add_to_queue(transaction)
27
+ if !transaction.exception? && transaction.action
28
+ current_slowest_transaction = @slowest_transactions[transaction.action]
29
+ if current_slowest_transaction
30
+ if current_slowest_transaction.process_action_event.duration <
31
+ transaction.process_action_event.duration
32
+ current_slowest_transaction.clear_payload_and_events!
33
+ @slowest_transactions[transaction.action] = transaction
34
+ else
35
+ transaction.clear_payload_and_events!
36
+ end
37
+ else
38
+ @slowest_transactions[transaction.action] = transaction
39
+ end
40
+ end
41
+ @queue << transaction
42
+ end
43
+
44
+ def send_queue
45
+ Appsignal.logger.debug "Sending queue"
46
+ begin
47
+ handle_result transmitter.transmit(queue.map(&:to_hash))
48
+ rescue Exception => ex
49
+ Appsignal.logger.error "Exception while communicating with AppSignal: #{ex}"
50
+ handle_result nil
51
+ end
52
+ end
53
+
54
+ def handle_result(code)
55
+ Appsignal.logger.debug "Queue sent, response code: #{code}"
56
+ case code.to_i
57
+ when 200
58
+ good_response
59
+ when 420 # Enhance Your Calm
60
+ good_response
61
+ @sleep_time = @sleep_time * 1.5
62
+ when 413 # Request Entity Too Large
63
+ good_response
64
+ @sleep_time = @sleep_time / 1.5
65
+ when 429
66
+ Appsignal.logger.error "Too many requests sent, disengaging the agent"
67
+ stop_logging
68
+ when 406
69
+ Appsignal.logger.error "Your appsignal gem cannot communicate with the API anymore, please upgrade. Disengaging the agent"
70
+ stop_logging
71
+ when 402
72
+ Appsignal.logger.error "Payment required, disengaging the agent"
73
+ stop_logging
74
+ when 401
75
+ Appsignal.logger.error "API token cannot be authorized, disengaging the agent"
76
+ stop_logging
77
+ else
78
+ retry_once
79
+ end
80
+ end
81
+
82
+ protected
83
+
84
+ def good_response
85
+ @queue = []
86
+ @slowest_transactions = {}
87
+ @retry_request = true
88
+ end
89
+
90
+ def retry_once
91
+ if @retry_request
92
+ @retry_request = false
93
+ else
94
+ @retry_request = true
95
+ @queue = []
96
+ end
97
+ end
98
+
99
+ def stop_logging
100
+ ActiveSupport::Notifications.unsubscribe(Appsignal.subscriber)
101
+ Thread.kill(@thread)
102
+ end
103
+ end
104
+ end