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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 53c18884499d7d5cb6bcbc25cf3343c60347926d
4
+ data.tar.gz: db57ee020032965afd5b93c00379a66faf594580
5
+ SHA512:
6
+ metadata.gz: 334e0e654618769b5bf5c524c092e7585cd9ebf7700ba3e0f87c8f042876a7a614f2b2e2033f2918a9c1bf01e4b7ab857429c1f7380fdd2625e9f0c94000f10c
7
+ data.tar.gz: ac15445e1ec1e33cc262a58c97d133e8b2c0e854b3d0d10738e234ae12b79cc4e8db5f835c658fd5d3fe37f49560c0781b205845c91952317b95b48d060bd4e2
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 1.0.0
2
+
3
+ * Initial Release
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,102 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ copperegg-apm (1.0.0.pre1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ actionpack (3.2.12)
10
+ activemodel (= 3.2.12)
11
+ activesupport (= 3.2.12)
12
+ builder (~> 3.0.0)
13
+ erubis (~> 2.7.0)
14
+ journey (~> 1.0.4)
15
+ rack (~> 1.4.5)
16
+ rack-cache (~> 1.2)
17
+ rack-test (~> 0.6.1)
18
+ sprockets (~> 2.2.1)
19
+ activemodel (3.2.12)
20
+ activesupport (= 3.2.12)
21
+ builder (~> 3.0.0)
22
+ activesupport (3.2.12)
23
+ i18n (~> 0.6)
24
+ multi_json (~> 1.0)
25
+ builder (3.0.4)
26
+ diff-lcs (1.1.3)
27
+ erubis (2.7.0)
28
+ ethon (0.5.12)
29
+ ffi (>= 1.3.0)
30
+ mime-types (~> 1.18)
31
+ faker (1.1.2)
32
+ i18n (~> 0.5)
33
+ ffi (1.8.1)
34
+ hike (1.2.2)
35
+ i18n (0.6.4)
36
+ journey (1.0.4)
37
+ json (1.5.5)
38
+ mime-types (1.23)
39
+ multi_json (1.7.2)
40
+ mysql (2.9.1)
41
+ mysql2 (0.3.11)
42
+ pg (0.15.1)
43
+ rack (1.4.5)
44
+ rack-cache (1.2)
45
+ rack (>= 0.4)
46
+ rack-ssl (1.3.3)
47
+ rack
48
+ rack-test (0.6.2)
49
+ rack (>= 1.0)
50
+ railties (3.2.12)
51
+ actionpack (= 3.2.12)
52
+ activesupport (= 3.2.12)
53
+ rack-ssl (~> 1.3.2)
54
+ rake (>= 0.8.7)
55
+ rdoc (~> 3.4)
56
+ thor (>= 0.14.6, < 2.0)
57
+ rake (10.0.4)
58
+ rdoc (3.12.2)
59
+ json (~> 1.4)
60
+ rest-client (1.6.7)
61
+ mime-types (>= 1.16)
62
+ rspec (2.11.0)
63
+ rspec-core (~> 2.11.0)
64
+ rspec-expectations (~> 2.11.0)
65
+ rspec-mocks (~> 2.11.0)
66
+ rspec-core (2.11.1)
67
+ rspec-expectations (2.11.3)
68
+ diff-lcs (~> 1.1.3)
69
+ rspec-mocks (2.11.3)
70
+ rspec-rails (2.11.4)
71
+ actionpack (>= 3.0)
72
+ activesupport (>= 3.0)
73
+ railties (>= 3.0)
74
+ rspec (~> 2.11.0)
75
+ sprockets (2.2.2)
76
+ hike (~> 1.2)
77
+ multi_json (~> 1.0)
78
+ rack (~> 1.0)
79
+ tilt (~> 1.1, != 1.3.0)
80
+ sqlite3 (1.3.7)
81
+ thor (0.18.1)
82
+ tilt (1.3.7)
83
+ typhoeus (0.6.3)
84
+ ethon (~> 0.5.11)
85
+
86
+ PLATFORMS
87
+ ruby
88
+
89
+ DEPENDENCIES
90
+ actionpack (~> 3.0)
91
+ copperegg-apm!
92
+ ethon (~> 0.5)
93
+ faker (~> 1.1.2)
94
+ mysql (~> 2.9)
95
+ mysql2 (~> 0.2)
96
+ pg (~> 0.9)
97
+ rake (~> 10.0.0)
98
+ rest-client (~> 1.6)
99
+ rspec (~> 2.11.0)
100
+ rspec-rails (~> 2.0)
101
+ sqlite3 (~> 1.3)
102
+ typhoeus (~> 0.3)
data/README.md ADDED
@@ -0,0 +1,181 @@
1
+ # CopperEgg APM
2
+
3
+ Monitor the performance of your application with code instrumentation and exception aggregation.
4
+
5
+ **Code instrumentation** measures the elapsed time used to execute a SQL statement, outbound HTTP request, or method call.
6
+
7
+ **Exception aggregation** monitors the number and type of exceptions occurring in your code.
8
+
9
+ ## Getting Started
10
+
11
+ ### Set Up Your CopperEgg Account
12
+
13
+ ![CopperEgg App Screenshot](https://raw.github.com/CopperEgg/apm/master/screenshot01.png)
14
+
15
+ Login to your account at [CopperEgg.com](https://app.copperegg.com/login) and click on the `Apps` tag to create a new App. An App represents your existing application. Give it a descriptive name like 'MySaaSApp.com'.
16
+
17
+ You must add Code Instrumentation to your App. Each component of your application can be represented by an Instrument. Benchmarks from your app will be separated by Instrument. For example, you may want to create a separate Instrument for each daemon process in your application.
18
+
19
+ Copy your Instrument key. You will need to add it to your gem configuration as described below.
20
+
21
+ ### Installation
22
+
23
+ You can install the `copperegg-apm` directly or use Bunder:
24
+
25
+ Install the gem directly
26
+
27
+ gem install copperegg-apm
28
+
29
+ Using Bunder, add to your Gemfile
30
+
31
+ gem 'copperegg-apm'
32
+
33
+ #### Ruby on Rails 3+
34
+
35
+ Once the gem is bundled, you will need to generate a configuration file. Under your project's root directory, run:
36
+
37
+ rails g copperegg:apm:init
38
+
39
+ This will create a configuration file at `config/initializers/copperegg_apm_config.rb`. You will be prompted to enter your Instrument key.
40
+
41
+ #### Sinatra and Other Ruby Environments
42
+
43
+ From your project's root directory, run `copperegg-apm-init`. This will create a configuration file named `copperegg_apm_config.rb`. You will be prompted to enter your Instrument key.
44
+
45
+ Require this file below your other require directives.
46
+
47
+ ### Configuration
48
+
49
+ Instrumentation is initiated in your project by calling the `CopperEgg::APM.configure` block.
50
+
51
+ The auto-generated configuration script contains the default configuration values.
52
+
53
+ ```ruby
54
+ CopperEgg::APM.configure do |config|
55
+ config.instrument_key = "your_instrument_key"
56
+ config.benchmark_sql = true # Benchmark database queries
57
+ config.benchmark_exceptions = true
58
+ config.benchmark_http = true # Benchmark outgoing HTTP requests
59
+ config.benchmark_methods(:disabled)
60
+ end
61
+ ```
62
+
63
+ ### Automatic Method Benchmarking
64
+
65
+ For performance reasons, automatic method benchmarking is disabled by default. When enabled, *CopperEgg::APM* intelligently adds benchmarking to public and protected methods defined within your codebase. By default, methods whose names begin with an underscore (_) or end with a question mark (?) are not benchmarked.
66
+
67
+ #### Configuring Automatic Method Benchmarking
68
+
69
+ Automatic method benchmarking is enabled by calling `config.benchmark_methods` within the configuration block. It expects a level which is represented by a symbol whose value is either :disabled, :basic, :moderate, :full, or :custom.
70
+
71
+ The levls *basic* and *moderate* are Rails-specific. With the *basic* level only controller methods are automatically benchmarked. The *moderate* level benchmarks all controller methods as well as methods of classes that descend from *ActiveRecord::Base*. Note that for controller actions, benchmarks also include rendering time.
72
+
73
+ A level *full* benchmarks all methods within your project. For non-Rails projects, the *basic* and *moderate* levels will behave like the *full* level.
74
+
75
+ The *custom* level allows you to explicitly set which methods to benchmark. When setting `benchmark_methods` to :custom a second argument is expected which is an array of strings representing the methods to benchmark. The format of these strings is explained in the following section.
76
+
77
+ #### Inclusions and Exclusions
78
+
79
+ With automatic method benchmarking enabled, you can fine-tune the list of methods by passing either a hash (for values :basic, :moderate, or :full) or an array of strings (for :custom).
80
+
81
+ The hash must have keys named either :include or :exclude which are set to an array of strings.
82
+
83
+ Each string in these arrays must follow the pattern *Class.class_method*, *Class#instance_method*, *Class*, or *method_name*.
84
+
85
+ The following is an example configuration that only benchmarks a discreet set of methods:
86
+
87
+ ```ruby
88
+ config.benchmark_methods :custom, %w(User.authenticate! run)
89
+ ```
90
+
91
+ In the example above, the only methods benchmarked will be the method 'authenticate!' in class 'User' and any method named 'run' in any instance or class defined in your project.
92
+
93
+ By contrast, you can benchmark all methods defined in your project except for a discreet set:
94
+
95
+ ```ruby
96
+ config.benchmark_methods :full, :exclude => %w(User#full_name User#last_name Widget.expired MyLogger perform)
97
+ ```
98
+
99
+ In the example above, the methods 'full_name' and 'last_name' in any instance of class 'User', the class method 'expired' in class 'Widget', any instance or class method in 'MyLogger' and any method named 'perform' in any instance or class will not be benchmarked.
100
+
101
+ Similarly, any method in your project not benchmarked by default can be included:
102
+
103
+ ```ruby
104
+ config.benchmark_methods :basic, :include => %w(BackgroundJob#running? User#_callback_after_1135 User#_callback_after_1136)
105
+ ```
106
+
107
+ These options can be used in conjunction:
108
+
109
+ ```ruby
110
+ config.benchmark_methods :moderate, :include => %w(MyCustomLibrary), :exclude => %w(ReportsController User#generate_password)
111
+ ```
112
+
113
+ In the case where a method is named in both *include* and *exclude* lists, the *exclude* list takes precendence.
114
+
115
+ For a Rails project, run `rake copperegg:apm:methods` on the command line in your application root directory to print a list of all methods defined in your project and whether or not they are benchmarked.
116
+
117
+ For non-Rails projects, this can be acheived by running `copperegg-apm-methods` on the command line in your application root directory.
118
+
119
+ Automatic method benchmarking is only available for Ruby 1.9+ and REE.
120
+
121
+ **Because benchmarking is added to your methods using reflection and metaprogramming, you should use this directive with discretion to avoid performance degredation.**
122
+
123
+ #### Block-based Method Benchmarking
124
+
125
+ Even with method benchmarking disabled, you can still benchmark blocks of code with the `CopperEgg::APM.benchmark` method.
126
+
127
+ ```ruby
128
+ def generate_password(length=16)
129
+ CopperEgg::APM.benchmark(self) do
130
+ chars = ("a".."z").to_a + ("0".."9").to_a + %w($ * - _)
131
+ password = Array.new(length).map { chars[rand(chars.length)].send([:upcase,:downcase][rand(2)]) }.join
132
+ while !PASSWORD_PATTERN.match(password) do
133
+ password = generate_password(length, include_special)
134
+ end
135
+ end
136
+ password
137
+ end
138
+ ```
139
+
140
+ ### Supported Databases
141
+
142
+ Database query benchmarking is supported for the following engines:
143
+
144
+ + MySQL (via the mysql and mysql2 gems)
145
+ + PostgreSQL (via the pg gem)
146
+ + SQLite (via the sqlite3 gem)
147
+
148
+ ### Supported Gems For Benchmarking Outbound HTTP Requests
149
+
150
+ Outbound HTTP requests performed within your project will be benchmarked from any of the following sources:
151
+
152
+ + Net/HTTP
153
+ + Ethon
154
+ + Typhoeus::Hydra
155
+ + RestClient
156
+
157
+ ## Exception Aggregation
158
+
159
+ When exception benchmarking is enabled, any exception raised in your project will be aggregated by class, source location, and system.
160
+
161
+ ## Disable Gem Functionality
162
+
163
+ To temporarily disable all functionality of this gem, set all `config.benchmark_` values to false in the configuration file and then restart your application:
164
+
165
+ ```ruby
166
+ CopperEgg::APM.configure do |config|
167
+ config.instrument_key = "IOFiZ4cslRiV5SXl"
168
+ config.benchmark_sql = false
169
+ config.benchmark_http = false
170
+ config.benchmark_exceptions = false
171
+ config.benchmark_methods = false
172
+ end
173
+ ```
174
+
175
+ ## Support
176
+
177
+ Questions or problems? Contact [CopperEgg Support](https://copperegg.zendesk.com/home)
178
+
179
+ ## License
180
+
181
+ CopperEgg::APM is released under the [MIT license](http://www.opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'rake'
2
+ require 'bundler'
3
+ Bundler.setup
4
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'copperegg-apm'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'copperegg-apm'
8
+ end
9
+
10
+ require 'optparse'
11
+ require 'erb'
12
+
13
+ template = File.read File.join(File.dirname(__FILE__), '../lib/generators/copperegg/apm/templates/config.rb')
14
+
15
+ options = OptionParser.new do |opts|
16
+ opts.banner = "Usage: #{File.basename(__FILE__)}"
17
+
18
+ opts.on("-h") do
19
+ puts opts.banner
20
+ end
21
+ end.parse!
22
+
23
+ template_path = if File.exists?(File.join(Dir.pwd, "config", "initializers"))
24
+ File.join(Dir.pwd, "config", "initializers", "copperegg_apm_config.rb")
25
+ else
26
+ File.join(Dir.pwd, "copperegg_apm_config.rb")
27
+ end
28
+
29
+ if File.exists?(template_path)
30
+ print "Override existing file #{template_path}? (Y/n): "
31
+ if gets =~ /n/i
32
+ puts "Configuration file creation abandoned."
33
+ exit
34
+ end
35
+ end
36
+
37
+ print "Enter your instrument key: "
38
+
39
+ instrument_key = gets.strip
40
+
41
+ File.open(template_path, File::WRONLY|File::CREAT|File::TRUNC) do |file|
42
+ file.write "require 'copperegg-apm'\n" + ERB.new(template).result(binding)
43
+ end
44
+
45
+ puts "Configuration file written to #{template_path}."
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'copperegg-apm'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'copperegg-apm'
8
+ end
9
+
10
+ require File.join(File.dirname(__FILE__), '../lib/copperegg/apm/benchmark_methods_table')
11
+
12
+ usage = "Usage: #{File.basename(__FILE__)} /path/to/startup_file"
13
+
14
+ if ARGV[0].to_s.empty?
15
+ puts usage
16
+ exit
17
+ elsif ARGV[0].strip == "-h" || ARGV[0].strip == "--help"
18
+ puts usage
19
+ exit
20
+ elsif File.exists?(ARGV[0])
21
+ require ARGV[0]
22
+ else
23
+ puts "No startup file found at #{ARGV[0]}. #{usage}"
24
+ exit
25
+ end
26
+
27
+ CopperEgg::APM::BenchmarkMethodsTable.new.print_table
Binary file
@@ -0,0 +1,34 @@
1
+ require './lib/copperegg/apm/version'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'copperegg-apm'
5
+ s.version = CopperEgg::APM::GEM_VERSION
6
+ s.authors = ['Mike Bradford']
7
+ s.email = 'mbradford@copperegg.com'
8
+ s.date = Time.now.utc.strftime("%Y-%m-%d")
9
+
10
+ s.homepage = 'http://github.com/copperegg/apm'
11
+ s.summary = "copperegg-apm-#{CopperEgg::APM::GEM_VERSION}"
12
+ s.description = 'CopperEgg Application Performance Monitoring'
13
+ s.license = 'MIT'
14
+
15
+ s.platform = Gem::Platform::RUBY
16
+ s.require_path = 'lib'
17
+ s.files = Dir["#{File.dirname(__FILE__)}/**/*"] + %w(README.md Rakefile copperegg-apm.gemspec)
18
+ s.test_files = Dir.glob("spec/**/*.rb")
19
+ s.rdoc_options = ['--line-numbers', '--inline-source', '--title', 'copperegg-apm', '--main', 'README.md']
20
+ s.executables = ['copperegg-apm-init', 'copperegg-apm-methods']
21
+
22
+ s.add_development_dependency 'rake', '~> 10.0.0'
23
+ s.add_development_dependency 'rspec', '~> 2.11.0'
24
+ s.add_development_dependency 'rspec-rails', '~> 2.0'
25
+ s.add_development_dependency 'actionpack', '~> 3.0'
26
+ s.add_development_dependency 'faker', '~> 1.1.2'
27
+ s.add_development_dependency 'mysql', '~> 2.9'
28
+ s.add_development_dependency 'mysql2', '~> 0.2'
29
+ s.add_development_dependency 'sqlite3', '~> 1.3'
30
+ s.add_development_dependency 'pg', '~> 0.9'
31
+ s.add_development_dependency 'ethon', '~> 0.5'
32
+ s.add_development_dependency 'typhoeus', '~> 0.3'
33
+ s.add_development_dependency 'rest-client', '~> 1.6'
34
+ end
Binary file
data/ext/mkrf_conf.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'rubygems/command.rb'
3
+ require 'rubygems/dependency_installer.rb'
4
+ begin
5
+ Gem::Command.build_args = ARGV
6
+ rescue NoMethodError
7
+ end
8
+ inst = Gem::DependencyInstaller.new
9
+ begin
10
+ if RUBY_VERSION < "1.9"
11
+ inst.install "json"
12
+ end
13
+ rescue
14
+ exit(1)
15
+ end
16
+
17
+ f = File.open(File.join(File.dirname(__FILE__), "Rakefile"), "w") # create dummy rakefile to indicate success
18
+ f.write("task :default\n")
19
+ f.close
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), "copperegg", "apm")