copperegg-apm 1.0.0.pre1

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 (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