scout 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,120 @@
1
+ require "rake/rdoctask"
2
+ require "rake/testtask"
3
+ require "rake/gempackagetask"
4
+ require "rake/contrib/rubyforgepublisher"
5
+ require "net/ssh"
6
+
7
+ require "rubygems"
8
+ require "rubyforge"
9
+
10
+ dir = File.dirname(__FILE__)
11
+ lib = File.join(dir, "lib", "scout.rb")
12
+ version = File.read(lib)[/^\s*VERSION\s*=\s*(['"])(\d\.\d\.\d)\1/, 2]
13
+ history = File.read("CHANGELOG").split(/^(===.*)/)
14
+ changes ||= history[0..2].join.strip
15
+
16
+ need_tar = true
17
+ need_zip = true
18
+
19
+ task :default => [:test]
20
+
21
+ Rake::TestTask.new do |test|
22
+ test.libs << "test"
23
+ test.test_files = [ "test/scout_test.rb" ]
24
+ test.verbose = true
25
+ end
26
+
27
+ Rake::RDocTask.new do |rdoc|
28
+ rdoc.main = "README"
29
+ rdoc.rdoc_dir = "doc/html"
30
+ rdoc.title = "Scout Client Documentation"
31
+ rdoc.rdoc_files.include( "README", "INSTALL",
32
+ "TODO", "CHANGELOG",
33
+ "AUTHORS", "COPYING",
34
+ "LICENSE", "lib/" )
35
+ end
36
+
37
+ desc "Upload current documentation to Scout Gem Server"
38
+ task :upload_docs => [:rdoc] do
39
+ sh "scp -r doc/html/* " +
40
+ "deploy@gems.scoutapp.com:/var/www/gems/docs"
41
+ end
42
+
43
+ spec = Gem::Specification.new do |spec|
44
+ spec.name = "scout"
45
+ spec.version = version
46
+
47
+ spec.platform = Gem::Platform::RUBY
48
+ spec.summary = "Scout makes monitoring and reporting on your web applications as flexible and simple as possible."
49
+
50
+ # TODO: test suite
51
+ # spec.test_suite_file = "test/ts_all.rb"
52
+ spec.files = Dir.glob("{lib,test,examples}/**/*.rb").
53
+ reject { |item| item.include?(".svn") } +
54
+ Dir.glob("{test,examples}/**/*.csv").
55
+ reject { |item| item.include?(".svn") } +
56
+ ["Rakefile", "setup.rb"]
57
+ spec.executables = ["scout"]
58
+
59
+ spec.has_rdoc = true
60
+ spec.extra_rdoc_files = %w[ AUTHORS COPYING README INSTALL TODO CHANGELOG
61
+ LICENSE ]
62
+ spec.rdoc_options << "--title" << "Scout Client Documentation" <<
63
+ "--main" << "README"
64
+
65
+ spec.require_path = "lib"
66
+
67
+ spec.add_dependency "elif"
68
+ # spec.add_dependency "hpricot", "=0.6"
69
+
70
+ spec.author = "Highgroove Studios"
71
+ spec.email = "scout@highgroove.com"
72
+ spec.rubyforge_project = "scout"
73
+ spec.homepage = "http://scoutapp.com"
74
+ spec.description = <<END_DESC
75
+ Scout makes monitoring and reporting on your web applications as flexible and simple as possible.
76
+
77
+ Scout is a product of Highgroove Studios.
78
+ END_DESC
79
+ end
80
+
81
+ Rake::GemPackageTask.new(spec) do |pkg|
82
+ pkg.need_zip = need_tar
83
+ pkg.need_tar = need_zip
84
+ end
85
+
86
+ desc "Publish Gem to Scout Gem Server"
87
+ task :publish => [:package] do
88
+ pkg = "pkg/#{spec.name}-#{spec.version}"
89
+
90
+ if $DEBUG then
91
+ puts "release_id = rf.add_release #{spec.rubyforge_project.inspect}, #{spec.name.inspect}, #{spec.version.inspect}, \"#{pkg}.tgz\""
92
+ puts "rf.add_file #{spec.rubyforge_project.inspect}, #{spec.name.inspect}, release_id, \"#{pkg}.gem\""
93
+ end
94
+
95
+ puts "Publishing on RubyForge"
96
+ rf = RubyForge.new
97
+ puts "Logging in"
98
+ rf.login
99
+
100
+ c = rf.userconfig
101
+ puts rf.inspect
102
+ c["release_notes"] = spec.description if spec.description
103
+ c["release_changes"] = changes if changes
104
+ c["preformatted"] = true
105
+
106
+ files = [(need_tar ? "#{pkg}.tgz" : nil),
107
+ (need_zip ? "#{pkg}.zip" : nil),
108
+ "#{pkg}.gem"].compact
109
+
110
+ puts "Releasing #{spec.name} v. #{spec.version}"
111
+ rf.add_release spec.rubyforge_project, spec.name, spec.version, *files
112
+
113
+ puts "Publishing on Scout Server"
114
+ sh "scp -r pkg/*.gem " +
115
+ "deploy@gems.scoutapp.com:/var/www/gems/gems"
116
+ ssh = Net::SSH.start('gems.scoutapp.com','deploy')
117
+ ssh_shell = ssh.shell.sync
118
+ ssh_out = ssh_shell.send_command "/usr/bin/index_gem_repository.rb -d /var/www/gems"
119
+ puts "Published, and updated gem server." if ssh_out.stdout.empty? && !ssh_out.stderr
120
+ end
data/TODO ADDED
@@ -0,0 +1,6 @@
1
+ = To Do List
2
+
3
+ The following is a list of planned expansions for the Scout Client:
4
+
5
+ * optionally find the "nice" command if available and use that in the
6
+ crontab output
data/bin/scout ADDED
@@ -0,0 +1,200 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $VERBOSE = true # -w
4
+ $KCODE = "u" # -Ku
5
+
6
+ $LOAD_PATH << File.join(File.dirname(__FILE__), *%w[.. lib])
7
+ require "scout"
8
+ require "optparse"
9
+ require "logger"
10
+ require "fileutils"
11
+ require "pp"
12
+
13
+ CONFIG_DIR = File.join((File.expand_path("~") rescue "/"), ".scout")
14
+ USER = ENV["USER"] || ENV["USERNAME"] || "root"
15
+
16
+ options = { :server => "https://scoutapp.com/",
17
+ :history => File.join(CONFIG_DIR , "client_history.yaml"),
18
+ :verbose => false,
19
+ :level => "info" }
20
+
21
+ ARGV.options do |opts|
22
+ opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} [OPTIONS] CLIENT_KEY"
23
+
24
+ opts.separator ""
25
+ opts.separator "CLIENT_KEY is the indentification key assigned to this " +
26
+ "client by the server."
27
+ opts.separator ""
28
+ opts.separator "Note: This client is meant to be installed and invoked " +
29
+ "through cron or any other scheduler."
30
+ opts.separator ""
31
+ opts.separator "Specific Options:"
32
+
33
+ opts.on( "-s", "--server SERVER", String,
34
+ "The URL for the server this client reports to." ) do |url|
35
+ options[:server] = url
36
+ end
37
+ opts.on( "-p", "--plugin PLUGIN", String,
38
+ "The path to a plugin to run locally. " +
39
+ "Useful for Testing." ) do |plugin|
40
+ options[:plugin] = plugin
41
+ end
42
+
43
+ opts.separator ""
44
+
45
+ opts.on( "-d", "--data DATA", String,
46
+ "The data file used to track the history of executions." ) do |file|
47
+ options[:history] = file
48
+ end
49
+ opts.on( "-l", "--level LEVEL", Logger::SEV_LABEL.map { |l| l.downcase },
50
+ "The level of logging to report." ) do |level|
51
+ options[:level] = level
52
+ end
53
+ opts.on( "-o", "--plugin-options PLUGIN_OPTIONS", String,
54
+ "The options YAML file to pass to the plugin." ) do |plugin_options|
55
+ options[:plugin_options] = plugin_options
56
+ end
57
+
58
+ opts.separator "Common Options:"
59
+
60
+ opts.on( "-h", "--help",
61
+ "Show this message." ) do
62
+ puts opts
63
+ exit
64
+ end
65
+ opts.on( "-v", "--[no-]verbose",
66
+ "Turn on logging to STDOUT" ) do |bool|
67
+ options[:verbose] = bool
68
+ end
69
+
70
+ begin
71
+ opts.parse!
72
+ options[:client_key] = ARGV.shift if ARGV.size == 1
73
+ rescue
74
+ puts opts
75
+ exit
76
+ end
77
+ end
78
+
79
+ log = Logger.new($stdout)
80
+ log.datetime_format = "%Y-%m-%d %H:%M "
81
+ log.level = Logger.const_get(options[:level].upcase) \
82
+ rescue Logger::INFO
83
+
84
+ real_config_dir = File.dirname(options[:history])
85
+ FileUtils.mkdir_p(real_config_dir) # ensure dir exists
86
+
87
+ # make sure only one copy is ever running at a time
88
+ pid_file = File.join(real_config_dir, "scout_client_pid.txt")
89
+ begin
90
+ File.open(pid_file, File::CREAT|File::EXCL|File::WRONLY) { |pid| pid.puts $$ }
91
+ at_exit do
92
+ begin
93
+ File.unlink(pid_file)
94
+ rescue
95
+ log.error "Unable to unlink pid file: #{$!.message}"
96
+ end
97
+ end
98
+ rescue
99
+ pid = File.read(pid_file).strip.to_i rescue "unknown"
100
+ running = true
101
+ begin
102
+ Process.kill(0, pid)
103
+ rescue Errno::ESRCH
104
+ running = false
105
+ rescue
106
+ # do nothing, we didn't have permission to the running process
107
+ end
108
+ if running
109
+ log.warn "Process #{pid} was already running"
110
+ exit
111
+ else
112
+ log.info "Stale PID file found. Clearing it and reloading..."
113
+ File.unlink(pid_file)
114
+ retry
115
+ end
116
+ end
117
+
118
+ if [:client_key, :plugin].all? { |o| options[o].nil? } and
119
+ $stdin.tty? # install wizard
120
+ puts <<-END_INTRO.gsub(/^ {2}/, "")
121
+ == Scout Installation Wizard ==
122
+
123
+ You need the Client Key displayed in the Client Settings
124
+ tab. It looks like:
125
+
126
+ 6ecad322-0d17-4cb8-9b2c-a12c4541853f
127
+
128
+ Enter the Client Key:
129
+ END_INTRO
130
+ options[:client_key] = gets.to_s.chomp!
131
+
132
+ # puts "Attempting to contact the server..."
133
+ begin
134
+ Scout::Server.new( options[:server],
135
+ options[:client_key],
136
+ options[:history],
137
+ options[:verbose] ? log : nil ) { |server| server.test }
138
+
139
+ puts <<-END_SUCCESS.gsub(/^ {4}/, "")
140
+
141
+ Success!
142
+
143
+ ******* NOW, INSTALL IN CRONTAB *******
144
+
145
+ */10 * * * * #{USER} #{File.expand_path($PROGRAM_NAME)} #{options[:client_key]}
146
+
147
+ ******* END CRONTAB SAMPLE *******
148
+
149
+ For help setting up Scout with crontab, please visit:
150
+
151
+ http://scoutapp.com/help#cron
152
+
153
+ END_SUCCESS
154
+ rescue SystemExit
155
+ puts <<-END_ERROR.gsub(/^ {4}/, "")
156
+
157
+ Could not contact server. The client key may be incorrect. For more help,
158
+ please visit:
159
+
160
+ http://scoutapp.com/help
161
+
162
+ END_ERROR
163
+ end
164
+ elsif options[:plugin] # local plugin
165
+ # read the plugin_code from the file specified
166
+ plugin_code = File.read(options[:plugin])
167
+
168
+ plugin_options = if options[:plugin_options].to_s[0..0] == "{"
169
+ eval(options[:plugin_options]) # options from command-line
170
+ elsif options[:plugin_options]
171
+ #
172
+ # read the plugin_options from the YAML file specified,
173
+ # parse each option and use the default value specified
174
+ # in the options as the value to be passed to the test plugin
175
+ #
176
+ Hash[ *File.open(options[:plugin_options]) { |f| YAML.load(f) }["options"].
177
+ map { |name, details| [name, details["default"]] }.flatten ]
178
+ else
179
+ Hash.new
180
+ end
181
+
182
+ Scout::Server.new( nil,
183
+ options[:client_key],
184
+ options[:history],
185
+ options[:verbose] ? log : nil ) do |server|
186
+ pp server.process_plugin( { :interval => 0,
187
+ :plugin_id => 1,
188
+ :name => "Local Plugin",
189
+ :code => plugin_code,
190
+ :options => plugin_options,
191
+ :path => options[:plugin] } )
192
+ end
193
+ else # normal run
194
+ Scout::Server.new( options[:server],
195
+ options[:client_key],
196
+ options[:history],
197
+ options[:verbose] ? log : nil ) do |server|
198
+ server.run_plugins_by_plan
199
+ end
200
+ end
data/lib/scout.rb ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby -wKU
2
+
3
+ require "scout/plugin"
4
+ require "scout/server"
5
+
6
+ module Scout
7
+ VERSION = "1.1.0".freeze
8
+ end
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby -wKU
2
+
3
+ module Scout
4
+ class Plugin
5
+ class << self
6
+ attr_reader :last_defined
7
+
8
+ def inherited(new_plugin)
9
+ @last_defined = new_plugin
10
+ end
11
+
12
+ def load(last_run, memory, options)
13
+ new(last_run, memory, options)
14
+ end
15
+ end
16
+
17
+ # Creates a new Scout Plugin to run.
18
+ #
19
+ def initialize(last_run, memory, options)
20
+ @last_run = last_run
21
+ @memory = memory
22
+ @options = options
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,285 @@
1
+ #!/usr/bin/env ruby -wKU
2
+
3
+ require "net/https"
4
+ require "uri"
5
+ require "yaml"
6
+ require "timeout"
7
+
8
+ module Scout
9
+ class Server
10
+ # A new class for plugin Timeout errors.
11
+ class PluginTimeoutError < RuntimeError; end
12
+
13
+ # The default URLS are used to communicate with the Scout Server.
14
+ URLS = { :plan => "/clients/CLIENT_KEY/plugins.scout?version=CLIENT_VERSION",
15
+ :report => "/clients/CLIENT_KEY/plugins/PLUGIN_ID/reports.scout?version=CLIENT_VERSION",
16
+ :error => "/clients/CLIENT_KEY/plugins/PLUGIN_ID/errors.scout?version=CLIENT_VERSION",
17
+ :alert => "/clients/CLIENT_KEY/plugins/PLUGIN_ID/alerts.scout?version=CLIENT_VERSION" }
18
+
19
+ #
20
+ # A plugin cannot take more than PLUGIN_TIMEOUT seconds to execute,
21
+ # otherwise, a timeout error is generated.
22
+ #
23
+ PLUGIN_TIMEOUT = 60
24
+
25
+ # Creates a new Scout Server connection.
26
+ def initialize(server, client_key, history_file, logger = nil)
27
+ @server = server
28
+ @client_key = client_key
29
+ @history_file = history_file
30
+ @history = Hash.new
31
+ @logger = logger
32
+
33
+ if block_given?
34
+ load_history
35
+ yield self
36
+ save_history
37
+ end
38
+ end
39
+
40
+ #
41
+ # Loads the history file from disk. If the file does not exist,
42
+ # it creates one.
43
+ #
44
+ def load_history
45
+ unless File.exist? @history_file
46
+ debug "Creating empty history file..."
47
+ File.open(@history_file, "w") do |file|
48
+ YAML.dump({"last_runs" => Hash.new, "memory" => Hash.new}, file)
49
+ end
50
+ info "History file created."
51
+ end
52
+ debug "Loading history file..."
53
+ @history = File.open(@history_file) { |file| YAML.load(file) }
54
+ info "History file loaded."
55
+ end
56
+
57
+ # Saves the history file to disk.
58
+ def save_history
59
+ debug "Saving history file..."
60
+ File.open(@history_file, "w") { |file| YAML.dump(@history, file) }
61
+ info "History file saved."
62
+ end
63
+
64
+ # Runs all plugins from a given plan. Calls process_plugin on each plugin.
65
+ def run_plugins_by_plan
66
+ plan do |plugin|
67
+ process_plugin(plugin)
68
+ end
69
+ end
70
+
71
+ #
72
+ # This is the heart of Scout.
73
+ #
74
+ # First, it determines if a plugin is past interval and needs to be run.
75
+ # If it is, it simply evals the code, compiling it.
76
+ # It then loads the plugin and runs it with a PLUGIN_TIMEOUT time limit.
77
+ # The plugin generates data, alerts, and errors. In addition, it will
78
+ # set memory and last_run information in the history file.
79
+ #
80
+ def process_plugin(plugin)
81
+ info "Processing the #{plugin[:name]} plugin:"
82
+ last_run = @history["last_runs"][plugin[:name]]
83
+ memory = @history["memory"][plugin[:name]]
84
+ run_time = Time.now
85
+ if last_run.nil? or run_time > last_run + plugin[:interval]
86
+ debug "Plugin is past interval and needs to be run. " +
87
+ "(last run: #{last_run || 'nil'})"
88
+ debug "Compiling plugin..."
89
+ begin
90
+ eval(plugin[:code], TOPLEVEL_BINDING, plugin[:path] || plugin[:name])
91
+ info "Plugin compiled."
92
+ rescue Exception
93
+ error "Plugin would not compile: #{$!.message}"
94
+ return
95
+ end
96
+ debug "Loading plugin..."
97
+ if job = Plugin.last_defined.load( last_run, (memory || Hash.new),
98
+ plugin[:options] || Hash.new )
99
+ info "Plugin loaded."
100
+ debug "Running plugin..."
101
+ begin
102
+ data = {}
103
+ Timeout.timeout(PLUGIN_TIMEOUT, PluginTimeoutError) do
104
+ data = job.run
105
+ end
106
+ rescue Timeout::Error
107
+ error "Plugin took too long to run."
108
+ return
109
+ rescue Exception
110
+ error "Plugin failed to run: #{$!.backtrace}"
111
+ end
112
+ info "Plugin completed its run."
113
+
114
+ # handle single report or array of reports
115
+ send_report(data[:report], plugin[:plugin_id]) if data[:report]
116
+ if data[:reports] and not data[:reports].empty?
117
+ data[:reports].each { |r| send_report(r, plugin[:plugin_id]) }
118
+ end
119
+ # handle single alert or array of alerts
120
+ send_alert(data[:alert], plugin[:plugin_id]) if data[:alert]
121
+ if data[:alerts] and not data[:alerts].empty?
122
+ data[:alerts].each { |a| send_alert(a, plugin[:plugin_id]) }
123
+ end
124
+ # handle single error or array of errors
125
+ send_error(data[:error], plugin[:plugin_id]) if data[:error]
126
+ if data[:errors] and not data[:errors].empty?
127
+ data[:errors].each { |e| send_error(e, plugin[:plugin_id]) }
128
+ end
129
+
130
+ @history["last_runs"][plugin[:name]] = run_time
131
+ @history["memory"][plugin[:name]] = data[:memory]
132
+ else
133
+ scout_error({:subject => "Plugin would not load."}, plugin[:plugin_id])
134
+ end
135
+ else
136
+ debug "Plugin does not need to be run at this time. " +
137
+ "(last run: #{last_run || 'nil'})"
138
+ end
139
+ info "Plugin #{plugin[:name]} processing complete."
140
+ data
141
+ end
142
+
143
+ #
144
+ # Retrieves the Plugin Plan from the server. This is the list of plugins
145
+ # to execute, along with all options.
146
+ #
147
+ def plan
148
+ url = urlify(:plan)
149
+ info "Loading plan from #{url}..."
150
+ get(url, "Could not retrieve plan from server.") do |res|
151
+ begin
152
+ plugin_execution_plan = Marshal.load(res.body)
153
+ info "Plan loaded. (#{plugin_execution_plan.size} plugins: " +
154
+ "#{plugin_execution_plan.map { |p| p[:name] }.join(', ')})"
155
+ rescue TypeError
156
+ fatal "Plan from server was malformed."
157
+ exit
158
+ end
159
+ plugin_execution_plan.each do |plugin|
160
+ begin
161
+ yield plugin if block_given?
162
+ rescue RuntimeError
163
+ scout_error( { :subject => "Exception: #{$!.message}.",
164
+ :body => $!.backtrace },
165
+ plugin[:plugin_id] )
166
+ end
167
+ end
168
+ end
169
+ end
170
+ alias_method :test, :plan
171
+
172
+ # Sends report data to the Scout Server.
173
+ def send_report(data, plugin_id)
174
+ url = urlify(:report, :plugin_id => plugin_id)
175
+ report_hash = {:data => data, :plugin_id => plugin_id}
176
+
177
+ # add in any special fields
178
+ if time = ( data.delete(:scout_time) || data.delete("scout_time") )
179
+ report_hash[:time] = time
180
+ end
181
+
182
+ debug "Sending report to #{url} (#{data.inspect})..."
183
+ post url,
184
+ "Unable to send report to server.",
185
+ :report => report_hash
186
+ info "Report sent."
187
+ end
188
+
189
+ # Sends an alert to the Scout Server.
190
+ def send_alert(data, plugin_id)
191
+ url = urlify(:alert, :plugin_id => plugin_id)
192
+ debug "Sending alert to #{url} (subject: #{data[:subject]})..."
193
+ post url,
194
+ "Unable to send alert to server.",
195
+ :alert => data.merge(:plugin_id => plugin_id)
196
+ info "Alert sent."
197
+ end
198
+
199
+ # Sends an error to the Scout Server.
200
+ def send_error(data, plugin_id)
201
+ url = urlify(:error, :plugin_id => plugin_id)
202
+ debug "Sending error to #{url} (subject: #{data[:subject]})..."
203
+ post url,
204
+ "Unable to log error on server.",
205
+ :error => data.merge(:plugin_id => plugin_id)
206
+ info "Error sent."
207
+ end
208
+
209
+ private
210
+
211
+ def urlify(url_name, options = Hash.new)
212
+ return unless @server
213
+ options.merge!(:client_version => Scout::VERSION)
214
+ URI.join( @server,
215
+ URLS[url_name].
216
+ gsub(/\bCLIENT_KEY\b/, @client_key).
217
+ gsub(/\b[A-Z_]+\b/) { |k| options[k.downcase.to_sym] || k } )
218
+ end
219
+
220
+ def paramify(params, prefix = nil)
221
+ params.inject(Hash.new) do |all, (key, value)|
222
+ parent = prefix ? "#{prefix}[#{key}]" : String(key)
223
+ if value.is_a? Hash
224
+ all.merge(paramify(value, parent))
225
+ else
226
+ all.merge(parent => String(value))
227
+ end
228
+ end
229
+ end
230
+
231
+ def post(url, error, params = {}, &response_handler)
232
+ return unless url
233
+ request(url, response_handler, error) do |connection|
234
+ post = Net::HTTP::Post.new(url.path + (url.query ? ('?' + url.query) : ''))
235
+ post.set_form_data(paramify(params))
236
+ connection.request(post)
237
+ end
238
+ end
239
+
240
+ def get(url, error, params = {}, &response_handler)
241
+ return unless url
242
+ request(url, response_handler, error) do |connection|
243
+ connection.get(url.path + (url.query ? ('?' + url.query) : ''))
244
+ end
245
+ end
246
+
247
+ def request(url, response_handler, error, &connector)
248
+ http = Net::HTTP.new(url.host, url.port)
249
+ if url.is_a? URI::HTTPS
250
+ http.use_ssl = true
251
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
252
+ end
253
+ case response = no_warnings { http.start(&connector) }
254
+ when Net::HTTPSuccess
255
+ response_handler[response] unless response_handler.nil?
256
+ else
257
+ fatal error
258
+ exit
259
+ end
260
+ rescue Timeout::Error
261
+ fatal "Request timed out."
262
+ exit
263
+ rescue Exception
264
+ fatal "An HTTP error occurred: #{$!.message}"
265
+ exit
266
+ end
267
+
268
+ def no_warnings
269
+ old_verbose = $VERBOSE
270
+ $VERBOSE = false
271
+ yield
272
+ ensure
273
+ $VERBOSE = old_verbose
274
+ end
275
+
276
+ # Forward Logger methods to an active instance, when there is one.
277
+ def method_missing(meth, *args, &block)
278
+ if (Logger::SEV_LABEL - %w[ANY]).include? meth.to_s.upcase
279
+ @logger.send(meth, *args, &block) unless @logger.nil?
280
+ else
281
+ super
282
+ end
283
+ end
284
+ end
285
+ end