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