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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +102 -0
- data/README.md +181 -0
- data/Rakefile +4 -0
- data/bin/copperegg-apm-init +45 -0
- data/bin/copperegg-apm-methods +27 -0
- data/copperegg-apm-1.0.0.pre1.gem +0 -0
- data/copperegg-apm.gemspec +34 -0
- data/copperegg_apm_test.db +0 -0
- data/ext/mkrf_conf.rb +19 -0
- data/lib/copperegg-apm.rb +1 -0
- data/lib/copperegg/apm.rb +28 -0
- data/lib/copperegg/apm/action_controller/base.rb +31 -0
- data/lib/copperegg/apm/active_record/connection_adapters/abstract_adapter.rb +35 -0
- data/lib/copperegg/apm/benchmark.rb +212 -0
- data/lib/copperegg/apm/benchmark_methods_table.rb +65 -0
- data/lib/copperegg/apm/configuration.rb +188 -0
- data/lib/copperegg/apm/engine.rb +15 -0
- data/lib/copperegg/apm/errors.rb +6 -0
- data/lib/copperegg/apm/ethon/easy/operations.rb +37 -0
- data/lib/copperegg/apm/kernel.rb +19 -0
- data/lib/copperegg/apm/middleware.rb +20 -0
- data/lib/copperegg/apm/mysql.rb +31 -0
- data/lib/copperegg/apm/mysql2/client.rb +35 -0
- data/lib/copperegg/apm/net/http.rb +39 -0
- data/lib/copperegg/apm/pg/connection.rb +35 -0
- data/lib/copperegg/apm/restclient/request.rb +33 -0
- data/lib/copperegg/apm/rum.rb +24 -0
- data/lib/copperegg/apm/sqlite3/database.rb +35 -0
- data/lib/copperegg/apm/tasks.rb +11 -0
- data/lib/copperegg/apm/typhoeus/hydra.rb +33 -0
- data/lib/copperegg/apm/unbound_method.rb +116 -0
- data/lib/copperegg/apm/version.rb +5 -0
- data/lib/generators/copperegg/apm/init_generator.rb +20 -0
- data/lib/generators/copperegg/apm/templates/config.rb +10 -0
- data/performance/mysql2.rb +44 -0
- data/screenshot01.png +0 -0
- data/spec/action_controller_spec.rb +139 -0
- data/spec/apm_spec.rb +330 -0
- data/spec/ethon_spec.rb +73 -0
- data/spec/helpers/mysql2_setup.rb +51 -0
- data/spec/helpers/mysql_setup.rb +45 -0
- data/spec/helpers/pg_setup.rb +43 -0
- data/spec/helpers/rails.rb +18 -0
- data/spec/helpers/sqlite3_setup.rb +30 -0
- data/spec/kernel_spec.rb +37 -0
- data/spec/mysql2_spec.rb +58 -0
- data/spec/mysql_spec.rb +66 -0
- data/spec/net_http_spec.rb +52 -0
- data/spec/pg_spec.rb +45 -0
- data/spec/restclient_spec.rb +67 -0
- data/spec/rum_spec.rb +23 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/sqlite3_spec.rb +45 -0
- data/spec/typhoeus_spec.rb +66 -0
- metadata +316 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
at_exit do
|
2
|
+
begin
|
3
|
+
CopperEgg::APM.send_payload_cache
|
4
|
+
rescue
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
%w(
|
9
|
+
benchmark
|
10
|
+
action_controller/base
|
11
|
+
active_record/connection_adapters/abstract_adapter
|
12
|
+
configuration
|
13
|
+
errors
|
14
|
+
ethon/easy/operations
|
15
|
+
kernel
|
16
|
+
mysql
|
17
|
+
mysql2/client
|
18
|
+
net/http
|
19
|
+
pg/connection
|
20
|
+
restclient/request
|
21
|
+
rum
|
22
|
+
sqlite3/database
|
23
|
+
typhoeus/hydra
|
24
|
+
unbound_method
|
25
|
+
version
|
26
|
+
).each { |file| require "copperegg/apm/#{file}" }
|
27
|
+
|
28
|
+
require 'copperegg/apm/engine' if defined?(::Rails::Engine)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module CopperEgg
|
2
|
+
module APM
|
3
|
+
module ActionController
|
4
|
+
module Base
|
5
|
+
def self.included(klass)
|
6
|
+
klass.class_eval do
|
7
|
+
def self.add_benchmarks(options={})
|
8
|
+
raise ArgumentError.new("hash expected") if !options.is_a?(Hash)
|
9
|
+
around_filter :ce_benchmark_controller_actions, options
|
10
|
+
end
|
11
|
+
|
12
|
+
def ce_benchmark_controller_actions
|
13
|
+
starttime = (Time.now.to_f * 1000.0).to_i
|
14
|
+
yield
|
15
|
+
time = (Time.now.to_f * 1000.0).to_i - starttime
|
16
|
+
CopperEgg::APM.send_payload({:method => "#{self.to_s.sub!(/:[a-z0-9]x[^>]+>/, "").sub!("#<","")}##{action_name}", :time => time})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
if defined?(::ActionController::Base)
|
26
|
+
|
27
|
+
class ActionController::Base
|
28
|
+
include CopperEgg::APM::ActionController::Base
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module CopperEgg
|
2
|
+
module APM
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module AbstractAdapter
|
6
|
+
def log_with_ce_instrumentation(*args)
|
7
|
+
if CopperEgg::APM::Configuration.benchmark_active_record?
|
8
|
+
sql, name = args
|
9
|
+
starttime = (Time.now.to_f * 1000.0).to_i
|
10
|
+
result = block_given? ? log_without_ce_instrumentation(*args, &Proc.new) : log_without_ce_instrumentation(*args)
|
11
|
+
time = (Time.now.to_f * 1000.0).to_i - starttime
|
12
|
+
|
13
|
+
CopperEgg::APM.send_payload({:sql => CopperEgg::APM.obfuscate_sql(sql), :time => time})
|
14
|
+
|
15
|
+
result
|
16
|
+
else
|
17
|
+
block_given? ? log_without_ce_instrumentation(*args, &Proc.new) : log_without_ce_instrumentation(*args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 3
|
27
|
+
|
28
|
+
class ActiveRecord::ConnectionAdapters::AbstractAdapter
|
29
|
+
include CopperEgg::APM::ActiveRecord::ConnectionAdapters::AbstractAdapter
|
30
|
+
alias_method :log_without_ce_instrumentation, :log
|
31
|
+
alias_method :log, :log_with_ce_instrumentation
|
32
|
+
protected :log
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module CopperEgg
|
5
|
+
module APM
|
6
|
+
DO_NOT_INCLUDE_PATHS = %w(. test spec vendor config)
|
7
|
+
PAYLOAD_FREQUENCY = 15
|
8
|
+
PAYLOAD_BYTESIZE = 1024*8
|
9
|
+
@@benchmarkable_methods = []
|
10
|
+
@@payload_sent_at = 0
|
11
|
+
@@payload_cache = ""
|
12
|
+
@@gem_version_sent = false
|
13
|
+
|
14
|
+
module_function
|
15
|
+
|
16
|
+
def configure(&block)
|
17
|
+
@@payload_cache = {:version => CopperEgg::APM::GEM_VERSION}.to_json
|
18
|
+
send_payload_cache
|
19
|
+
Configuration.configure(&block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def benchmark(arg, &block)
|
23
|
+
if arg.class == Hash
|
24
|
+
parameters = arg
|
25
|
+
elsif arg.class == Class || arg.class == Module
|
26
|
+
parameters = {:method => "#{arg}.#{calling_method}"}
|
27
|
+
else
|
28
|
+
parameters = {:method => "#{arg.class}##{calling_method}"}
|
29
|
+
end
|
30
|
+
|
31
|
+
starttime = (Time.now.to_f * 1000.0).to_i
|
32
|
+
result = yield
|
33
|
+
parameters[:time] = (Time.now.to_f * 1000.0).to_i - starttime
|
34
|
+
|
35
|
+
send_payload(parameters)
|
36
|
+
|
37
|
+
result
|
38
|
+
end
|
39
|
+
|
40
|
+
# Inject benchmarking code into user-defined public instance and class accessor methods whose names are made up of only alphanumeric characters.
|
41
|
+
# By user-defined, we mean those whose source is defined in neither the load path nor the gem path.
|
42
|
+
def add_method_benchmarking
|
43
|
+
benchmarkable_methods.each {|method| method.add_benchmarking unless method.excluded?}
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns an array of unbound methods to which benchmarking can be added.
|
47
|
+
def benchmarkable_methods
|
48
|
+
return @@benchmarkable_methods if @@benchmarkable_methods.size > 0
|
49
|
+
|
50
|
+
if defined?(::Rails) && Rails.respond_to?(:configuration)
|
51
|
+
$LOAD_PATH.each do |path|
|
52
|
+
v, $VERBOSE = $VERBOSE, nil
|
53
|
+
if path.include?(Rails.configuration.root.to_s) && DO_NOT_INCLUDE_PATHS.detect {|part| path.include?("/#{part}")}.nil?
|
54
|
+
Dir.glob("#{path}/**/*.rb").each {|f| ActiveSupport::Dependencies::Loadable.require_dependency f, ""}
|
55
|
+
end
|
56
|
+
$VERBOSE = v
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
ObjectSpace.each_object(Class) do |class_or_module|
|
61
|
+
begin
|
62
|
+
class_or_module.instance_methods(false).each do |name|
|
63
|
+
method = class_or_module.instance_method(name)
|
64
|
+
method.parent_class = class_or_module
|
65
|
+
@@benchmarkable_methods.push(method) if method.benchmarkable?
|
66
|
+
end
|
67
|
+
rescue
|
68
|
+
end
|
69
|
+
|
70
|
+
begin
|
71
|
+
class_or_module.singleton_class.instance_methods(false).each do |name|
|
72
|
+
method = class_or_module.singleton_class.instance_method(name)
|
73
|
+
method.parent_class = class_or_module
|
74
|
+
method.class_method = true
|
75
|
+
@@benchmarkable_methods.push(method) if method.benchmarkable?
|
76
|
+
end
|
77
|
+
rescue
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
@@benchmarkable_methods
|
82
|
+
end
|
83
|
+
|
84
|
+
def send_payload(parameters)
|
85
|
+
return if CopperEgg::APM::Configuration.instrument_key == nil
|
86
|
+
|
87
|
+
param_type = if parameters.has_key?(:sql)
|
88
|
+
:sql
|
89
|
+
elsif parameters.has_key?(:method)
|
90
|
+
:method
|
91
|
+
elsif parameters.has_key?(:url)
|
92
|
+
:url
|
93
|
+
elsif parameters.has_key?(:error)
|
94
|
+
:error
|
95
|
+
end
|
96
|
+
|
97
|
+
parameters[param_type] += " {Ruby}"
|
98
|
+
|
99
|
+
key = parameters[:error] ? :excp : :inst
|
100
|
+
hash = {key => parameters, :id => CopperEgg::APM::Configuration.instrument_key}
|
101
|
+
|
102
|
+
json = hash.to_json
|
103
|
+
payload = "#{[0x6375].pack("N")}#{[json.length].pack("N")}#{json}"
|
104
|
+
if @@payload_cache.bytesize > PAYLOAD_BYTESIZE
|
105
|
+
@@payload_cache = ""
|
106
|
+
elsif @@payload_cache.bytesize > 0 && ((@@payload_cache.bytesize + payload.bytesize) >= PAYLOAD_BYTESIZE || Time.now.to_i - @@payload_sent_at >= PAYLOAD_FREQUENCY)
|
107
|
+
send_payload_cache
|
108
|
+
end
|
109
|
+
@@payload_cache = "#{@@payload_cache}#{payload}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def send_payload_cache
|
113
|
+
begin
|
114
|
+
if RUBY_VERSION < "1.9"
|
115
|
+
socket = UDPSocket.new
|
116
|
+
socket.send(@@payload_cache, 0, CopperEgg::APM::Configuration.udp_host, CopperEgg::APM::Configuration.udp_port)
|
117
|
+
else
|
118
|
+
socket = Socket.new(:INET, :DGRAM)
|
119
|
+
addr = Socket.sockaddr_in(CopperEgg::APM::Configuration.udp_port, CopperEgg::APM::Configuration.udp_host)
|
120
|
+
socket.connect_nonblock(addr)
|
121
|
+
socket.send(@@payload_cache, 0)
|
122
|
+
end
|
123
|
+
rescue Errno::EMSGSIZE
|
124
|
+
end
|
125
|
+
socket.close
|
126
|
+
CopperEgg::APM::Configuration.log @@payload_cache
|
127
|
+
@@payload_sent_at = Time.now.to_i
|
128
|
+
@@payload_cache = ""
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns a copy of self with escaping, quotes, whitespacing and actual values removed
|
132
|
+
# from SQL statements
|
133
|
+
def obfuscate_sql(sql)
|
134
|
+
return sql if sql.bytesize > 1024 # don't bother with sql statements larger than 1k
|
135
|
+
sql = sql.dup
|
136
|
+
|
137
|
+
# Remove escaping quotes
|
138
|
+
sql.gsub!(/\\["']/, '')
|
139
|
+
|
140
|
+
# Remove surrounding backticks
|
141
|
+
sql.gsub!(/[`]([^`]+)[`]/i,'\1')
|
142
|
+
|
143
|
+
# Remove surrounding quotes from strings immediately following "FROM"
|
144
|
+
sql.gsub!(/(from|into|update|set)\s*['"`]([^'"`]+)['"`]/i,'\1 \2')
|
145
|
+
|
146
|
+
# Remove surrounding quotes from strings immediately neighboring a period
|
147
|
+
sql.gsub!(/([\.])['"]([^'"]+)['"]/,'\1\2')
|
148
|
+
sql.gsub!(/['"]([^'"]+)['"]([\.])/,'\1\2')
|
149
|
+
|
150
|
+
# Replace other quoted strings with a question mark
|
151
|
+
sql.gsub!(/['"]([^'"]+)['"]/,'?')
|
152
|
+
|
153
|
+
# Remove integers
|
154
|
+
sql.gsub!(/\b\d+\b/, '?')
|
155
|
+
|
156
|
+
# Removing padded spaces
|
157
|
+
sql.gsub!(/(\s){2,}/,'\1')
|
158
|
+
|
159
|
+
# Remove leading and trailing whitespace
|
160
|
+
sql.strip!
|
161
|
+
|
162
|
+
sql
|
163
|
+
end
|
164
|
+
|
165
|
+
def trim_stacktrace(array)
|
166
|
+
previous_components = nil
|
167
|
+
|
168
|
+
last = array.last.strip
|
169
|
+
|
170
|
+
array.reject! {|path| path.include? CopperEgg::APM::Configuration.gem_root}
|
171
|
+
|
172
|
+
if !CopperEgg::APM::Configuration.app_root.empty?
|
173
|
+
array.reject! {|path| !path.include?(CopperEgg::APM::Configuration.app_root) || !CopperEgg::APM::DO_NOT_INCLUDE_PATHS.detect {|part| path.include?("/#{part}")}.nil?}
|
174
|
+
end
|
175
|
+
|
176
|
+
array.map! do |path|
|
177
|
+
if previous_components
|
178
|
+
current_components = path.split("/")
|
179
|
+
|
180
|
+
if (current_components - previous_components).size == 1
|
181
|
+
path = ".../" + (current_components - previous_components).join("/")
|
182
|
+
end
|
183
|
+
|
184
|
+
previous_components = current_components
|
185
|
+
else
|
186
|
+
previous_components = path.split("/")
|
187
|
+
end
|
188
|
+
path.strip
|
189
|
+
end
|
190
|
+
|
191
|
+
array.push(last) if last && array.empty?
|
192
|
+
array
|
193
|
+
end
|
194
|
+
|
195
|
+
def calling_method
|
196
|
+
caller[1] =~ /`([^']*)'/ and $1
|
197
|
+
end
|
198
|
+
|
199
|
+
def capture_exception(*args)
|
200
|
+
exception = if args.size == 0
|
201
|
+
$!.nil? ? RuntimeError.new : $!
|
202
|
+
elsif args.size == 1
|
203
|
+
args.first.is_a?(String) ? RuntimeError.exception(args.first) : args.first.exception
|
204
|
+
elsif args.size <= 3
|
205
|
+
args.first.exception(args[1])
|
206
|
+
end
|
207
|
+
stacktrace = trim_stacktrace(caller)
|
208
|
+
parameters = {:error => "#{exception.class}|#{stacktrace.first}", :stacktrace => "#{exception.message}\n#{stacktrace.join("\n")}", :ts => Time.now.to_i}
|
209
|
+
send_payload(parameters)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module CopperEgg
|
2
|
+
module APM
|
3
|
+
class BenchmarkMethodsTable
|
4
|
+
def benchmarkable_methods
|
5
|
+
@benchmarkable_methods ||= CopperEgg::APM.benchmarkable_methods.sort_by(&:display_name)
|
6
|
+
end
|
7
|
+
|
8
|
+
def excluded_methods
|
9
|
+
benchmarkable_methods.select {|method| method.excluded?}
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_column_width
|
13
|
+
benchmarkable_methods.reduce(0) do |memo, method|
|
14
|
+
method.display_name.size > memo ? method.display_name.size : memo
|
15
|
+
end + column_padding
|
16
|
+
end
|
17
|
+
|
18
|
+
def source_filename_column_width
|
19
|
+
benchmarkable_methods.reduce(0) do |memo, method|
|
20
|
+
method.display_filename.size > memo ? method.display_filename.size : memo
|
21
|
+
end + column_padding
|
22
|
+
end
|
23
|
+
|
24
|
+
def column_padding
|
25
|
+
2
|
26
|
+
end
|
27
|
+
|
28
|
+
def separator
|
29
|
+
"+" + "-"*(method_column_width+1) + "+" + "-"*(source_filename_column_width+1) + "+" + "--------------+"
|
30
|
+
end
|
31
|
+
|
32
|
+
def header_columns
|
33
|
+
"| Method" + " "*(method_column_width - 6).abs + "| Source Location" + " "*(source_filename_column_width - 15).abs + "| Benchmarked? |"
|
34
|
+
end
|
35
|
+
|
36
|
+
def method_columns(method)
|
37
|
+
"| #{method.display_name}" + " "*(method_column_width - method.display_name.size) + "| #{method.display_filename}" + " "*(source_filename_column_width - method.display_filename.size) + "| #{method.excluded? ? "NO " : "YES"} |"
|
38
|
+
end
|
39
|
+
|
40
|
+
def classes_count
|
41
|
+
benchmarkable_methods.map(&:owner).uniq!.size
|
42
|
+
end
|
43
|
+
|
44
|
+
def file_count
|
45
|
+
benchmarkable_methods.map(&:source_filename).uniq!.size
|
46
|
+
end
|
47
|
+
|
48
|
+
def print_table
|
49
|
+
if RUBY_VERSION < "1.9"
|
50
|
+
puts "Method benchmarking is not available in Ruby #{RUBY_VERSION}. It is only available in Ruby 1.9 or later."
|
51
|
+
elsif CopperEgg::APM::Configuration.benchmark_methods_level == :disabled
|
52
|
+
puts "Method benchmarking is disabled. You can enable method benchmarking by setting the benchmark_methods configuration value."
|
53
|
+
elsif benchmarkable_methods.size == 0
|
54
|
+
puts "No methods benchmarked"
|
55
|
+
else
|
56
|
+
puts
|
57
|
+
puts "#{separator}\n#{header_columns}\n#{separator}\n#{benchmarkable_methods.map {|method| method_columns(method)}.join("\n")}\n#{separator}"
|
58
|
+
puts "#{benchmarkable_methods.size} methods defined in #{classes_count} classes across #{file_count} files. #{benchmarkable_methods.size - excluded_methods.size == 1 ? "1 method" : "#{benchmarkable_methods.size - excluded_methods.size} methods"} benchmarked."
|
59
|
+
puts
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'date'
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module CopperEgg
|
6
|
+
module APM
|
7
|
+
class Configuration
|
8
|
+
BENCHMARK_METHOD_LEVELS = [:disabled, :basic, :moderate, :full, :custom]
|
9
|
+
|
10
|
+
@@udp_port = 28344
|
11
|
+
@@udp_host = "127.0.0.1"
|
12
|
+
@@app_root = ""
|
13
|
+
@@instrument_key = nil
|
14
|
+
@@rum_short_url = false
|
15
|
+
@@rum_beacon_url = "http://bacon.copperegg.com/bacon.gif"
|
16
|
+
@@gem_root = File.dirname(File.dirname(__FILE__))
|
17
|
+
@@log_to = nil
|
18
|
+
@@benchmark_sql = true
|
19
|
+
@@benchmark_active_record = false
|
20
|
+
@@benchmark_http = true
|
21
|
+
@@benchmark_exceptions = true
|
22
|
+
@@benchmark_methods_level = :disabled
|
23
|
+
@@only_methods = []
|
24
|
+
@@exclude_methods = []
|
25
|
+
@@include_methods = []
|
26
|
+
@@logger = nil
|
27
|
+
|
28
|
+
def self.udp_port
|
29
|
+
@@udp_port
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.udp_host
|
33
|
+
@@udp_host
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.rum_beacon_url
|
37
|
+
@@rum_beacon_url
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.gem_root
|
41
|
+
@@gem_root
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.instrument_key=(key)
|
45
|
+
raise ConfigurationError.new("invalid instrument key") if !key =~ /\A[a-z0-9]+\z/i
|
46
|
+
@@instrument_key = key
|
47
|
+
create_logfile if @@log_to
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.instrument_key
|
51
|
+
@@instrument_key
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.rum_short_url=(boolean)
|
55
|
+
raise ConfigurationError.new("RUM short url must be a boolean") if boolean != true && boolean != false
|
56
|
+
@@rum_short_url = boolean
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.rum_short_url
|
60
|
+
@@rum_short_url
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.app_root=(path)
|
64
|
+
@@app_root = path.to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.app_root
|
68
|
+
@@app_root
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.benchmark_sql=(boolean)
|
72
|
+
raise ConfigurationError.new("Boolean expected for benchmark_sql") if boolean != true && boolean != false
|
73
|
+
@@benchmark_sql = boolean
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.benchmark_sql?
|
77
|
+
@@benchmark_sql
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.benchmark_active_record=(boolean)
|
81
|
+
raise ConfigurationError.new("Boolean expected for benchmark_active_record") if boolean != true && boolean != false
|
82
|
+
@@benchmark_sql = boolean
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.benchmark_active_record?
|
86
|
+
@@benchmark_active_record
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.benchmark_http=(boolean)
|
90
|
+
raise ConfigurationError.new("Boolean expected for benchmark_http") if boolean != true && boolean != false
|
91
|
+
@@benchmark_http = boolean
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.benchmark_http?
|
95
|
+
@@benchmark_http
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.benchmark_exceptions=(boolean)
|
99
|
+
raise ConfigurationError.new("Boolean expected for benchmark_exceptions") if boolean != true && boolean != false
|
100
|
+
@@benchmark_exceptions = boolean
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.benchmark_exceptions?
|
104
|
+
@@benchmark_exceptions
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.benchmark_methods(level, options={})
|
108
|
+
raise ConfigurationError.new("Method benchmark level can only be :disabled, :basic, :moderate, :full, or :custom") if !BENCHMARK_METHOD_LEVELS.include?(level)
|
109
|
+
|
110
|
+
@@benchmark_methods_level = level
|
111
|
+
return if level == :disabled
|
112
|
+
|
113
|
+
if level == :custom
|
114
|
+
benchmark_methods_option(options, :@@only_methods)
|
115
|
+
else
|
116
|
+
benchmark_methods_option(options[:include], :@@include_methods) if options[:include]
|
117
|
+
benchmark_methods_option(options[:exclude], :@@exclude_methods) if options[:exclude]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.benchmark_methods_level
|
122
|
+
@@benchmark_methods_level
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.only_methods
|
126
|
+
@@only_methods || []
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.include_methods
|
130
|
+
@@include_methods || []
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.exclude_methods
|
134
|
+
@@exclude_methods || []
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.log(payload)
|
138
|
+
return if @@logger == nil
|
139
|
+
@@logger.debug "Payload sent at #{DateTime.strptime(Time.now.to_i.to_s, '%s').strftime('%Y-%m-%d %H:%M:%S')} #{payload.bytesize} bytes\n"
|
140
|
+
@@logger.debug payload.split("\x00").select {|i| i.size > 2}.map {|i| i.sub(/^[^\{]+/,'')}.join("\n")
|
141
|
+
@@logger.debug ""
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.enable_logging=(boolean)
|
145
|
+
raise ConfigurationError.new("Boolean expected for logging_enabled") if boolean != true && boolean != false
|
146
|
+
dir = '/tmp'
|
147
|
+
raise ConfigurationError.new("Directory #{dir} must be readable and writable.") if !File.readable?(dir) || !File.writable?(dir)
|
148
|
+
@@log_to = dir
|
149
|
+
if @@instrument_key
|
150
|
+
create_logfile
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.configure(&block)
|
155
|
+
yield(self)
|
156
|
+
|
157
|
+
if @@app_root.empty?
|
158
|
+
if defined?(::Rails) && ::Rails.respond_to?(:configuration)
|
159
|
+
@@app_root = ::Rails.configuration.root.to_s
|
160
|
+
else
|
161
|
+
@@app_root = File.dirname(caller[1])
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
CopperEgg::APM.add_method_benchmarking if @@benchmark_methods_level != :disabled
|
166
|
+
end
|
167
|
+
|
168
|
+
class <<self
|
169
|
+
private
|
170
|
+
|
171
|
+
def benchmark_methods_option(array, class_variable_name)
|
172
|
+
raise ConfigurationError.new("Array expected for benchmark method option") if !array.is_a?(Array)
|
173
|
+
array.each do |value|
|
174
|
+
raise ConfigurationError.new("Invalid item #{value} in benchmark method option. String expected.") if !value.is_a?(String)
|
175
|
+
end
|
176
|
+
class_variable_set(class_variable_name, array)
|
177
|
+
end
|
178
|
+
|
179
|
+
def create_logfile
|
180
|
+
logdir = File.join(@@log_to, 'copperegg', 'apm')
|
181
|
+
FileUtils.mkdir_p(logdir) unless File.directory?(logdir)
|
182
|
+
|
183
|
+
@@logger = Logger.new(File.join(logdir, "#{@@instrument_key}.log"), 0)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|