indy 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +18 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/History.txt +11 -0
- data/README.md +132 -0
- data/Rakefile +68 -0
- data/autotest/discover.rb +2 -0
- data/cucumber.yml +6 -0
- data/features/after_time.feature +41 -0
- data/features/application.feature +33 -0
- data/features/around_time.feature +34 -0
- data/features/before_time.feature +34 -0
- data/features/custom_pattern.feature +52 -0
- data/features/exact_log_level.feature +35 -0
- data/features/exact_message.feature +33 -0
- data/features/exact_mulitple_fields.feature +38 -0
- data/features/exact_time.feature +28 -0
- data/features/file.feature +30 -0
- data/features/log_levels.feature +40 -0
- data/features/message.feature +39 -0
- data/features/multiple_fields.feature +38 -0
- data/features/step_definitions/find_by.steps.rb +55 -0
- data/features/step_definitions/log_file.steps.rb +8 -0
- data/features/step_definitions/support/env.rb +1 -0
- data/features/step_definitions/support/transforms.rb +28 -0
- data/features/step_definitions/test_setup.steps.rb +4 -0
- data/features/step_definitions/test_teardown.steps.rb +0 -0
- data/features/step_definitions/time.steps.rb +29 -0
- data/features/within_time.feature +41 -0
- data/indy.gemspec +61 -0
- data/lib/indy.rb +5 -0
- data/lib/indy/indy.rb +463 -0
- data/lib/indy/result_set.rb +8 -0
- data/performance/helper.rb +5 -0
- data/performance/profile_spec.rb +35 -0
- data/spec/data.log +2 -0
- data/spec/helper.rb +4 -0
- data/spec/indy_spec.rb +212 -0
- data/spec/result_set_spec.rb +9 -0
- data/spec/search_spec.rb +97 -0
- data/spec/time_spec.rb +80 -0
- metadata +126 -0
data/indy.gemspec
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/lib/indy"
|
2
|
+
|
3
|
+
module Indy
|
4
|
+
|
5
|
+
def self.show_version_changes(version)
|
6
|
+
date = ""
|
7
|
+
changes = []
|
8
|
+
grab_changes = false
|
9
|
+
|
10
|
+
File.open("#{File.dirname(__FILE__)}/History.txt",'r') do |file|
|
11
|
+
while (line = file.gets) do
|
12
|
+
|
13
|
+
if line =~ /^===\s*#{version.gsub('.','\.')}\s*\/\s*(.+)\s*$/
|
14
|
+
grab_changes = true
|
15
|
+
date = $1.strip
|
16
|
+
elsif line =~ /^===\s*.+$/
|
17
|
+
grab_changes = false
|
18
|
+
elsif grab_changes
|
19
|
+
changes = changes << line
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
{ :date => date, :changes => changes }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Gem::Specification.new do |s|
|
30
|
+
s.name = 'indy'
|
31
|
+
s.version = ::Indy::VERSION
|
32
|
+
s.authors = ["Franklin Webber","Brandon Faloona"]
|
33
|
+
s.description = %{ Indy is a log archelogy tool that allows you to search through log files. }
|
34
|
+
s.summary = "Log Search Tool"
|
35
|
+
s.email = 'franklin.webber@gmail.com'
|
36
|
+
s.homepage = "http://github.com/burtlo/Indy"
|
37
|
+
s.license = 'MIT'
|
38
|
+
|
39
|
+
s.platform = Gem::Platform::RUBY
|
40
|
+
s.required_ruby_version = '>= 1.8.7'
|
41
|
+
s.add_dependency('activesupport', '>= 2.3.5')
|
42
|
+
|
43
|
+
changes = Indy.show_version_changes(::Indy::VERSION)
|
44
|
+
|
45
|
+
s.post_install_message = %{
|
46
|
+
[<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>]
|
47
|
+
|
48
|
+
Thank you for installing Indy #{::Indy::VERSION} / #{changes[:date]}.
|
49
|
+
|
50
|
+
Changes:
|
51
|
+
#{changes[:changes].collect{|change| " #{change}"}.join("")}
|
52
|
+
[<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>] [<>]
|
53
|
+
|
54
|
+
}
|
55
|
+
|
56
|
+
s.rubygems_version = "1.3.7"
|
57
|
+
s.files = `git ls-files`.split("\n")
|
58
|
+
s.extra_rdoc_files = ["README.md", "History.txt"]
|
59
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
60
|
+
s.require_path = "lib"
|
61
|
+
end
|
data/lib/indy.rb
ADDED
data/lib/indy/indy.rb
ADDED
@@ -0,0 +1,463 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
|
4
|
+
class Indy
|
5
|
+
|
6
|
+
VERSION = "0.1.1"
|
7
|
+
|
8
|
+
#
|
9
|
+
# string, file, or command that provides the log
|
10
|
+
#
|
11
|
+
attr_accessor :source
|
12
|
+
|
13
|
+
#
|
14
|
+
# array with regexp string and capture groups followed by log field
|
15
|
+
# name symbols. :time field is required to use time scoping
|
16
|
+
#
|
17
|
+
attr_accessor :pattern
|
18
|
+
|
19
|
+
#
|
20
|
+
# format string for explicit date/time format (optional)
|
21
|
+
#
|
22
|
+
attr_accessor :time_format
|
23
|
+
|
24
|
+
DATE_TIME = "\\d{4}.\\d{2}.\\d{2}\s+\\d{2}.\\d{2}.\\d{2}" #"%Y-%m-%d %H:%M:%S"
|
25
|
+
SEVERITY = [:trace,:debug,:info,:warn,:error,:fatal]
|
26
|
+
SEVERITY_PATTERN = "(?:#{SEVERITY.map{|s| s.to_s.upcase}.join("|")})"
|
27
|
+
APPLICATION = "\\w+"
|
28
|
+
MESSAGE = ".+"
|
29
|
+
|
30
|
+
DEFAULT_LOG_PATTERN = "^(#{DATE_TIME})\\s+(#{SEVERITY_PATTERN})\\s+(#{APPLICATION})\\s+-\\s+(#{MESSAGE})$"
|
31
|
+
DEFAULT_LOG_FIELDS = [:time,:severity,:application,:message]
|
32
|
+
|
33
|
+
FOREVER_AGO = DateTime.now - 200_000
|
34
|
+
FOREVER = DateTime.now + 200_000
|
35
|
+
|
36
|
+
|
37
|
+
#
|
38
|
+
# Initialize Indy.
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
#
|
42
|
+
# Indy.new(:source => LOG_FILENAME)
|
43
|
+
# Indy.new(:source => LOG_CONTENTS_STRING)
|
44
|
+
# Indy.new(:source => {:cmd => LOG_COMMAND_STRING})
|
45
|
+
# Indy.new(:pattern => [LOG_REGEX_PATTERN,:time,:application,:message],:source => LOG_FILENAME)
|
46
|
+
# Indy.new(:time_format => '%m-%d-%Y',:pattern => [LOG_REGEX_PATTERN,:time,:application,:message],:source => LOG_FILENAME)
|
47
|
+
#
|
48
|
+
def initialize(args)
|
49
|
+
@source = @pattern = nil
|
50
|
+
@source_info = Hash.new
|
51
|
+
|
52
|
+
while (arg = args.shift) do
|
53
|
+
send("#{arg.first}=",arg.last)
|
54
|
+
end
|
55
|
+
|
56
|
+
@pattern = @pattern || [DEFAULT_LOG_PATTERN,DEFAULT_LOG_FIELDS].flatten
|
57
|
+
@time_field = ( @pattern[1..-1].include?(:time) ? :time : nil )
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
class << self
|
62
|
+
|
63
|
+
#
|
64
|
+
# Create a new instance of Indy with @source, or multiple, parameters
|
65
|
+
# specified. This allows for a more fluent creation that moves
|
66
|
+
# into the execution.
|
67
|
+
#
|
68
|
+
# @param [String,Hash] params To specify @source, provide a filename or
|
69
|
+
# log contents as a string. To specify a command, use a :cmd => STRING hash.
|
70
|
+
# Alternately, a Hash with a :source key (amoung others) can be used to
|
71
|
+
# provide multiple initialization parameters.
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# Indy.search("apache.log").for(:severity => "INFO")
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# Indy.search("INFO 2000-09-07 MyApp - Entering APPLICATION.\nINFO 2000-09-07 MyApp - Entering APPLICATION.").for(:all)
|
78
|
+
#
|
79
|
+
# @example
|
80
|
+
# Indy.search(:cmd => "cat apache.log").for(:severity => "INFO")
|
81
|
+
#
|
82
|
+
# @example
|
83
|
+
# Indy.search(:source => {:cmd => "cat apache.log"}, :pattern => LOG_PATTERN, :time_format => MY_TIME_FORMAT).for(:all)
|
84
|
+
#
|
85
|
+
def search(params)
|
86
|
+
if params.respond_to?(:keys) && params[:source]
|
87
|
+
Indy.new(params)
|
88
|
+
else
|
89
|
+
Indy.new(:source => params, :pattern => [DEFAULT_LOG_PATTERN,DEFAULT_LOG_FIELDS].flatten)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
#
|
98
|
+
# Specify the log pattern to use as the comparison against each line within
|
99
|
+
# the log file that has been specified.
|
100
|
+
#
|
101
|
+
# @param [Array] pattern_array an Array with the regular expression as the first element
|
102
|
+
# followed by list of fields (Symbols) in the log entry
|
103
|
+
# to use for comparison against each log line.
|
104
|
+
#
|
105
|
+
# @example Log formatted as - HH:MM:SS Message
|
106
|
+
#
|
107
|
+
# Indy.search(LOG_FILE).with("^(\\d{2}.\\d{2}.\\d{2})\s*(.+)$",:time,:message)
|
108
|
+
#
|
109
|
+
def with(pattern_array = :default)
|
110
|
+
@pattern = pattern_array == :default ? [DEFAULT_LOG_PATTERN,DEFAULT_LOG_FIELDS].flatten : pattern_array
|
111
|
+
@time_field = @pattern[1..-1].include?(:time) ? :time : nil
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# Search the source and make an == comparison
|
117
|
+
#
|
118
|
+
# @param [Hash,Symbol] search_criteria the field to search for as the key and the
|
119
|
+
# value to compare against the other log messages. This function also
|
120
|
+
# supports symbol :all to return all messages
|
121
|
+
#
|
122
|
+
def search(search_criteria)
|
123
|
+
results = ResultSet.new
|
124
|
+
|
125
|
+
case
|
126
|
+
when search_criteria.is_a?(Enumerable)
|
127
|
+
results += _search do |result|
|
128
|
+
OpenStruct.new(result) if search_criteria.reject {|criteria,value| result[criteria] == value }.empty?
|
129
|
+
end
|
130
|
+
when search_criteria == :all
|
131
|
+
results += _search {|result| OpenStruct.new(result) }
|
132
|
+
end
|
133
|
+
|
134
|
+
results
|
135
|
+
end
|
136
|
+
|
137
|
+
alias_method :for, :search
|
138
|
+
|
139
|
+
#
|
140
|
+
# Search the source and make a regular expression comparison
|
141
|
+
#
|
142
|
+
# @param [Hash] search_criteria the field to search for as the key and the
|
143
|
+
# value to compare against the other log messages
|
144
|
+
#
|
145
|
+
# @example For all applications that end with Service
|
146
|
+
#
|
147
|
+
# Indy.search(LOG_FILE).like(:application => '(.+)Service')
|
148
|
+
#
|
149
|
+
def like(search_criteria)
|
150
|
+
results = ResultSet.new
|
151
|
+
|
152
|
+
results += _search do |result|
|
153
|
+
OpenStruct.new(result) if search_criteria.reject {|criteria,value| result[criteria] =~ /#{value}/ }.empty?
|
154
|
+
end
|
155
|
+
|
156
|
+
results
|
157
|
+
end
|
158
|
+
|
159
|
+
alias_method :matching, :like
|
160
|
+
|
161
|
+
|
162
|
+
#
|
163
|
+
# After scopes the eventual search to all entries after to this point.
|
164
|
+
#
|
165
|
+
# @param [Hash] scope_criteria the field to scope for as the key and the
|
166
|
+
# value to compare against the other log messages
|
167
|
+
#
|
168
|
+
# @example For all messages after specified date
|
169
|
+
#
|
170
|
+
# Indy.search(LOG_FILE).after(:time => time).for(:all)
|
171
|
+
#
|
172
|
+
def after(scope_criteria)
|
173
|
+
if scope_criteria[:time]
|
174
|
+
time = parse_date(scope_criteria[:time])
|
175
|
+
@inclusive = scope_criteria[:inclusive] || false
|
176
|
+
|
177
|
+
if scope_criteria[:span]
|
178
|
+
span = (scope_criteria[:span].to_i * 60).seconds
|
179
|
+
within(:time => [time, time + span])
|
180
|
+
else
|
181
|
+
@start_time = time
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
self
|
186
|
+
end
|
187
|
+
|
188
|
+
#
|
189
|
+
# Before scopes the eventual search to all entries prior to this point.
|
190
|
+
#
|
191
|
+
# @param [Hash] scope_criteria the field to scope for as the key and the
|
192
|
+
# value to compare against the other log messages
|
193
|
+
#
|
194
|
+
# @example For all messages before specified date
|
195
|
+
#
|
196
|
+
# Indy.search(LOG_FILE).before(:time => time).for(:all)
|
197
|
+
# Indy.search(LOG_FILE).before(:time => time, :span => 10).for(:all)
|
198
|
+
#
|
199
|
+
def before(scope_criteria)
|
200
|
+
if scope_criteria[:time]
|
201
|
+
time = parse_date(scope_criteria[:time])
|
202
|
+
@inclusive = scope_criteria[:inclusive] || false
|
203
|
+
|
204
|
+
if scope_criteria[:span]
|
205
|
+
span = (scope_criteria[:span].to_i * 60).seconds
|
206
|
+
within(:time => [time - span, time], :inclusive => scope_criteria[:inclusive])
|
207
|
+
else
|
208
|
+
@end_time = time
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
self
|
213
|
+
end
|
214
|
+
|
215
|
+
def around(scope_criteria)
|
216
|
+
if scope_criteria[:time]
|
217
|
+
time = parse_date(scope_criteria[:time])
|
218
|
+
|
219
|
+
# does @inclusive add any real value to the #around method?
|
220
|
+
@inclusive = scope_criteria[:inclusive] || false
|
221
|
+
|
222
|
+
half_span = ((scope_criteria[:span].to_i * 60)/2).seconds rescue 300.seconds
|
223
|
+
within(:time => [time - half_span, time + half_span])
|
224
|
+
end
|
225
|
+
|
226
|
+
self
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
#
|
231
|
+
# Within scopes the eventual search to all entries between two points.
|
232
|
+
#
|
233
|
+
# @param [Hash] scope_criteria the field to scope for as the key and the
|
234
|
+
# value to compare against the other log messages
|
235
|
+
#
|
236
|
+
# @example For all messages within the specified dates
|
237
|
+
#
|
238
|
+
# Indy.search(LOG_FILE).within(:time => [start_time,stop_time]).for(:all)
|
239
|
+
#
|
240
|
+
def within(scope_criteria)
|
241
|
+
if scope_criteria[:time]
|
242
|
+
@start_time, @end_time = scope_criteria[:time]
|
243
|
+
@inclusive = scope_criteria[:inclusive] || false
|
244
|
+
end
|
245
|
+
|
246
|
+
self
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
#
|
251
|
+
# Search the source for the specific severity
|
252
|
+
#
|
253
|
+
# @param [String,Symbol] severity the severity of the log messages to search
|
254
|
+
# for within the source
|
255
|
+
# @param [Symbol] direction by default search at the severity level, but you
|
256
|
+
# can specify :equal, :equal_and_above, and :equal_and_below
|
257
|
+
#
|
258
|
+
# @example INFO and more severe
|
259
|
+
#
|
260
|
+
# Indy.search(LOG_FILE).severity('INFO',:equal_and_above)
|
261
|
+
#
|
262
|
+
# @example Custom Level and Below
|
263
|
+
#
|
264
|
+
# Indy.search(LOG_FILE).with([CUSTOM_PATTERN,time,severity,message]).severity(:yellow,:equal_and_below,[:green,:yellow,:orange,:red])
|
265
|
+
# Indy.search(LOG_FILE).with([CUSTOM_PATTERN,time,severity,message]).matching(:severity => '(GREEN|YELLOW)')
|
266
|
+
#
|
267
|
+
def severity(severity,direction = :equal,scale = SEVERITY)
|
268
|
+
severity = severity.to_s.downcase.to_sym
|
269
|
+
|
270
|
+
case direction
|
271
|
+
when :equal
|
272
|
+
severity = [severity]
|
273
|
+
when :equal_and_above
|
274
|
+
severity = scale[scale.index(severity)..-1]
|
275
|
+
when :equal_and_below
|
276
|
+
severity = scale[0..scale.index(severity)]
|
277
|
+
end
|
278
|
+
|
279
|
+
ResultSet.new + _search {|result| OpenStruct.new(result) if severity.include?(result[:severity].downcase.to_sym) }
|
280
|
+
|
281
|
+
end
|
282
|
+
|
283
|
+
private
|
284
|
+
|
285
|
+
#
|
286
|
+
# Sets the source for the Indy instance.
|
287
|
+
#
|
288
|
+
# @param [String,Hash] source A filename or string. Use a Hash to specify a command string.
|
289
|
+
#
|
290
|
+
# @example
|
291
|
+
#
|
292
|
+
# source("apache.log")
|
293
|
+
# source(:cmd => "cat apache.log")
|
294
|
+
# source("INFO 2000-09-07 MyApp - Entering APPLICATION.\nINFO 2000-09-07 MyApp - Entering APPLICATION.")
|
295
|
+
#
|
296
|
+
def source=(specified_source)
|
297
|
+
|
298
|
+
cmd = specified_source[:cmd] rescue nil
|
299
|
+
|
300
|
+
if cmd
|
301
|
+
possible_source = try_as_command(cmd)
|
302
|
+
@source_info[:cmd] = specified_source[:cmd]
|
303
|
+
else
|
304
|
+
|
305
|
+
possible_source = try_as_file(specified_source) unless possible_source
|
306
|
+
|
307
|
+
if possible_source
|
308
|
+
@source_info[:file] = specified_source
|
309
|
+
else
|
310
|
+
possible_source = StringIO.new(specified_source.to_s)
|
311
|
+
@source_info[:string] = specified_source
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
@source = possible_source
|
316
|
+
end
|
317
|
+
|
318
|
+
#
|
319
|
+
# Search the specified source and yield to the block the line that was found
|
320
|
+
# with the given log pattern
|
321
|
+
#
|
322
|
+
# This method is supposed to be used internally.
|
323
|
+
# @param [IO] source is a Ruby IO object
|
324
|
+
#
|
325
|
+
def _search(source = @source,pattern_array = @pattern,&block)
|
326
|
+
|
327
|
+
if @start_time || @end_time
|
328
|
+
@start_time = @start_time || FOREVER_AGO
|
329
|
+
@end_time = @end_time || FOREVER
|
330
|
+
end
|
331
|
+
|
332
|
+
if @source_info[:cmd]
|
333
|
+
actual_source = try_as_command(@source_info[:cmd])
|
334
|
+
else
|
335
|
+
source.rewind
|
336
|
+
actual_source = source.dup
|
337
|
+
end
|
338
|
+
|
339
|
+
results = actual_source.each.collect do |line|
|
340
|
+
|
341
|
+
hash = parse_line(line, pattern_array)
|
342
|
+
|
343
|
+
if @time_field && @start_time
|
344
|
+
set_time(hash)
|
345
|
+
next unless inside_time_window?(hash)
|
346
|
+
end
|
347
|
+
|
348
|
+
next unless hash
|
349
|
+
|
350
|
+
block_given? ? block.call(hash) : nil
|
351
|
+
end
|
352
|
+
|
353
|
+
|
354
|
+
results.compact
|
355
|
+
end
|
356
|
+
|
357
|
+
#
|
358
|
+
# Return a hash of field=>value pairs for the log line
|
359
|
+
#
|
360
|
+
# @param [String] line The log line
|
361
|
+
# @param [Array] pattern_array The match regexp string, followed by log fields
|
362
|
+
# see Class method search
|
363
|
+
#
|
364
|
+
def parse_line( line, pattern_array = @pattern)
|
365
|
+
regexp, *fields = pattern_array
|
366
|
+
|
367
|
+
if /#{regexp}/.match(line)
|
368
|
+
values = /#{regexp}/.match(line).captures
|
369
|
+
raise "Field mismatch between log pattern and log data. The data is: '#{values.join(':::')}'" unless values.length == fields.length
|
370
|
+
|
371
|
+
hash = Hash[ *fields.zip( values ).flatten ]
|
372
|
+
hash[:line] = line.strip
|
373
|
+
|
374
|
+
hash
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
#
|
379
|
+
# Set the :_time value in the hash
|
380
|
+
#
|
381
|
+
# @param [Hash] hash The log line hash to modify
|
382
|
+
#
|
383
|
+
def set_time(hash)
|
384
|
+
hash[:_time] = parse_date( hash ) if hash
|
385
|
+
end
|
386
|
+
|
387
|
+
#
|
388
|
+
# Evaluate if a log line satisfies the configured time conditions
|
389
|
+
#
|
390
|
+
# @param [Hash] line_hash The log line hash to be evaluated
|
391
|
+
#
|
392
|
+
def inside_time_window?( line_hash )
|
393
|
+
|
394
|
+
if line_hash && line_hash[:_time]
|
395
|
+
if @inclusive
|
396
|
+
true unless line_hash[:_time] > @end_time or line_hash[:_time] < @start_time
|
397
|
+
else
|
398
|
+
true unless line_hash[:_time] >= @end_time or line_hash[:_time] <= @start_time
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
end
|
403
|
+
|
404
|
+
#
|
405
|
+
# Return a valid DateTime object for the log line or string
|
406
|
+
#
|
407
|
+
# @param [String, Hash] param The log line hash, or string to be evaluated
|
408
|
+
#
|
409
|
+
def parse_date(param)
|
410
|
+
return nil unless @time_field
|
411
|
+
|
412
|
+
time_string = param[@time_field] ? param[@time_field] : param
|
413
|
+
|
414
|
+
begin
|
415
|
+
# Attempt the appropriate parse method
|
416
|
+
date = @time_format ? DateTime.strptime(time_string, @time_format) : DateTime.parse(time_string)
|
417
|
+
rescue
|
418
|
+
begin
|
419
|
+
# If appropriate, fall back to simple parse method
|
420
|
+
if @time_format
|
421
|
+
date = DateTime.parse(time_string)
|
422
|
+
else
|
423
|
+
date = @time_field = nil
|
424
|
+
end
|
425
|
+
rescue ArgumentError
|
426
|
+
date = @time_field = nil
|
427
|
+
end
|
428
|
+
end
|
429
|
+
date
|
430
|
+
end
|
431
|
+
|
432
|
+
#
|
433
|
+
# Try opening the string as a command string, returning an IO object
|
434
|
+
#
|
435
|
+
# @param [String] command_string string of command that will return log contents
|
436
|
+
#
|
437
|
+
def try_as_command(command_string)
|
438
|
+
|
439
|
+
begin
|
440
|
+
io = IO.popen(command_string)
|
441
|
+
return nil if io.eof?
|
442
|
+
rescue
|
443
|
+
nil
|
444
|
+
end
|
445
|
+
io
|
446
|
+
end
|
447
|
+
|
448
|
+
#
|
449
|
+
# Try opening the string as a file, returning an File IO Object
|
450
|
+
#
|
451
|
+
# @param [String] filename path to log file
|
452
|
+
#
|
453
|
+
def try_as_file(filename)
|
454
|
+
|
455
|
+
begin
|
456
|
+
File.open(filename)
|
457
|
+
rescue
|
458
|
+
nil
|
459
|
+
end
|
460
|
+
|
461
|
+
end
|
462
|
+
|
463
|
+
end
|