indy 0.1.1
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.
- 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
|