copperegg-apm 1.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +102 -0
  5. data/README.md +181 -0
  6. data/Rakefile +4 -0
  7. data/bin/copperegg-apm-init +45 -0
  8. data/bin/copperegg-apm-methods +27 -0
  9. data/copperegg-apm-1.0.0.pre1.gem +0 -0
  10. data/copperegg-apm.gemspec +34 -0
  11. data/copperegg_apm_test.db +0 -0
  12. data/ext/mkrf_conf.rb +19 -0
  13. data/lib/copperegg-apm.rb +1 -0
  14. data/lib/copperegg/apm.rb +28 -0
  15. data/lib/copperegg/apm/action_controller/base.rb +31 -0
  16. data/lib/copperegg/apm/active_record/connection_adapters/abstract_adapter.rb +35 -0
  17. data/lib/copperegg/apm/benchmark.rb +212 -0
  18. data/lib/copperegg/apm/benchmark_methods_table.rb +65 -0
  19. data/lib/copperegg/apm/configuration.rb +188 -0
  20. data/lib/copperegg/apm/engine.rb +15 -0
  21. data/lib/copperegg/apm/errors.rb +6 -0
  22. data/lib/copperegg/apm/ethon/easy/operations.rb +37 -0
  23. data/lib/copperegg/apm/kernel.rb +19 -0
  24. data/lib/copperegg/apm/middleware.rb +20 -0
  25. data/lib/copperegg/apm/mysql.rb +31 -0
  26. data/lib/copperegg/apm/mysql2/client.rb +35 -0
  27. data/lib/copperegg/apm/net/http.rb +39 -0
  28. data/lib/copperegg/apm/pg/connection.rb +35 -0
  29. data/lib/copperegg/apm/restclient/request.rb +33 -0
  30. data/lib/copperegg/apm/rum.rb +24 -0
  31. data/lib/copperegg/apm/sqlite3/database.rb +35 -0
  32. data/lib/copperegg/apm/tasks.rb +11 -0
  33. data/lib/copperegg/apm/typhoeus/hydra.rb +33 -0
  34. data/lib/copperegg/apm/unbound_method.rb +116 -0
  35. data/lib/copperegg/apm/version.rb +5 -0
  36. data/lib/generators/copperegg/apm/init_generator.rb +20 -0
  37. data/lib/generators/copperegg/apm/templates/config.rb +10 -0
  38. data/performance/mysql2.rb +44 -0
  39. data/screenshot01.png +0 -0
  40. data/spec/action_controller_spec.rb +139 -0
  41. data/spec/apm_spec.rb +330 -0
  42. data/spec/ethon_spec.rb +73 -0
  43. data/spec/helpers/mysql2_setup.rb +51 -0
  44. data/spec/helpers/mysql_setup.rb +45 -0
  45. data/spec/helpers/pg_setup.rb +43 -0
  46. data/spec/helpers/rails.rb +18 -0
  47. data/spec/helpers/sqlite3_setup.rb +30 -0
  48. data/spec/kernel_spec.rb +37 -0
  49. data/spec/mysql2_spec.rb +58 -0
  50. data/spec/mysql_spec.rb +66 -0
  51. data/spec/net_http_spec.rb +52 -0
  52. data/spec/pg_spec.rb +45 -0
  53. data/spec/restclient_spec.rb +67 -0
  54. data/spec/rum_spec.rb +23 -0
  55. data/spec/spec_helper.rb +12 -0
  56. data/spec/sqlite3_spec.rb +45 -0
  57. data/spec/typhoeus_spec.rb +66 -0
  58. metadata +316 -0
@@ -0,0 +1,15 @@
1
+ module CopperEgg
2
+ module APM
3
+ class Engine < ::Rails::Engine
4
+ initializer "copperegg_apm.helpers" do
5
+ ActiveSupport.on_load_all do
6
+ helper CopperEgg::APM::Rum
7
+ end
8
+ end
9
+
10
+ rake_tasks do
11
+ load 'copperegg/apm/tasks.rb'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ module CopperEgg
2
+ module APM
3
+ class ConfigurationError < Exception; end
4
+ class PayloadError < Exception; end
5
+ end
6
+ end
@@ -0,0 +1,37 @@
1
+ module CopperEgg
2
+ module APM
3
+ module Ethon
4
+ module Easy
5
+ module Operations
6
+ def perform_with_ce_instrumentation
7
+ if CopperEgg::APM::Configuration.benchmark_http?
8
+ x = url.gsub(/\/\/[^:]+:[^@]@/,"//").gsub(/\?.*/,"")
9
+ starttime = (Time.now.to_f * 1000.0).to_i
10
+ result = perform_without_ce_instrumentation
11
+ time = (Time.now.to_f * 1000.0).to_i - starttime
12
+
13
+ CopperEgg::APM.send_payload(:url => x, :time => time)
14
+
15
+ result
16
+ else
17
+ perform_without_ce_instrumentation
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ if defined?(::Ethon::Easy::Operations)
27
+
28
+ module Ethon
29
+ class Easy
30
+ module Operations
31
+ include CopperEgg::APM::Ethon::Easy::Operations
32
+ alias_method :perform_without_ce_instrumentation, :perform
33
+ alias_method :perform, :perform_with_ce_instrumentation
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,19 @@
1
+ module CopperEgg
2
+ module APM
3
+ module Kernel
4
+ alias_method :raise_without_ce_instrumentation, :raise
5
+
6
+ def raise(*args)
7
+ super(ArgumentError, "wrong number of arguments", caller) if args.size > 3
8
+ CopperEgg::APM.capture_exception(*args) if CopperEgg::APM::Configuration.benchmark_exceptions?
9
+ raise_without_ce_instrumentation(*args)
10
+ end
11
+
12
+ alias_method :fail, :raise
13
+ end
14
+ end
15
+ end
16
+
17
+ Object.class_eval do
18
+ include CopperEgg::APM::Kernel
19
+ end
@@ -0,0 +1,20 @@
1
+ module CopperEgg
2
+ module APM
3
+ class Middleware
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ CopperEgg::APM.benchmark(:url => env['REQUEST_URI'].gsub(/\/\/[^:]+:[^@]@/,"//").gsub(/\?.*/,"")) do
10
+ @status, @headers, @response = @app.call(env)
11
+ end
12
+ [@status, @headers, self]
13
+ end
14
+
15
+ def each(&block)
16
+ @response.each(&block)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ module CopperEgg
2
+ module APM
3
+ module Mysql
4
+ def query_with_ce_instrumentation(*args)
5
+ if CopperEgg::APM::Configuration.benchmark_sql?
6
+ starttime = (Time.now.to_f * 1000.0).to_i
7
+ result = query_without_ce_instrumentation(*args)
8
+ time = (Time.now.to_f * 1000.0).to_i - starttime
9
+
10
+ return result if args.first =~ /\A\s*(begin|commit|rollback|set)/i
11
+
12
+ CopperEgg::APM.send_payload(:sql => CopperEgg::APM.obfuscate_sql(args.first), :time => time)
13
+
14
+ result
15
+ else
16
+ query_without_ce_instrumentation(*args)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ if defined?(::Mysql)
24
+
25
+ class Mysql
26
+ include CopperEgg::APM::Mysql
27
+ alias_method :query_without_ce_instrumentation, :query
28
+ alias_method :query, :query_with_ce_instrumentation
29
+ end
30
+
31
+ end
@@ -0,0 +1,35 @@
1
+ module CopperEgg
2
+ module APM
3
+ module Mysql2
4
+ module Client
5
+ def query_with_ce_instrumentation(*args)
6
+ if CopperEgg::APM::Configuration.benchmark_sql?
7
+ starttime = (Time.now.to_f * 1000.0).to_i
8
+ result = query_without_ce_instrumentation(*args)
9
+ time = (Time.now.to_f * 1000.0).to_i - starttime
10
+
11
+ return result if args.first =~ /\A\s*(begin|commit|rollback|set)/i
12
+
13
+ CopperEgg::APM.send_payload(:sql => CopperEgg::APM.obfuscate_sql(args.first), :time => time)
14
+
15
+ result
16
+ else
17
+ query_without_ce_instrumentation(*args)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ if defined?(::Mysql2::Client)
26
+
27
+ module Mysql2
28
+ class Client
29
+ include CopperEgg::APM::Mysql2::Client
30
+ alias_method :query_without_ce_instrumentation, :query
31
+ alias_method :query, :query_with_ce_instrumentation
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,39 @@
1
+ module CopperEgg
2
+ module APM
3
+ module Net
4
+ module HTTP
5
+ def request_with_ce_instrumentation(req, body=nil, &block)
6
+ if CopperEgg::APM::Configuration.benchmark_http?
7
+ starttime = (Time.now.to_f * 1000.0).to_i
8
+ result = request_without_ce_instrumentation(req, body, &block)
9
+ time = (Time.now.to_f * 1000.0).to_i - starttime
10
+ url = "http#{"s" if @use_ssl}://#{address}#{":#{port}" if port != ::Net::HTTP.default_port}#{req.path.sub(/\?.*/,"")}"
11
+
12
+ CopperEgg::APM.send_payload(:url => url, :time => time)
13
+
14
+ result
15
+ else
16
+ request_without_ce_instrumentation(req, body, &block)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ begin
25
+ require 'net/http'
26
+ rescue LoadError
27
+ end
28
+
29
+ if defined?(::Net::HTTP)
30
+
31
+ module Net
32
+ class HTTP
33
+ include CopperEgg::APM::Net::HTTP
34
+ alias_method :request_without_ce_instrumentation, :request
35
+ alias_method :request, :request_with_ce_instrumentation
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,35 @@
1
+ module CopperEgg
2
+ module APM
3
+ module PG
4
+ module Connection
5
+ def exec_with_ce_instrumentation(*args)
6
+ if CopperEgg::APM::Configuration.benchmark_sql?
7
+ starttime = (Time.now.to_f * 1000.0).to_i
8
+ result = block_given? ? exec_without_ce_instrumentation(*args, &Proc.new) : exec_without_ce_instrumentation(*args)
9
+ time = (Time.now.to_f * 1000.0).to_i - starttime
10
+
11
+ return result if args.first =~ /\A\s*(begin|commit|rollback|set)/i
12
+
13
+ CopperEgg::APM.send_payload(:sql => CopperEgg::APM.obfuscate_sql(args.first), :time => time)
14
+
15
+ result
16
+ else
17
+ block_given? ? exec_without_ce_instrumentation(*args, &Proc.new) : exec_without_ce_instrumentation(*args)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ if defined?(::PG::Connection)
26
+
27
+ module PG
28
+ class Connection
29
+ include CopperEgg::APM::PG::Connection
30
+ alias_method :exec_without_ce_instrumentation, :exec
31
+ alias_method :exec, :exec_with_ce_instrumentation
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,33 @@
1
+ module CopperEgg
2
+ module APM
3
+ module RestClient
4
+ module Request
5
+ def execute_with_ce_instrumentation(&block)
6
+ if CopperEgg::APM::Configuration.benchmark_http?
7
+ starttime = (Time.now.to_f * 1000.0).to_i
8
+ result = execute_without_ce_instrumentation(&block)
9
+ time = (Time.now.to_f * 1000.0).to_i - starttime
10
+
11
+ CopperEgg::APM.send_payload(:url => url.gsub(/\/\/[^:]+:[^@]@/,"//").gsub(/\?.*/,""), :time => time)
12
+
13
+ result
14
+ else
15
+ execute_without_ce_instrumentation(&block)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ if defined?(::RestClient::Request)
24
+
25
+ module RestClient
26
+ class Request
27
+ include CopperEgg::APM::RestClient::Request
28
+ alias_method :execute_without_ce_instrumentation, :execute
29
+ alias_method :execute, :execute_with_ce_instrumentation
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,24 @@
1
+ module CopperEgg
2
+ module APM
3
+ module Rum
4
+ def real_user_monitoring_javascript_tag
5
+ script = %Q(
6
+ <script type="text/javascript">
7
+ var BACON = BACON || {};
8
+
9
+ BACON.id = '#{CopperEgg::APM::Configuration.instrument_key}';
10
+ BACON.short_url = #{CopperEgg::APM::Configuration.rum_short_url};
11
+ BACON.beaconUrl = '#{CopperEgg::APM::Configuration.rum_beacon_url}';
12
+
13
+ BACON.starttime = new Date().getTime();
14
+ (function(d, s) { var js = d.createElement(s);
15
+ js.async = true; js.src = "http://cdn.copperegg.com/rum/bacon.min.js";
16
+ var s = d.getElementsByTagName(s)[0]; s.parentNode.insertBefore(js, s);
17
+ })(document, "script");
18
+ </script>
19
+ )
20
+ script.respond_to?(:html_safe) ? script.html_safe : script
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,35 @@
1
+ module CopperEgg
2
+ module APM
3
+ module SQLite3
4
+ module Database
5
+ def execute_with_ce_instrumentation(sql, bind_vars = [], *args, &block)
6
+ if CopperEgg::APM::Configuration.benchmark_sql?
7
+ starttime = (Time.now.to_f * 1000.0).to_i
8
+ result = execute_without_ce_instrumentation(sql, bind_vars, *args, &block)
9
+ time = (Time.now.to_f * 1000.0).to_i - starttime
10
+
11
+ return result if sql =~ /\A\s*(begin|commit|rollback|set)/i
12
+
13
+ CopperEgg::APM.send_payload(:sql => CopperEgg::APM.obfuscate_sql(sql), :time => time)
14
+
15
+ result
16
+ else
17
+ execute_without_ce_instrumentation(sql, bind_vars, *args, &block)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ if defined?(::SQLite3::Database)
26
+
27
+ module SQLite3
28
+ class Database
29
+ include CopperEgg::APM::SQLite3::Database
30
+ alias_method :execute_without_ce_instrumentation, :execute
31
+ alias_method :execute, :execute_with_ce_instrumentation
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,11 @@
1
+ require 'rake'
2
+ require File.join(File.dirname(__FILE__), 'benchmark_methods_table')
3
+
4
+ namespace :copperegg do
5
+ namespace :apm do
6
+ desc "Prints a summary of all methods defined in your project and whether or not they are benchmarked."
7
+ task :methods => :environment do
8
+ CopperEgg::APM::BenchmarkMethodsTable.new.print_table
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ module CopperEgg
2
+ module APM
3
+ module Typhoeus
4
+ module Hydra
5
+ def handle_request_with_ce_instrumentation(request, response, live_request=true)
6
+ if CopperEgg::APM::Configuration.benchmark_http?
7
+ starttime = (Time.now.to_f * 1000.0).to_i
8
+ result = handle_request_without_ce_instrumentation(request, response, live_request)
9
+ time = (Time.now.to_f * 1000.0).to_i - starttime
10
+
11
+ CopperEgg::APM.send_payload(:url => request.url.gsub(/\/\/[^:]+:[^@]@/,"//").gsub(/\?.*/,""), :time => time)
12
+
13
+ result
14
+ else
15
+ handle_request_without_ce_instrumentation(request, response, live_request)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ if defined?(::Typhoeus::Hydra) && defined?(::Typhoeus::VERSION) && ::Typhoeus::VERSION =~ /\A0\.3\.?/
24
+
25
+ module Typhoeus
26
+ class Hydra
27
+ include CopperEgg::APM::Typhoeus::Hydra
28
+ alias_method :handle_request_without_ce_instrumentation, :handle_request
29
+ alias_method :handle_request, :handle_request_with_ce_instrumentation
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,116 @@
1
+ module CopperEgg
2
+ module APM
3
+ module UnboundMethod
4
+ def class_method?
5
+ self.class_method == true
6
+ end
7
+
8
+ def benchmarkable?
9
+ return false if !user_defined? || attribute?
10
+ name =~ /\A[\w\d\!\?]+\z/ ? true : false
11
+ end
12
+
13
+ def user_defined?
14
+ return false if source_filename.nil? || !File.exists?(source_filename)
15
+ paths = Gem.path + [Gem.dir, CopperEgg::APM::Configuration.gem_root, "/lib/ruby/", "/site_ruby/", "/vendor_ruby/", "/gems/"]
16
+ source_filename ? paths.select! {|path| source_filename.include?(path) }.empty? : false
17
+ end
18
+
19
+ def attribute?
20
+ parent_class.instance_methods.include?("#{name}=".to_sym)
21
+ end
22
+
23
+ def source_filename
24
+ @source_filename ||= if respond_to?(:source_location)
25
+ source_location.to_a.first
26
+ elsif respond_to?(:__file__)
27
+ __file__
28
+ end
29
+ end
30
+
31
+ def source_line
32
+ @source_line ||= if respond_to?(:source_location)
33
+ source_location.to_a.last
34
+ elsif respond_to?(:__line__)
35
+ __line__
36
+ end
37
+ end
38
+
39
+ def display_filename
40
+ return @display_filename if @display_filename
41
+ @display_filename = "#{source_filename}:#{source_line}"
42
+ @display_filename.sub! CopperEgg::APM::Configuration.app_root, "..."
43
+ end
44
+
45
+ def display_name
46
+ "#{parent_class}#{class_method? ? "." : "#"}#{name}"
47
+ end
48
+
49
+ def class_eval_string
50
+ <<-DEF
51
+ #{"class << self" if class_method?}
52
+ alias_method "_cu_#{name}", "#{name}"
53
+ def #{name}(*args)
54
+ starttime = (Time.now.to_f * 1000.0).to_i
55
+ result = block_given? ? _cu_#{name}(*args,&Proc.new) : _cu_#{name}(*args)
56
+ time = (Time.now.to_f * 1000.0).to_i - starttime
57
+
58
+ CopperEgg::APM.send_payload({:method => "#{display_name}", :time => time})
59
+
60
+ result
61
+ end
62
+ #{"end" if class_method?}
63
+ DEF
64
+ end
65
+
66
+ def add_benchmarking
67
+ parent_class.module_eval(class_eval_string)
68
+ end
69
+
70
+ def benchmark_levels
71
+ levels = [:full, :custom]
72
+ return levels if !defined?(::Rails)
73
+
74
+ if parent_class < ::ActiveRecord::Base
75
+ levels << :moderate
76
+ end
77
+ if parent_class < ::ActionController::Base
78
+ levels << :basic
79
+ end
80
+ levels
81
+ end
82
+
83
+ def excluded?
84
+ return true if CopperEgg::APM::Configuration.benchmark_methods_level == :disabled
85
+
86
+ return false if CopperEgg::APM::Configuration.only_methods.include?(parent_class.to_s)
87
+ return false if CopperEgg::APM::Configuration.only_methods.include?(display_name)
88
+ return false if CopperEgg::APM::Configuration.only_methods.include?(name)
89
+
90
+ if CopperEgg::APM::Configuration.only_methods.length > 0
91
+ return true if !CopperEgg::APM::Configuration.only_methods.include?(parent_class.to_s) && !CopperEgg::APM::Configuration.only_methods.include?(display_name) && !CopperEgg::APM::Configuration.only_methods.include?(name)
92
+ end
93
+
94
+ return true if CopperEgg::APM::Configuration.exclude_methods.include?(parent_class.to_s)
95
+ return true if CopperEgg::APM::Configuration.exclude_methods.include?(display_name)
96
+ return true if CopperEgg::APM::Configuration.exclude_methods.include?(display_name)
97
+
98
+ return false if CopperEgg::APM::Configuration.include_methods.include?(parent_class.to_s)
99
+ return false if CopperEgg::APM::Configuration.include_methods.include?(display_name)
100
+ return false if CopperEgg::APM::Configuration.include_methods.include?(name)
101
+
102
+ return true if name =~ /\A_/
103
+ return true if name =~ /\A[\w\d]+\?\z/
104
+
105
+ return false if benchmark_levels.include?(CopperEgg::APM::Configuration.benchmark_methods_level)
106
+
107
+ true
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ class UnboundMethod
114
+ include CopperEgg::APM::UnboundMethod
115
+ attr_accessor :parent_class, :class_method
116
+ end