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,5 @@
1
+ module CopperEgg
2
+ module APM
3
+ GEM_VERSION = '1.0.0.pre1'
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ module Copperegg
2
+ module Apm
3
+ module Generators
4
+ class InitGenerator < Rails::Generators::Base
5
+ source_root File.expand_path("../templates", __FILE__)
6
+ desc "Creates an initializer file at config/initializers/copperegg_apm_config.rb"
7
+
8
+ def create_initializer
9
+ template "config.rb", "#{Rails.root}/config/initializers/copperegg_apm_config.rb", :verbose => true
10
+ end
11
+
12
+ private
13
+
14
+ def instrument_key
15
+ @instrument_key = ask("Enter your app key:")
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ CopperEgg::APM.configure do |config|
2
+ config.instrument_key = "<%= instrument_key %>"
3
+ config.benchmark_sql = true
4
+ config.benchmark_exceptions = true
5
+ config.benchmark_http = true
6
+ config.benchmark_methods :disabled # To enable, set to :basic, :moderate, or :full
7
+ # Below are examples of customizing method benchmarking
8
+ # config.benchmark_methods :basic, :exclude => %w(UsersController#new UsersController#edit), :include => %w(User UserRole perform)
9
+ # config.benchmark_methods :custom, %w(User Client index ClientsController#create)
10
+ end
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../spec/helpers/mysql2_setup'
3
+ require 'copperegg/apm'
4
+ require 'benchmark'
5
+
6
+ CopperEgg::APM.configure do |config|
7
+ config.instrument_key = "key"
8
+ config.benchmark_sql = false
9
+ end
10
+
11
+ client = Mysql2::Client.new :host => "localhost", :database => "copperegg_apm_test", :username => ENV["MYSQL_USER"]
12
+ sql = "UPDATE `users` SET `details` = '512.777.9311', `updated_at` = '#{Time.now.strftime('%Y-%m-%d %H:%M%S')}' WHERE `users`.`id` = 1"
13
+ n = 10000
14
+
15
+ puts "\nFor \"#{sql}\"\n\n"
16
+
17
+ Benchmark.bm(31) do |x|
18
+ x.report("#{n} queries w/o instrumentation") { n.times { client.query(sql) } }
19
+ end
20
+
21
+ puts
22
+
23
+ CopperEgg::APM.configure do |config|
24
+ config.instrument_key = "key"
25
+ config.benchmark_sql = true
26
+ end
27
+
28
+ Benchmark.bm(31) do |x|
29
+ x.report("#{n} queries w/ sql obfuscation") { n.times { client.query(sql) } }
30
+ end
31
+
32
+ puts
33
+
34
+ # class String
35
+ # def bytesize
36
+ # 1025
37
+ # end
38
+ # end
39
+
40
+ Benchmark.bm(31) do |x|
41
+ x.report("#{n} queries w/o sql obfuscation") { n.times { client.query(sql) } }
42
+ end
43
+
44
+ puts
data/screenshot01.png ADDED
Binary file
@@ -0,0 +1,139 @@
1
+ # Props to @relishapp (http://www.relishapp.com/rspec/rspec-rails/docs/controller-specs/anonymous-controller) and
2
+ # @AlexandrZaytsev (http://say26.com/rspec-testing-controllers-outside-of-a-rails-application) for their helpful blog posts
3
+ require File.dirname(__FILE__) + '/helpers/rails'
4
+ require 'spec_helper'
5
+
6
+ RSpec.configure do |c|
7
+ c.infer_base_class_for_anonymous_controllers = true
8
+ end
9
+
10
+ class ApplicationController < ActionController::Base
11
+ include Rails.application.routes.url_helpers
12
+ end
13
+
14
+ class ExceptionController < ApplicationController; end
15
+
16
+ class BenchmarkAllActionsController < ApplicationController; end
17
+
18
+ class BenchmarkOnlyActionsController < ApplicationController; end
19
+
20
+ class BenchmarkExceptActionsController < ApplicationController; end
21
+
22
+ describe ExceptionController, :type => :controller do
23
+ controller(ExceptionController) do
24
+ def index
25
+ @count = 1/0
26
+ end
27
+ end
28
+
29
+ it "should instrument all exceptions not rescued" do
30
+ expect { get :index, {:id => 1, :sort => "name"} }.to raise_error
31
+
32
+ last_payload = CopperEgg::APM.send(:class_variable_get, :@@payload_cache).split("\x00").select {|i| i.size > 2}.map {|i| i.sub(/^[^\{]+/,'')}.last
33
+ hash = JSON.parse last_payload
34
+
35
+ expect(hash.keys.sort).to eq ["excp", "id"]
36
+ expect(hash["id"]).to match(/\A[0-1a-z]{16}\z/i)
37
+ expect(hash["excp"].keys.sort).to eq ["error", "stacktrace", "ts"]
38
+ expect(hash["excp"]["error"]).to match(/\AZeroDivisionError\|/)
39
+ expect(hash["excp"]["error"]).to match(/\{Ruby\}\Z/)
40
+ expect(hash["excp"]["stacktrace"]).to match(/\Adivided by 0\n/)
41
+ expect(hash["excp"]["ts"]).to be_an_instance_of(Fixnum)
42
+ end
43
+ end
44
+
45
+ describe BenchmarkAllActionsController, :type => :controller do
46
+ controller(BenchmarkAllActionsController) do
47
+ add_benchmarks
48
+
49
+ def index
50
+ 100.times.reduce(:+)
51
+ render :nothing => true
52
+ end
53
+ alias_method :create, :index
54
+ end
55
+
56
+ it "should benchmark all actions" do
57
+ get :index
58
+
59
+ last_payload = CopperEgg::APM.send(:class_variable_get, :@@payload_cache).split("\x00").select {|i| i.size > 2}.map {|i| i.sub(/^[^\{]+/,'')}.last
60
+ hash = JSON.parse last_payload
61
+
62
+ expect(hash.keys.sort).to eq ["id", "inst"]
63
+ expect(hash["id"]).to match(/\A[0-1a-z]{16}\z/i)
64
+ expect(hash["inst"].keys.sort).to eq ["method", "time"]
65
+ expect(hash["inst"]["method"]).to match(/#index \{Ruby\}/)
66
+ expect(hash["inst"]["time"].to_s).to match(/\A\d+\Z/)
67
+
68
+ post :create
69
+
70
+ last_payload = CopperEgg::APM.send(:class_variable_get, :@@payload_cache).split("\x00").select {|i| i.size > 2}.map {|i| i.sub(/^[^\{]+/,'')}.last
71
+ hash = JSON.parse last_payload
72
+
73
+ expect(hash["inst"]["method"]).to match(/#create \{Ruby\}/)
74
+ end
75
+ end
76
+
77
+ describe BenchmarkOnlyActionsController, :type => :controller do
78
+ controller(BenchmarkOnlyActionsController) do
79
+ add_benchmarks :only => [:index]
80
+
81
+ def index
82
+ 100.times.reduce(:+)
83
+ render :nothing => true
84
+ end
85
+ alias_method :create, :index
86
+ end
87
+
88
+ it "should benchmark all actions" do
89
+ get :index
90
+
91
+ last_payload = CopperEgg::APM.send(:class_variable_get, :@@payload_cache).split("\x00").select {|i| i.size > 2}.map {|i| i.sub(/^[^\{]+/,'')}.last
92
+ hash = JSON.parse last_payload
93
+
94
+ expect(hash.keys.sort).to eq ["id", "inst"]
95
+ expect(hash["id"]).to match(/\A[0-1a-z]{16}\z/i)
96
+ expect(hash["inst"].keys.sort).to eq ["method", "time"]
97
+ expect(hash["inst"]["method"]).to match(/#index \{Ruby\}/)
98
+ expect(hash["inst"]["time"].to_s).to match(/\A\d+\Z/)
99
+
100
+ post :create
101
+
102
+ last_payload = CopperEgg::APM.send(:class_variable_get, :@@payload_cache).split("\x00").select {|i| i.size > 2}.map {|i| i.sub(/^[^\{]+/,'')}.last
103
+ hash = JSON.parse last_payload
104
+
105
+ expect(hash["inst"]["method"]).to match(/#index \{Ruby\}/)
106
+ end
107
+ end
108
+
109
+ describe BenchmarkExceptActionsController, :type => :controller do
110
+ controller(BenchmarkExceptActionsController) do
111
+ add_benchmarks :except => [:create]
112
+
113
+ def index
114
+ 100.times.reduce(:+)
115
+ render :nothing => true
116
+ end
117
+ alias_method :create, :index
118
+ end
119
+
120
+ it "should benchmark all actions" do
121
+ get :index
122
+
123
+ last_payload = CopperEgg::APM.send(:class_variable_get, :@@payload_cache).split("\x00").select {|i| i.size > 2}.map {|i| i.sub(/^[^\{]+/,'')}.last
124
+ hash = JSON.parse last_payload
125
+
126
+ expect(hash.keys.sort).to eq ["id", "inst"]
127
+ expect(hash["id"]).to match(/\A[0-1a-z]{16}\z/i)
128
+ expect(hash["inst"].keys.sort).to eq ["method", "time"]
129
+ expect(hash["inst"]["method"]).to match(/#index \{Ruby\}/)
130
+ expect(hash["inst"]["time"].to_s).to match(/\A\d+\Z/)
131
+
132
+ post :create
133
+
134
+ last_payload = CopperEgg::APM.send(:class_variable_get, :@@payload_cache).split("\x00").select {|i| i.size > 2}.map {|i| i.sub(/^[^\{]+/,'')}.last
135
+ hash = JSON.parse last_payload
136
+
137
+ expect(hash["inst"]["method"]).to match(/#index \{Ruby\}/)
138
+ end
139
+ end
data/spec/apm_spec.rb ADDED
@@ -0,0 +1,330 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ class Foo
5
+ def self.bar
6
+ CopperEgg::APM.benchmark(self) { 100.times.reduce(:+) }
7
+ end
8
+
9
+ def bar
10
+ CopperEgg::APM.benchmark(self) { 100.times.reduce(:+) }
11
+ end
12
+
13
+ def baz
14
+ CopperEgg::APM.benchmark(nil) { 100.times.reduce(:+) }
15
+ end
16
+ end
17
+
18
+ module Baz
19
+ def self.bar
20
+ CopperEgg::APM.benchmark(self) { 100.times.reduce(:+) }
21
+ end
22
+ end
23
+
24
+ describe CopperEgg::APM do
25
+ describe ".trim_stacktrace" do
26
+ it "should remove lines not in app root" do
27
+ CopperEgg::APM::Configuration.app_root = "/deploy/current/"
28
+
29
+ stacktrace = <<-LINES
30
+ #{File.dirname(File.dirname(__FILE__))}/mysql2/client.rb:10:in `query'
31
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/mysql2-0.2.18/lib/active_record/connection_adapters/mysql2_adapter.rb:265:in `block in execute'
32
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract_adapter.rb:202:in `block in log'
33
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activesupport-3.0.20/lib/active_support/notifications/instrumenter.rb:21:in `instrument'
34
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract_adapter.rb:200:in `log'
35
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/mysql2-0.2.18/lib/active_record/connection_adapters/mysql2_adapter.rb:265:in `execute'
36
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/mysql2-0.2.18/lib/active_record/connection_adapters/mysql2_adapter.rb:586:in `select'
37
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract/database_statements.rb:7:in `select_all'
38
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract/query_cache.rb:56:in `select_all'
39
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/base.rb:473:in `find_by_sql'
40
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/relation.rb:64:in `to_a'
41
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/r>elation/finder_methods.rb:143:in `all'
42
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job_active_record-0.3.3/lib/delayed/backend/active_record.rb:58:in `block in find_available'
43
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activesupport-3.0.20/lib/active_support/benchmarkable.rb:55:in `silence'
44
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job_active_record-0.3.3/lib/delayed/backend/active_record.rb:57:in `find_available'
45
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/backend/base.rb:45:in `reserve'
46
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/worker.rb:258:in `reserve_and_run_one_job'
47
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/worker.rb:187:in `block in work_off'
48
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/worker.rb:186:in `times'
49
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/worker.rb:186:in `work_off'
50
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/worker.rb:151:in `block (4 levels) in start'
51
+ /home/copperegg/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/benchmark.rb:310:in `realtime'
52
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/worker.rb:150:in `block (3 levels) in start'
53
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/lifecycle.rb:60:in `call'
54
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/lifecycle.rb:60:in `block in initialize'
55
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/lifecycle.rb:65:in `call'
56
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/lifecycle.rb:65:in `execute'
57
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/lifecycle.rb:38:in `run_callbacks'
58
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/worker.rb:149:in `block (2 levels) in start'
59
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/worker.rb:148:in `loop'
60
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/worker.rb:148:in `block in start'
61
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/plugins/clear_locks.rb:7:in `call'
62
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/plugins/clear_locks.rb:7:in `block (2 levels) in '
63
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/lifecycle.rb:78:in `call'
64
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/lifecycle.rb:78:in `block (2 levels) in add'
65
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/lifecycle.rb:60:in `call'
66
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/lifecycle.rb:60:in `block in initialize'
67
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/lifecycle.rb:78:in `call'
68
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/lifecycle.rb:78:in `block in add'
69
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/lifecycle.rb:65:in `call'
70
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/lifecycle.rb:65:in `execute'
71
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/lifecycle.rb:38:in `run_callbacks'
72
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/worker.rb:147:in `start'
73
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/command.rb:104:in `run'
74
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/command.rb:92:in `block in run_process'
75
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/daemons-1.1.9/lib/daemons/application.rb:255:in `call'
76
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/daemons-1.1.9/lib/daemons/application.rb:255:in `block in start_proc'
77
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/daemons-1.1.9/lib/daemons/daemonize.rb:82:in `call'
78
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/daemons-1.1.9/lib/daemons/daemonize.rb:82:in `call_as_daemon'
79
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/daemons-1.1.9/lib/daemons/application.rb:259:in `start_proc'
80
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/daemons-1.1.9/lib/daemons/application.rb:296:in `start'
81
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/daemons-1.1.9/lib/daemons/controller.rb:70:in `run'
82
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/daemons-1.1.9/lib/daemons.rb:197:in `block in run_proc'
83
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/daemons-1.1.9/lib/daemons/cmdline.rb:109:in `call'
84
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/daemons-1.1.9/lib/daemons/cmdline.rb:109:in `catch_exceptions'
85
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/daemons-1.1.9/lib/daemons.rb:196:in `run_proc'
86
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delay>ed/command.rb:90:in `run_process'
87
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/command.rb:83:in `block in daemonize'
88
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/command.rb:81:in `times'
89
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/delayed_job-3.0.5/lib/delayed/command.rb:81:in `daemonize'
90
+ script/delayed_job:5:in `'
91
+ LINES
92
+
93
+ expect(CopperEgg::APM.trim_stacktrace(stacktrace.split("\n"))).to eq [stacktrace.lines.to_a.last.strip]
94
+ end
95
+
96
+ it "should only include lines in app root" do
97
+ CopperEgg::APM::Configuration.app_root = "/home/copperegg/rails/"
98
+
99
+ stacktrace = <<-LINES
100
+ #{File.dirname(File.dirname(__FILE__))}/lib/copperegg/apm/mysql2/client.rb:10:in `query'
101
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/mysql2-0.2.18/lib/active_record/connection_adapters/mysql2_adapter.rb:265:in `block in execute'
102
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract_adapter.rb:202:in `block in log'
103
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activesupport-3.0.20/lib/active_support/notifications/instrumenter.rb:21:in `instrument'
104
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract_adapter.rb:200:in `log'
105
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/mysql2-0.2.18/lib/active_record/connection_adapters/mysql2_adapter.rb:265:in `execute'
106
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/mysql2-0.2.18/lib/active_record/connection_adapters/mysql2_adapter.rb:586:in `select'
107
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract/database_statements.rb:7:in `select_all'
108
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract/query_cache.rb:56:in `select_all'
109
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/base.rb:473:in `find_by_sql'
110
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/relation.rb:64:in `to_a'
111
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/relation/finder_methods.rb:143:in `all'
112
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/base.rb:444:in `all'
113
+ /home/copperegg/rails/app/models/cluster.rb:60:in `populate_cache!'
114
+ /home/copperegg/rails/app/models/cluster.rb:48:in `get_nodes'
115
+ /home/copperegg/rails/lib/store.rb:48:in `io'
116
+ /home/copperegg/rails/lib/store.rb:218:in `save'
117
+ /home/copperegg/rails/app/models/sample.rb:74:in `stor'
118
+ /home/copperegg/rails/app/models/sample.rb:284:in `block (2 levels) in save'
119
+ /home/copperegg/rails/app/models/sample.rb:204:in `each'
120
+ /home/copperegg/rails/app/models/sample.rb:204:in `block in save'
121
+ /home/copperegg/rails/app/models/sample.rb:199:in `each'
122
+ /home/copperegg/rails/app/models/sample.rb:199:in `save'
123
+ /home/copperegg/rails/lib/worker.rb:401:in `save_sample'
124
+ /home/copperegg/rails/lib/worker.rb:256:in `worker_run'
125
+ /home/copperegg/rails/lib/worker.rb:170:in `block in start_worker'
126
+ /home/copperegg/rails/lib/worker.rb:164:in `fork'
127
+ /home/copperegg/rails/lib/worker.rb:164:in `start_worker'
128
+ /home/copperegg/rails/lib/worker.rb:209:in `block (2 levels) in spawn_workers'
129
+ /home/copperegg/rails/lib/worker.rb:206:in `times'
130
+ /home/copperegg/rails/lib/worker.rb:206:in `block in spawn_workers'
131
+ /home/copperegg/rails/lib/worker.rb:202:in `times'
132
+ /home/copperegg/rails/lib/worker.rb:202:in `spawn_workers'
133
+ /home/copperegg/rails/lib/worker.rb:153:in `run'
134
+ /home/copperegg/rails/script/worker_daemon.rb:10:in `'
135
+ LINES
136
+
137
+ trimmed_lines = CopperEgg::APM.trim_stacktrace(stacktrace.split("\n")).map(&:strip)
138
+
139
+ trimmed_stacktrace = <<-LINES
140
+ /home/copperegg/rails/app/models/cluster.rb:60:in `populate_cache!'
141
+ .../cluster.rb:48:in `get_nodes'
142
+ /home/copperegg/rails/lib/store.rb:48:in `io'
143
+ .../store.rb:218:in `save'
144
+ /home/copperegg/rails/app/models/sample.rb:74:in `stor'
145
+ .../sample.rb:284:in `block (2 levels) in save'
146
+ .../sample.rb:204:in `each'
147
+ .../sample.rb:204:in `block in save'
148
+ .../sample.rb:199:in `each'
149
+ .../sample.rb:199:in `save'
150
+ /home/copperegg/rails/lib/worker.rb:401:in `save_sample'
151
+ .../worker.rb:256:in `worker_run'
152
+ .../worker.rb:170:in `block in start_worker'
153
+ .../worker.rb:164:in `fork'
154
+ .../worker.rb:164:in `start_worker'
155
+ .../worker.rb:209:in `block (2 levels) in spawn_workers'
156
+ .../worker.rb:206:in `times'
157
+ .../worker.rb:206:in `block in spawn_workers'
158
+ .../worker.rb:202:in `times'
159
+ .../worker.rb:202:in `spawn_workers'
160
+ .../worker.rb:153:in `run'
161
+ /home/copperegg/rails/script/worker_daemon.rb:10:in `'
162
+ LINES
163
+
164
+ expect(trimmed_lines).to eq trimmed_stacktrace.split("\n").map(&:strip)
165
+ end
166
+
167
+ it "should include all lines if app root is not set" do
168
+ CopperEgg::APM::Configuration.app_root = nil
169
+
170
+ stacktrace = <<-LINES
171
+ #{File.dirname(File.dirname(__FILE__))}/lib/copperegg/apm/mysql2/client.rb:10:in `query'
172
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/mysql2-0.2.18/lib/active_record/connection_adapters/mysql2_adapter.rb:265:in `block in execute'
173
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract_adapter.rb:202:in `block in log'
174
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activesupport-3.0.20/lib/active_support/notifications/instrumenter.rb:21:in `instrument'
175
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract_adapter.rb:200:in `log'
176
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/mysql2-0.2.18/lib/active_record/connection_adapters/mysql2_adapter.rb:265:in `execute'
177
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/mysql2-0.2.18/lib/active_record/connection_adapters/mysql2_adapter.rb:586:in `select'
178
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract/database_statements.rb:7:in `select_all'
179
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract/query_cache.rb:56:in `select_all'
180
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/base.rb:473:in `find_by_sql'
181
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/relation.rb:64:in `to_a'
182
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/relation/finder_methods.rb:143:in `all'
183
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/base.rb:444:in `all'
184
+ /home/copperegg/rails/app/models/cluster.rb:60:in `populate_cache!'
185
+ /home/copperegg/rails/app/models/cluster.rb:48:in `get_nodes'
186
+ /home/copperegg/rails/lib/store.rb:48:in `io'
187
+ /home/copperegg/rails/lib/store.rb:218:in `save'
188
+ /home/copperegg/rails/app/models/sample.rb:74:in `stor'
189
+ /home/copperegg/rails/app/models/sample.rb:284:in `block (2 levels) in save'
190
+ /home/copperegg/rails/app/models/sample.rb:204:in `each'
191
+ /home/copperegg/rails/app/models/sample.rb:204:in `block in save'
192
+ /home/copperegg/rails/app/models/sample.rb:199:in `each'
193
+ /home/copperegg/rails/app/models/sample.rb:199:in `save'
194
+ /home/copperegg/rails/lib/worker.rb:401:in `save_sample'
195
+ /home/copperegg/rails/lib/worker.rb:256:in `worker_run'
196
+ /home/copperegg/rails/lib/worker.rb:170:in `block in start_worker'
197
+ /home/copperegg/rails/lib/worker.rb:164:in `fork'
198
+ /home/copperegg/rails/lib/worker.rb:164:in `start_worker'
199
+ /home/copperegg/rails/lib/worker.rb:209:in `block (2 levels) in spawn_workers'
200
+ /home/copperegg/rails/lib/worker.rb:206:in `times'
201
+ /home/copperegg/rails/lib/worker.rb:206:in `block in spawn_workers'
202
+ /home/copperegg/rails/lib/worker.rb:202:in `times'
203
+ /home/copperegg/rails/lib/worker.rb:202:in `spawn_workers'
204
+ /home/copperegg/rails/lib/worker.rb:153:in `run'
205
+ /home/copperegg/rails/script/worker_daemon.rb:10:in `'
206
+ LINES
207
+
208
+ trimmed_lines = CopperEgg::APM.trim_stacktrace(stacktrace.split("\n")).map(&:strip)
209
+
210
+ trimmed_stacktrace = <<-LINES
211
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/mysql2-0.2.18/lib/active_record/connection_adapters/mysql2_adapter.rb:265:in `block in execute'
212
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract_adapter.rb:202:in `block in log'
213
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activesupport-3.0.20/lib/active_support/notifications/instrumenter.rb:21:in `instrument'
214
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract_adapter.rb:200:in `log'
215
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/mysql2-0.2.18/lib/active_record/connection_adapters/mysql2_adapter.rb:265:in `execute'
216
+ .../mysql2_adapter.rb:586:in `select'
217
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/connection_adapters/abstract/database_statements.rb:7:in `select_all'
218
+ .../query_cache.rb:56:in `select_all'
219
+ .../base.rb:473:in `find_by_sql'
220
+ .../relation.rb:64:in `to_a'
221
+ /home/copperegg/.rvm/gems/ruby-1.9.2-p290@gems/activerecord-3.0.20/lib/active_record/relation/finder_methods.rb:143:in `all'
222
+ .../base.rb:444:in `all'
223
+ /home/copperegg/rails/app/models/cluster.rb:60:in `populate_cache!'
224
+ .../cluster.rb:48:in `get_nodes'
225
+ /home/copperegg/rails/lib/store.rb:48:in `io'
226
+ .../store.rb:218:in `save'
227
+ /home/copperegg/rails/app/models/sample.rb:74:in `stor'
228
+ .../sample.rb:284:in `block (2 levels) in save'
229
+ .../sample.rb:204:in `each'
230
+ .../sample.rb:204:in `block in save'
231
+ .../sample.rb:199:in `each'
232
+ .../sample.rb:199:in `save'
233
+ /home/copperegg/rails/lib/worker.rb:401:in `save_sample'
234
+ .../worker.rb:256:in `worker_run'
235
+ .../worker.rb:170:in `block in start_worker'
236
+ .../worker.rb:164:in `fork'
237
+ .../worker.rb:164:in `start_worker'
238
+ .../worker.rb:209:in `block (2 levels) in spawn_workers'
239
+ .../worker.rb:206:in `times'
240
+ .../worker.rb:206:in `block in spawn_workers'
241
+ .../worker.rb:202:in `times'
242
+ .../worker.rb:202:in `spawn_workers'
243
+ .../worker.rb:153:in `run'
244
+ /home/copperegg/rails/script/worker_daemon.rb:10:in `'
245
+ LINES
246
+
247
+ expect(trimmed_lines).to eq trimmed_stacktrace.split("\n").map(&:strip)
248
+ end
249
+ end
250
+
251
+ describe ".benchmark" do
252
+ it "should measure class method execution time" do
253
+ expect(Foo.bar).to eq 4950
254
+
255
+ last_payload = CopperEgg::APM.send(:class_variable_get, :@@payload_cache).split("\x00").select {|i| i.size > 2}.map {|i| i.sub(/^[^\{]+/,'')}.last
256
+ hash = JSON.parse last_payload
257
+
258
+ expect(hash.keys.sort).to eq ["id", "inst"]
259
+ expect(hash["id"]).to match(/\A[0-1a-z]{16}\z/i)
260
+ expect(hash["inst"].keys.sort).to eq ["method", "time"]
261
+ expect(hash["inst"]["method"]).to eq "Foo.bar {Ruby}"
262
+ expect(hash["inst"]["time"].to_s).to match(/\A\d+\Z/)
263
+ end
264
+
265
+ it "should measure instance method execution time" do
266
+ expect(Foo.new.bar).to eq 4950
267
+
268
+ last_payload = CopperEgg::APM.send(:class_variable_get, :@@payload_cache).split("\x00").select {|i| i.size > 2}.map {|i| i.sub(/^[^\{]+/,'')}.last
269
+ hash = JSON.parse last_payload
270
+
271
+ expect(hash.keys.sort).to eq ["id", "inst"]
272
+ expect(hash["id"]).to match(/\A[0-1a-z]{16}\z/i)
273
+ expect(hash["inst"].keys.sort).to eq ["method", "time"]
274
+ expect(hash["inst"]["method"]).to eq "Foo#bar {Ruby}"
275
+ expect(hash["inst"]["time"].to_s).to match(/\A\d+\Z/)
276
+ end
277
+
278
+ it "should measure module method execution time" do
279
+ expect(Baz.bar).to eq 4950
280
+
281
+ last_payload = CopperEgg::APM.send(:class_variable_get, :@@payload_cache).split("\x00").select {|i| i.size > 2}.map {|i| i.sub(/^[^\{]+/,'')}.last
282
+ hash = JSON.parse last_payload
283
+
284
+ expect(hash.keys.sort).to eq ["id", "inst"]
285
+ expect(hash["id"]).to match(/\A[0-1a-z]{16}\z/i)
286
+ expect(hash["inst"].keys.sort).to eq ["method", "time"]
287
+ expect(hash["inst"]["method"]).to eq "Baz.bar {Ruby}"
288
+ expect(hash["inst"]["time"].to_s).to match(/\A\d+\Z/)
289
+ end
290
+
291
+ it "should not label the method based on the argument passed" do
292
+ expect(Foo.new.baz).to eq 4950
293
+
294
+ last_payload = CopperEgg::APM.send(:class_variable_get, :@@payload_cache).split("\x00").select {|i| i.size > 2}.map {|i| i.sub(/^[^\{]+/,'')}.last
295
+ hash = JSON.parse last_payload
296
+
297
+ expect(hash.keys.sort).to eq ["id", "inst"]
298
+ expect(hash["id"]).to match(/\A[0-1a-z]{16}\z/i)
299
+ expect(hash["inst"].keys.sort).to eq ["method", "time"]
300
+ expect(hash["inst"]["method"]).to eq "NilClass#baz {Ruby}"
301
+ expect(hash["inst"]["time"].to_s).to match(/\A\d+\Z/)
302
+ end
303
+ end
304
+
305
+ describe ".obfuscate_sql" do
306
+ it "shoudl obfuscate select statement" do
307
+ sql = "SELECT `annotations`.* FROM `annotations` WHERE (`annotations`.id = 2) AND (updated_at > '#{Time.now.strftime('%Y-%m-%d %H:%M%S')}')"
308
+
309
+ expect(CopperEgg::APM.obfuscate_sql(sql)).to eq "SELECT annotations.* FROM annotations WHERE (annotations.id = ?) AND (updated_at > ?)"
310
+ end
311
+
312
+ it "should obfuscate items in a list" do
313
+ sql = 'SELECT COUNT(`items`.`id`) FROM `items` WHERE (`items`.id = 2) AND (state in ("enabled","expired"))'
314
+
315
+ expect(CopperEgg::APM.obfuscate_sql(sql)).to eq "SELECT COUNT(items.id) FROM items WHERE (items.id = ?) AND (state in (?,?))"
316
+ end
317
+
318
+ it "should obfuscate update statement" do
319
+ sql = "UPDATE `users` SET `phone` = '512.777.9311', `updated_at` = '#{Time.now.strftime('%Y-%m-%d %H:%M%S')}' WHERE `users`.`id` = 1"
320
+
321
+ expect(CopperEgg::APM.obfuscate_sql(sql)).to eq "UPDATE users SET phone = ?, updated_at = ? WHERE users.id = ?"
322
+ end
323
+
324
+ it "should obfuscate delete statement" do
325
+ sql = "DELETE FROM `reports` WHERE `reports`.`id` = 99"
326
+
327
+ expect(CopperEgg::APM.obfuscate_sql(sql)).to eq "DELETE FROM reports WHERE reports.id = ?"
328
+ end
329
+ end
330
+ end