influxdb-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +95 -0
  4. data/README.md +3 -0
  5. data/Rakefile +34 -0
  6. data/config.ru +7 -0
  7. data/gemfiles/Gemfile.rails-3.0.x +8 -0
  8. data/gemfiles/Gemfile.rails-3.0.x.lock +94 -0
  9. data/gemfiles/Gemfile.rails-3.1.x +8 -0
  10. data/gemfiles/Gemfile.rails-3.1.x.lock +121 -0
  11. data/gemfiles/Gemfile.rails-3.2.x +8 -0
  12. data/gemfiles/Gemfile.rails-3.2.x.lock +104 -0
  13. data/gemfiles/Gemfile.rails-4.0.x +8 -0
  14. data/gemfiles/Gemfile.rails-4.0.x.lock +109 -0
  15. data/generators/influxdb/influxdb_generator.rb +48 -0
  16. data/generators/influxdb/templates/initializer.rb +5 -0
  17. data/influxdb-rails.gemspec +33 -0
  18. data/lib/influxdb-rails.rb +82 -0
  19. data/lib/influxdb/rails/air_traffic_controller.rb +39 -0
  20. data/lib/influxdb/rails/backtrace.rb +44 -0
  21. data/lib/influxdb/rails/configuration.rb +117 -0
  22. data/lib/influxdb/rails/exception_presenter.rb +90 -0
  23. data/lib/influxdb/rails/instrumentation.rb +27 -0
  24. data/lib/influxdb/rails/logger.rb +13 -0
  25. data/lib/influxdb/rails/middleware/hijack_render_exception.rb +21 -0
  26. data/lib/influxdb/rails/middleware/hijack_rescue_action_everywhere.rb +32 -0
  27. data/lib/influxdb/rails/rack.rb +28 -0
  28. data/lib/influxdb/rails/rails.rb +39 -0
  29. data/lib/influxdb/rails/railtie.rb +67 -0
  30. data/lib/influxdb/rails/version.rb +5 -0
  31. data/lib/rails/generators/influxdb/influxdb_generator.rb +52 -0
  32. data/lib/rails/generators/influxdb/templates/initializer.rb +4 -0
  33. data/spec/controllers/widgets_controller_spec.rb +15 -0
  34. data/spec/integration/exceptions_spec.rb +37 -0
  35. data/spec/integration/integration_helper.rb +1 -0
  36. data/spec/spec_helper.rb +29 -0
  37. data/spec/suite.sh +42 -0
  38. data/spec/support/rails3/app.rb +24 -0
  39. data/spec/support/rails3/log/test.log +1120 -0
  40. data/spec/support/rails4/app.rb +27 -0
  41. data/spec/unit/backtrace_spec.rb +88 -0
  42. data/spec/unit/configuration_spec.rb +29 -0
  43. data/spec/unit/exception_presenter_spec.rb +69 -0
  44. data/spec/unit/influxdb_rails_spec.rb +67 -0
  45. metadata +226 -0
@@ -0,0 +1,90 @@
1
+ require "base64"
2
+ require "socket"
3
+
4
+ module InfluxDB
5
+ module Rails
6
+ class ExceptionPresenter
7
+ attr_accessor :hash
8
+
9
+ attr_reader :exception
10
+ attr_reader :backtrace
11
+ attr_reader :params
12
+ attr_reader :session_data
13
+ attr_reader :current_user
14
+ attr_reader :controller
15
+ attr_reader :action
16
+ attr_reader :request_url
17
+ attr_reader :referer
18
+ attr_reader :remote_ip
19
+ attr_reader :user_agent
20
+ attr_reader :custom_data
21
+
22
+ def initialize(e, params = {})
23
+ e = e.continued_exception if e.respond_to?(:continued_exception)
24
+ e = e.original_exception if e.respond_to?(:original_exception)
25
+
26
+ @exception = e.is_a?(String) ? Exception.new(e) : e
27
+ @backtrace = InfluxDB::Rails::Backtrace.new(@exception.backtrace)
28
+ @params = params[:params]
29
+ @session_data = params[:session_data] || {}
30
+ @current_user = params[:current_user]
31
+ @controller = params[:controller]
32
+ @action = params[:action]
33
+ @request_url = params[:request_url]
34
+ @user_agent = params[:user_agent]
35
+ @referer = params[:referer]
36
+ @remote_ip = params[:remote_ip]
37
+ @custom_data = params[:custom_data] || {}
38
+ @environment_variables = ENV.to_hash || {}
39
+ @dimensions = {}
40
+ end
41
+
42
+ def context
43
+ c = {
44
+ :time => Time.now.utc.to_i,
45
+ :application_name => InfluxDB::Rails.configuration.application_name,
46
+ :application_root => InfluxDB::Rails.configuration.application_root,
47
+ :framework => InfluxDB::Rails.configuration.framework,
48
+ :framework_version => InfluxDB::Rails.configuration.framework_version,
49
+ :message => @exception.message,
50
+ :backtrace => @backtrace.to_a,
51
+ :language => "Ruby",
52
+ :language_version => "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}",
53
+ :custom_data => @custom_data
54
+ }
55
+
56
+ c[:environment_variables] = @environment_variables.reject do |k,v|
57
+ InfluxDB::Rails.configuration.environment_variable_filters.any? { |filter| k =~ filter }
58
+ end
59
+
60
+ InfluxDB::Rails.configuration.add_custom_exception_data(self)
61
+
62
+ c[:request_data] = request_data if @controller || @action || !@params.blank?
63
+ c
64
+ end
65
+
66
+ def dimensions
67
+ d = {
68
+ :class => @exception.class.to_s,
69
+ :method => "#{@controller}##{@action}",
70
+ :filename => File.basename(@backtrace.lines.first.try(:file)),
71
+ :server => Socket.gethostname,
72
+ :status => "open"
73
+ }.merge(@dimensions)
74
+ end
75
+
76
+ def request_data
77
+ {
78
+ :params => @params,
79
+ :session_data => @session_data,
80
+ :controller => @controller,
81
+ :action => @action,
82
+ :request_url => @request_url,
83
+ :referer => @referer,
84
+ :remote_ip => @remote_ip,
85
+ :user_agent => @user_agent
86
+ }
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,27 @@
1
+ module InfluxDB
2
+ module Rails
3
+ module Instrumentation
4
+ def benchmark_for_instrumentation
5
+ start = Time.now
6
+ yield
7
+
8
+ unless InfluxDB.configuration.ignore_current_environment?
9
+ elapsed = ((Time.now - start) * 1000).ceil
10
+ dimensions = { :method => "#{controller_name}##{action_name}", :server => Socket.gethostname }
11
+ InfluxDB.aggregate "instrumentation", :value => elapsed, :dimensions => dimensions
12
+ end
13
+ end
14
+
15
+ def self.included(base)
16
+ base.extend(ClassMethods)
17
+ end
18
+
19
+ module ClassMethods
20
+ def instrument(methods = [])
21
+ methods = [methods] unless methods.is_a?(Array)
22
+ around_filter :benchmark_for_instrumentation, :only => methods
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ module InfluxDB
2
+ module Rails
3
+ module Logger
4
+ PREFIX = "[InfluxDB::Rails] "
5
+
6
+ private
7
+ def log(level, message)
8
+ return if level != :error && ! InfluxDB::Rails.configuration.debug?
9
+ InfluxDB::Rails.configuration.logger.send(level, PREFIX + message) if InfluxDB.configuration.logger
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ module InfluxDB
2
+ module Rails
3
+ module Middleware
4
+ module HijackRenderException
5
+ def self.included(base)
6
+ base.send(:alias_method_chain, :render_exception, :influxdb)
7
+ end
8
+
9
+ def render_exception_with_influxdb(env, e)
10
+ controller = env["action_controller.instance"]
11
+ request_data = controller.try(:influxdb_request_data) || {}
12
+ unless InfluxDB::Rails.configuration.ignore_user_agent?(request_data[:user_agent])
13
+ InfluxDB::Rails.report_exception_unless_ignorable(e, request_data)
14
+ end
15
+ render_exception_without_influxdb(env, e)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,32 @@
1
+ module InfluxDB
2
+ module Rails
3
+ module Middleware
4
+ module HijackRescueActionEverywhere
5
+ def self.included(base)
6
+ base.send(:alias_method_chain, :rescue_action_in_public, :influxdb)
7
+ base.send(:alias_method_chain, :rescue_action_locally, :influxdb)
8
+ end
9
+
10
+ private
11
+ def rescue_action_in_public_with_influxdb(e)
12
+ handle_exception(e)
13
+ rescue_action_in_public_without_influxdb(e)
14
+ end
15
+
16
+ def rescue_action_locally_with_influxdb(e)
17
+ handle_exception(e)
18
+ rescue_action_locally_without_influxdb(e)
19
+ end
20
+
21
+ def handle_exception(e)
22
+ request_data = influxdb_request_data || {}
23
+
24
+ unless InfluxDB.configuration.ignore_user_agent?(request_data[:user_agent])
25
+ InfluxDB.report_exception_unless_ignorable(e, request_data)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,28 @@
1
+ module InfluxDB
2
+ module Rails
3
+ class Rack
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ dup._call(env)
10
+ end
11
+
12
+ def _call(env)
13
+ begin
14
+ status, headers, body = @app.call(env)
15
+ rescue => e
16
+ InfluxDB.transmit_unless_ignorable(e, env)
17
+ raise(e)
18
+ ensure
19
+ _body = []
20
+ body.each { |line| _body << line } unless body.nil?
21
+ body.close if body.respond_to?(:close)
22
+ end
23
+
24
+ [status, headers, _body]
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ require 'action_controller'
2
+ require 'influxdb'
3
+ require 'influxdb/rails/middleware/hijack_rescue_action_everywhere'
4
+ require 'influxdb/rails/air_traffic_controller'
5
+ require 'influxdb/rails/benchmarking'
6
+ require 'influxdb/rails/instrumentation'
7
+
8
+ module InfluxDB
9
+ module Rails
10
+ def self.initialize
11
+ ActionController::Base.send(:include, InfluxDB::Rails::AirTrafficController)
12
+ ActionController::Base.send(:include, InfluxDB::Rails::Middleware::HijackRescueActionEverywhere)
13
+ ActionController::Base.send(:include, InfluxDB::Rails::Benchmarking)
14
+ ActionController::Base.send(:include, InfluxDB::Rails::Instrumentation)
15
+
16
+ ::Rails.configuration.middleware.insert_after 'ActionController::Failsafe', InfluxDB::Rack
17
+
18
+ InfluxDB.configure(true) do |config|
19
+ config.logger ||= ::Rails.logger
20
+ config.debug = true
21
+ config.environment ||= ::Rails.env
22
+ config.application_root ||= ::Rails.root
23
+ config.application_name ||= "Application"
24
+ config.framework = "Rails"
25
+ config.framework_version = ::Rails.version
26
+ end
27
+
28
+ if defined?(PhusionPassenger)
29
+ PhusionPassenger.on_event(:starting_worker_process) do |forked|
30
+ InfluxDB::Worker.spawn_threads() if forked
31
+ end
32
+ else
33
+ InfluxDB::Worker.spawn_threads()
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ InfluxDB::Rails.initialize
@@ -0,0 +1,67 @@
1
+ require 'influxdb'
2
+ require 'rails'
3
+
4
+ module InfluxDB
5
+ module Rails
6
+ class Railtie < ::Rails::Railtie
7
+ initializer "influxdb.insert_rack_middleware" do |app|
8
+ app.config.middleware.insert 0, InfluxDB::Rails::Rack
9
+ end
10
+
11
+ config.after_initialize do
12
+ InfluxDB::Rails.configure(true) do |config|
13
+ config.logger ||= ::Rails.logger
14
+ config.environment ||= ::Rails.env
15
+ config.application_root ||= ::Rails.root
16
+ config.application_name ||= ::Rails.application.class.parent_name
17
+ config.framework = "Rails"
18
+ config.framework_version = ::Rails.version
19
+ end
20
+
21
+ ActiveSupport.on_load(:action_controller) do
22
+ require 'influxdb/rails/air_traffic_controller'
23
+ include InfluxDB::Rails::AirTrafficController
24
+ require 'influxdb/rails/instrumentation'
25
+ include InfluxDB::Rails::Instrumentation
26
+ end
27
+
28
+ if defined?(::ActionDispatch::DebugExceptions)
29
+ require 'influxdb/rails/middleware/hijack_render_exception'
30
+ ::ActionDispatch::DebugExceptions.send(:include, InfluxDB::Rails::Middleware::HijackRenderException)
31
+ elsif defined?(::ActionDispatch::ShowExceptions)
32
+ require 'influxdb/rails/middleware/hijack_render_exception'
33
+ ::ActionDispatch::ShowExceptions.send(:include, InfluxDB::Rails::Middleware::HijackRenderException)
34
+ end
35
+
36
+ if defined?(ActiveSupport::Notifications)
37
+ ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, start, finish, id, payload|
38
+ if InfluxDB::Rails.configuration.instrumentation_enabled && ! InfluxDB::Rails.configuration.ignore_current_environment?
39
+ timestamp = finish.utc.to_i
40
+ controller_runtime = ((finish - start)*1000).ceil
41
+ view_runtime = (payload[:view_runtime] || 0).ceil
42
+ db_runtime = (payload[:db_runtime] || 0).ceil
43
+ controller_name = payload[:controller]
44
+ action_name = payload[:action]
45
+ hostname = Socket.gethostname
46
+
47
+ InfluxDB::Rails.client.write_point "rails.controllers",
48
+ :value => controller_runtime,
49
+ :method => "#{controller_name}##{action_name}",
50
+ :server => hostname
51
+
52
+ InfluxDB::Rails.client.write_point "rails.views",
53
+ :value => view_runtime,
54
+ :method => "#{controller_name}##{action_name}",
55
+ :server => hostname
56
+
57
+ InfluxDB::Rails.client.write_point "rails.db",
58
+ :value => db_runtime,
59
+ :method => "#{controller_name}##{action_name}",
60
+ :server => hostname
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,5 @@
1
+ module InfluxDB
2
+ module Rails
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,52 @@
1
+ require 'rails/generators'
2
+
3
+ class InfluxDBGenerator < Rails::Generators::Base
4
+ desc "Description:\n This creates a Rails initializer for InfluxDB."
5
+
6
+ begin
7
+ if ARGV.count == 1
8
+ puts "No Application ID provided, contacting InfluxDB API."
9
+ application_name = Rails.application.class.parent_name || "NewApplication"
10
+ api_key = ARGV.first
11
+
12
+ connection = Net::HTTP.new("influxdb.com", 443)
13
+ connection.use_ssl = true
14
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
15
+ url = "/api/v1/applications?api_key=#{api_key}&name=#{application_name}"
16
+ response = connection.post(url, nil)
17
+
18
+ unless response.is_a?(Net::HTTPSuccess)
19
+ raise "The InfluxDB API returned an error: #{response.inspect}"
20
+ end
21
+
22
+ @application = JSON.parse(response.body)
23
+ @application_id = @application["key"]
24
+ else
25
+ @application_id = ARGV[1]
26
+ end
27
+ puts "Received Application ID: #{@application_id}"
28
+
29
+ rescue => e
30
+ puts "We ran into a problem creating your application via the API!"
31
+ puts "If this issue persists, contact us at support@influxdb.com with the following details:"
32
+ puts "#{e.class}: #{e.message}"
33
+ end
34
+
35
+ source_root File.expand_path('../templates', __FILE__)
36
+ argument :api_key,
37
+ :required => true,
38
+ :type => :string,
39
+ :description => "API key for your InfluxDB organization"
40
+ argument :application_id,
41
+ :required => false,
42
+ :default => @application_id,
43
+ :type => :string,
44
+ :description => "Identifier for this application (Leave blank and a new one will be generated for you)"
45
+
46
+ def copy_initializer_file
47
+ template "initializer.rb", "config/initializers/influxdb.rb"
48
+ end
49
+
50
+ def install
51
+ end
52
+ end
@@ -0,0 +1,4 @@
1
+ InfluxDB.configure do |config|
2
+ config.api_key = "<%= api_key %>"
3
+ config.application_id = "<%= application_id %>"
4
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe WidgetsController do
4
+ describe "#new" do
5
+ it "should raise an exception" do
6
+ expect { get :new }.to raise_error(ZeroDivisionError)
7
+ end
8
+ end
9
+
10
+ describe "#index" do
11
+ it "should not raise an exception" do
12
+ expect { get :index }.to_not raise_error
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/integration_helper")
2
+
3
+ describe "exception handling" do
4
+ before do
5
+ InfluxDB::Rails.configure do |config|
6
+ config.ignored_environments = %w{development}
7
+ config.instrumentation_enabled = false
8
+ end
9
+ end
10
+
11
+ describe "in an action that raises an exception" do
12
+ it "should add an exception to the queue" do
13
+ InfluxDB::Rails.client.should_receive(:write_point)
14
+ get "/widgets/new"
15
+ end
16
+ end
17
+
18
+ describe "in an action that does not raise an exception" do
19
+ it "should not add anything to the queue" do
20
+ InfluxDB::Rails.client.should_not_receive(:write_point)
21
+ get "/widgets"
22
+ end
23
+ end
24
+
25
+ describe "for an ignored user agent" do
26
+ it "should not make an HTTP call to the API" do
27
+ InfluxDB::Rails.client.should_not_receive(:write_point)
28
+
29
+ InfluxDB::Rails.configure do |config|
30
+ config.ignored_user_agents = %w{Googlebot}
31
+ end
32
+
33
+ get "/widgets/new", {}, { "HTTP_USER_AGENT" => "Googlebot/2.1" }
34
+ end
35
+ end
36
+ end
37
+