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