indy 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,10 @@
1
+ === 0.1.2 / 2011-01-18
2
+
3
+ * Predefined log formats for NCSA Common, NCSA Combined, and Log4r (default)
4
+ * Source IO is explicitly closed after each #_search
5
+ * Removed instance method #search; use #for.
6
+ * Removed instance method #severity
7
+
1
8
  === 0.1.1 / 2011-01-13
2
9
 
3
10
  * Scope search by time (after, before, around, or within)
data/README.md CHANGED
@@ -4,7 +4,7 @@ Indy: A Log Archaeology Tool
4
4
  Synopsis
5
5
  --------
6
6
 
7
- Log files are often searched for particular strings but it does not often treat the logs themselves as data structures. Indy attempts to deliver logs with more powerful features by allowing the ability: to collect segments of a log from particular time; find a particular event; or monitor/reflect on a log to see if a particular event occurred (or not occurred).
7
+ Log files are often searched for particular strings but it does not often treat the logs themselves as data structures. Indy attempts to deliver logs with more powerful features by allowing the ability to collect segments of a log from particular time; find a particular event; or monitor/reflect on a log to see if a particular event occurred (or not occurred).
8
8
 
9
9
  Installation
10
10
  ------------
@@ -57,10 +57,22 @@ If the default pattern is obviously not strong enough for you, brew your own.
57
57
  To do so, specify a pattern and each of the match with their symbolic name.
58
58
 
59
59
  # HH:MM:SS SEVERITY APPLICATION#METHOD - MESSAGE
60
- custom_pattern = "^(\d{2}:\d{2}:\d{2})\s*(INFO|DEBUG|WARN|ERROR)\s*([^#]+)#([^\s]+)\s*-\s*(.+)$"
60
+ custom_pattern = /^(\d{2}:\d{2}:\d{2})\s*(INFO|DEBUG|WARN|ERROR)\s*([^#]+)#([^\s]+)\s*-\s*(.+)$/
61
61
 
62
62
  Indy.search(source).with(custom_pattern,:time,:severity,:application,:method,:message).for(:severity => 'INFO', :method => 'allocate')
63
63
 
64
+ ### Predefined Log Patterns
65
+
66
+ Several log formats have been predefined for ease of configuration. See indy/patterns.rb
67
+
68
+ # Indy::COMMON_LOG_PATTERN
69
+ # Indy::COMBINED_LOG_PATTERN
70
+ # Indy::LOG4R_DEFAULT_PATTERN
71
+ #
72
+ # Example (Log4r)
73
+ # INFO mylog: This is a message with level INFO
74
+ Indy.new(:source => 'logfile.txt', :pattern => Indy::LOG4R_DEFAULT_PATTERN).for(:application => 'mylog')
75
+
64
76
  ### Explicit Time Format
65
77
 
66
78
  By default, Indy tries to guess your time format (courtesy of DateTime#parse). If you supply an explicit time format, it will use DateTime#strptime, as well as try to guess.
@@ -99,10 +111,15 @@ This is required when log data uses a non-standard date format, e.g.: U.S. forma
99
111
 
100
112
  ## Process the Results
101
113
 
114
+ A ResultSet (Array) is returned by #for, #like, #after, etc.
115
+
102
116
  entries = Indy.search(source).for(:message => 'Entering Application')
103
117
 
104
118
  Indy.search(source).for(:message => 'Entering Application').each do |entry|
105
- puts entry
119
+
120
+ # each log line entry returned is an OpenStruct object
121
+ puts "[#{entry.time}] #{entry.message}: #{entry.application}"
122
+
106
123
  end
107
124
 
108
125
  LICENSE
data/Rakefile CHANGED
@@ -17,6 +17,7 @@ end
17
17
  desc "Run performance specs"
18
18
  RSpec::Core::RakeTask.new(:perf) do |t|
19
19
  t.pattern = "./performance/**/*_spec.rb"
20
+ t.rspec_opts = ['-f d']
20
21
  end
21
22
 
22
23
  desc "Run features"
@@ -7,14 +7,6 @@ When /^searching the log for the log severity (\w+)$/ do |severity|
7
7
  @results = @indy.for(:severity => severity)
8
8
  end
9
9
 
10
- When /^searching the log for the log severity (\w+) and lower$/ do |severity|
11
- @results = @indy.severity(severity,:equal_and_below)
12
- end
13
-
14
- When /^searching the log for the log severity (\w+) and higher$/ do |severity|
15
- @results = @indy.severity(severity,:equal_and_above)
16
- end
17
-
18
10
  When /^searching the log for the exact match of the message "([^"]+)"$/ do |message|
19
11
  @results = @indy.for(:message => message)
20
12
  end
@@ -25,7 +17,7 @@ end
25
17
 
26
18
  When /^searching the log for:$/ do |fields|
27
19
  fields.map_headers! {|header| header.is_a?(Symbol) ? header : header.downcase.gsub(/\s/,'_').to_sym }
28
- @results = @indy.search(fields.hashes.first)
20
+ @results = @indy.for(fields.hashes.first)
29
21
  end
30
22
 
31
23
  When /^searching the log for entries like:$/ do |fields|
@@ -34,7 +26,7 @@ When /^searching the log for entries like:$/ do |fields|
34
26
  end
35
27
 
36
28
  When /^searching the log for the exact match of custom field ([^"]+)\s*"([^"]+)"$/ do |field,value|
37
- @results = @indy.search(field.strip.gsub(/\s/,'_').to_sym => value)
29
+ @results = @indy.for(field.strip.gsub(/\s/,'_').to_sym => value)
38
30
  end
39
31
 
40
32
  When /^searching the log for entries in the (\w+ \w+), by (.+)$/ do |portion, method|
@@ -30,7 +30,7 @@ Gem::Specification.new do |s|
30
30
  s.name = 'indy'
31
31
  s.version = ::Indy::VERSION
32
32
  s.authors = ["Franklin Webber","Brandon Faloona"]
33
- s.description = %{ Indy is a log archelogy tool that allows you to search through log files. }
33
+ s.description = %{ Indy is a log archelogy tool that allows you to interact with log data like an object while you search by fields and/or time.}
34
34
  s.summary = "Log Search Tool"
35
35
  s.email = 'franklin.webber@gmail.com'
36
36
  s.homepage = "http://github.com/burtlo/Indy"
@@ -40,6 +40,13 @@ Gem::Specification.new do |s|
40
40
  s.required_ruby_version = '>= 1.8.7'
41
41
  s.add_dependency('activesupport', '>= 2.3.5')
42
42
 
43
+ s.add_development_dependency('cucumber', '>= 0.9.2')
44
+ s.add_development_dependency('rspec', '>= 2.4.0')
45
+ s.add_development_dependency('rspec-mocks', '>= 2.4.0')
46
+ s.add_development_dependency('rspec-prof', '>= 0.0.3')
47
+ s.add_development_dependency('rcov', '>= 0.9.9')
48
+ s.add_development_dependency('flog', '>= 2.5.0')
49
+
43
50
  changes = Indy.show_version_changes(::Indy::VERSION)
44
51
 
45
52
  s.post_install_message = %{
@@ -2,4 +2,6 @@ $:.unshift(File.dirname(__FILE__)) unless
2
2
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
3
 
4
4
  require 'indy/indy'
5
- require 'indy/result_set'
5
+ require 'indy/result_set'
6
+ require 'indy/log_formats'
7
+ require 'indy/patterns'
@@ -3,10 +3,10 @@ require 'active_support/core_ext'
3
3
 
4
4
  class Indy
5
5
 
6
- VERSION = "0.1.1"
6
+ VERSION = "0.1.2"
7
7
 
8
8
  #
9
- # string, file, or command that provides the log
9
+ # hash with one key (:string, :file, or :cmd) set to the string that defines the log
10
10
  #
11
11
  attr_accessor :source
12
12
 
@@ -21,15 +21,6 @@ class Indy
21
21
  #
22
22
  attr_accessor :time_format
23
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
24
  FOREVER_AGO = DateTime.now - 200_000
34
25
  FOREVER = DateTime.now + 200_000
35
26
 
@@ -47,13 +38,13 @@ class Indy
47
38
  #
48
39
  def initialize(args)
49
40
  @source = @pattern = nil
50
- @source_info = Hash.new
41
+ @source = Hash.new
51
42
 
52
43
  while (arg = args.shift) do
53
44
  send("#{arg.first}=",arg.last)
54
45
  end
55
46
 
56
- @pattern = @pattern || [DEFAULT_LOG_PATTERN,DEFAULT_LOG_FIELDS].flatten
47
+ @pattern = @pattern || DEFAULT_LOG_PATTERN
57
48
  @time_field = ( @pattern[1..-1].include?(:time) ? :time : nil )
58
49
 
59
50
  end
@@ -86,7 +77,7 @@ class Indy
86
77
  if params.respond_to?(:keys) && params[:source]
87
78
  Indy.new(params)
88
79
  else
89
- Indy.new(:source => params, :pattern => [DEFAULT_LOG_PATTERN,DEFAULT_LOG_FIELDS].flatten)
80
+ Indy.new(:source => params, :pattern => DEFAULT_LOG_PATTERN)
90
81
  end
91
82
  end
92
83
 
@@ -104,10 +95,10 @@ class Indy
104
95
  #
105
96
  # @example Log formatted as - HH:MM:SS Message
106
97
  #
107
- # Indy.search(LOG_FILE).with("^(\\d{2}.\\d{2}.\\d{2})\s*(.+)$",:time,:message)
98
+ # Indy.search(LOG_FILE).with(/^(\d{2}.\d{2}.\d{2})\s*(.+)$/,:time,:message)
108
99
  #
109
100
  def with(pattern_array = :default)
110
- @pattern = pattern_array == :default ? [DEFAULT_LOG_PATTERN,DEFAULT_LOG_FIELDS].flatten : pattern_array
101
+ @pattern = pattern_array == :default ? DEFAULT_LOG_PATTERN : pattern_array
111
102
  @time_field = @pattern[1..-1].include?(:time) ? :time : nil
112
103
  self
113
104
  end
@@ -119,345 +110,311 @@ class Indy
119
110
  # value to compare against the other log messages. This function also
120
111
  # supports symbol :all to return all messages
121
112
  #
122
- def search(search_criteria)
113
+ def for(search_criteria)
123
114
  results = ResultSet.new
124
115
 
125
- case
126
- when search_criteria.is_a?(Enumerable)
116
+ case search_criteria
117
+ when Enumerable
127
118
  results += _search do |result|
128
119
  OpenStruct.new(result) if search_criteria.reject {|criteria,value| result[criteria] == value }.empty?
129
120
  end
130
- when search_criteria == :all
121
+ when :all
131
122
  results += _search {|result| OpenStruct.new(result) }
132
123
  end
133
124
 
134
125
  results
135
126
  end
136
127
 
137
- alias_method :for, :search
138
128
 
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
129
+ #
130
+ # Search the source and make a regular expression comparison
131
+ #
132
+ # @param [Hash] search_criteria the field to search for as the key and the
133
+ # value to compare against the other log messages
134
+ #
135
+ # @example For all applications that end with Service
136
+ #
137
+ # Indy.search(LOG_FILE).like(:application => '(.+)Service')
138
+ #
139
+ def like(search_criteria)
140
+ results = ResultSet.new
151
141
 
152
- results += _search do |result|
153
- OpenStruct.new(result) if search_criteria.reject {|criteria,value| result[criteria] =~ /#{value}/ }.empty?
154
- end
142
+ results += _search do |result|
143
+ OpenStruct.new(result) if search_criteria.reject {|criteria,value| result[criteria] =~ /#{value}/ }.empty?
144
+ end
155
145
 
156
- results
157
- end
146
+ results
147
+ end
158
148
 
159
- alias_method :matching, :like
149
+ alias_method :matching, :like
160
150
 
161
151
 
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
152
+ #
153
+ # After scopes the eventual search to all entries after to this point.
154
+ #
155
+ # @param [Hash] scope_criteria the field to scope for as the key and the
156
+ # value to compare against the other log messages
157
+ #
158
+ # @example For all messages after specified date
159
+ #
160
+ # Indy.search(LOG_FILE).after(:time => time).for(:all)
161
+ #
162
+ def after(scope_criteria)
163
+ if scope_criteria[:time]
164
+ time = parse_date(scope_criteria[:time])
165
+ @inclusive = scope_criteria[:inclusive] || false
166
+
167
+ if scope_criteria[:span]
168
+ span = (scope_criteria[:span].to_i * 60).seconds
169
+ within(:time => [time, time + span])
170
+ else
171
+ @start_time = time
172
+ end
182
173
  end
183
- end
184
174
 
185
- self
186
- end
175
+ self
176
+ end
187
177
 
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
178
+ #
179
+ # Before scopes the eventual search to all entries prior to this point.
180
+ #
181
+ # @param [Hash] scope_criteria the field to scope for as the key and the
182
+ # value to compare against the other log messages
183
+ #
184
+ # @example For all messages before specified date
185
+ #
186
+ # Indy.search(LOG_FILE).before(:time => time).for(:all)
187
+ # Indy.search(LOG_FILE).before(:time => time, :span => 10).for(:all)
188
+ #
189
+ def before(scope_criteria)
190
+ if scope_criteria[:time]
191
+ time = parse_date(scope_criteria[:time])
192
+ @inclusive = scope_criteria[:inclusive] || false
193
+
194
+ if scope_criteria[:span]
195
+ span = (scope_criteria[:span].to_i * 60).seconds
196
+ within(:time => [time - span, time], :inclusive => scope_criteria[:inclusive])
197
+ else
198
+ @end_time = time
199
+ end
209
200
  end
201
+
202
+ self
210
203
  end
211
204
 
212
- self
213
- end
205
+ def around(scope_criteria)
206
+ if scope_criteria[:time]
207
+ time = parse_date(scope_criteria[:time])
214
208
 
215
- def around(scope_criteria)
216
- if scope_criteria[:time]
217
- time = parse_date(scope_criteria[:time])
209
+ # does @inclusive add any real value to the #around method?
210
+ @inclusive = scope_criteria[:inclusive] || false
218
211
 
219
- # does @inclusive add any real value to the #around method?
220
- @inclusive = scope_criteria[:inclusive] || false
212
+ half_span = ((scope_criteria[:span].to_i * 60)/2).seconds rescue 300.seconds
213
+ within(:time => [time - half_span, time + half_span])
214
+ end
221
215
 
222
- half_span = ((scope_criteria[:span].to_i * 60)/2).seconds rescue 300.seconds
223
- within(:time => [time - half_span, time + half_span])
216
+ self
224
217
  end
225
218
 
226
- self
227
- end
228
219
 
220
+ #
221
+ # Within scopes the eventual search to all entries between two points.
222
+ #
223
+ # @param [Hash] scope_criteria the field to scope for as the key and the
224
+ # value to compare against the other log messages
225
+ #
226
+ # @example For all messages within the specified dates
227
+ #
228
+ # Indy.search(LOG_FILE).within(:time => [start_time,stop_time]).for(:all)
229
+ #
230
+ def within(scope_criteria)
231
+ if scope_criteria[:time]
232
+ @start_time, @end_time = scope_criteria[:time]
233
+ @inclusive = scope_criteria[:inclusive] || false
234
+ end
229
235
 
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
236
+ self
244
237
  end
245
238
 
246
- self
247
- end
248
239
 
240
+ private
249
241
 
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
242
+ #
243
+ # Sets the source for the Indy instance.
244
+ #
245
+ # @param [String,Hash] source A filename, or log content as a string. Use a Hash with :cmd key to specify a command string.
246
+ #
247
+ # @example
248
+ #
249
+ # source("apache.log")
250
+ # source(:cmd => "cat apache.log")
251
+ # source("INFO 2000-09-07 MyApp - Entering APPLICATION.\nINFO 2000-09-07 MyApp - Entering APPLICATION.")
252
+ #
253
+ def source=(param)
278
254
 
279
- ResultSet.new + _search {|result| OpenStruct.new(result) if severity.include?(result[:severity].downcase.to_sym) }
255
+ cmd = param[:cmd] rescue nil
256
+ @source[:cmd] = param[:cmd] if cmd
280
257
 
281
- end
258
+ unless cmd
259
+ File.exist?(param) ? @source[:file] = param : @source[:string] = param
260
+ end
282
261
 
283
- private
262
+ end
284
263
 
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)
264
+ #
265
+ # Search the @source and yield to the block the line that was found
266
+ # with @pattern
267
+ #
268
+ # This method is supposed to be used internally.
269
+ #
270
+ def _search(&block)
271
+ time_search = use_time_criteria?
272
+ source_io = open_source
297
273
 
298
- cmd = specified_source[:cmd] rescue nil
274
+ results = source_io.each.collect do |line|
299
275
 
300
- if cmd
301
- possible_source = try_as_command(cmd)
302
- @source_info[:cmd] = specified_source[:cmd]
303
- else
276
+ hash = parse_line(line, @pattern)
304
277
 
305
- possible_source = try_as_file(specified_source) unless possible_source
278
+ if time_search
279
+ set_time(hash)
280
+ next unless inside_time_window?(hash)
281
+ end
282
+ next unless hash
283
+
284
+ block_given? ? block.call(hash) : nil
306
285
 
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
286
  end
287
+
288
+ source_io.close if @source[:file] || @source[:cmd]
289
+
290
+ results.compact
313
291
  end
314
292
 
315
- @source = possible_source
316
- end
317
293
 
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)
294
+ #
295
+ # Return a log io object
296
+ #
297
+ def open_source
298
+ begin
326
299
 
327
- if @start_time || @end_time
328
- @start_time = @start_time || FOREVER_AGO
329
- @end_time = @end_time || FOREVER
330
- end
300
+ case @source.keys.first # and only
301
+ when :cmd
302
+ source_io = exec_command(@source[:cmd])
303
+ raise "Failed to execute command (#{@source[:cmd]})" if source_io.nil?
331
304
 
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
305
+ when :file
306
+ source_io = File.open(@source[:file], 'r')
307
+ raise "Filed to open file: #{@source[:file]}" if source_io.nil?
338
308
 
339
- results = actual_source.each.collect do |line|
309
+ when :string
310
+ source_io = StringIO.new( @source[:string] )
340
311
 
341
- hash = parse_line(line, pattern_array)
312
+ else
313
+ raise "Unsupported log source: #{@source.inspect}"
314
+ end
342
315
 
343
- if @time_field && @start_time
344
- set_time(hash)
345
- next unless inside_time_window?(hash)
316
+ rescue Exception => e
317
+ raise "Unable to open log source. (#{e.message})"
346
318
  end
347
319
 
348
- next unless hash
349
-
350
- block_given? ? block.call(hash) : nil
320
+ source_io
351
321
  end
352
322
 
323
+ #
324
+ # Return a hash of field=>value pairs for the log line
325
+ #
326
+ # @param [String] line The log line
327
+ # @param [Array] pattern_array The match regexp string, followed by log fields
328
+ # see Class method search
329
+ #
330
+ def parse_line( line, pattern_array = @pattern)
331
+ regexp, *fields = pattern_array
353
332
 
354
- results.compact
355
- end
333
+ if /#{regexp}/.match(line)
334
+ values = /#{regexp}/.match(line).captures
335
+ raise "Field mismatch between log pattern and log data. The data is: '#{values.join(':::')}'" unless values.length == fields.length
356
336
 
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
337
+ hash = Hash[ *fields.zip( values ).flatten ]
338
+ hash[:line] = line.strip
366
339
 
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
340
+ hash
341
+ end
342
+ end
370
343
 
371
- hash = Hash[ *fields.zip( values ).flatten ]
372
- hash[:line] = line.strip
344
+ #
345
+ # Return true if start or end time has been set, and a :time field exists
346
+ #
347
+ def use_time_criteria?
348
+ if @start_time || @end_time
349
+ # ensure both boundaries are set
350
+ @start_time = @start_time || FOREVER_AGO
351
+ @end_time = @end_time || FOREVER
352
+ end
373
353
 
374
- hash
354
+ return (@time_field && @start_time && @end_time)
375
355
  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
356
 
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
357
 
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
358
+ #
359
+ # Set the :_time value in the hash
360
+ #
361
+ # @param [Hash] hash The log line hash to modify
362
+ #
363
+ def set_time(hash)
364
+ hash[:_time] = parse_date( hash ) if hash
400
365
  end
401
366
 
402
- end
367
+ #
368
+ # Evaluate if a log line satisfies the configured time conditions
369
+ #
370
+ # @param [Hash] line_hash The log line hash to be evaluated
371
+ #
372
+ def inside_time_window?( line_hash )
403
373
 
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)
374
+ if line_hash && line_hash[:_time]
375
+ if @inclusive
376
+ true unless line_hash[:_time] > @end_time or line_hash[:_time] < @start_time
422
377
  else
423
- date = @time_field = nil
378
+ true unless line_hash[:_time] >= @end_time or line_hash[:_time] <= @start_time
424
379
  end
425
- rescue ArgumentError
426
- date = @time_field = nil
427
380
  end
381
+
428
382
  end
429
- date
430
- end
431
383
 
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)
384
+ #
385
+ # Return a valid DateTime object for the log line or string
386
+ #
387
+ # @param [String, Hash] param The log line hash, or string to be evaluated
388
+ #
389
+ def parse_date(param)
390
+ return nil unless @time_field
438
391
 
439
- begin
440
- io = IO.popen(command_string)
441
- return nil if io.eof?
442
- rescue
443
- nil
392
+ time_string = param[@time_field] ? param[@time_field] : param
393
+
394
+ begin
395
+ # Attempt the appropriate parse method
396
+ @time_format ? DateTime.strptime(time_string, @time_format) : DateTime.parse(time_string)
397
+ rescue
398
+ # If appropriate, fall back to simple parse method
399
+ DateTime.parse(time_string) if @time_format rescue nil
400
+ end
444
401
  end
445
- io
446
- end
447
402
 
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)
403
+ #
404
+ # Try opening the string as a command string, returning an IO object
405
+ #
406
+ # @param [String] command_string string of command that will return log contents
407
+ #
408
+ def exec_command(command_string)
454
409
 
455
- begin
456
- File.open(filename)
457
- rescue
458
- nil
410
+ begin
411
+ io = IO.popen(command_string)
412
+ return nil if io.eof?
413
+ rescue
414
+ nil
415
+ end
416
+ io
459
417
  end
460
418
 
461
- end
462
419
 
463
- end
420
+ end