Class: Indy::Source

Inherits:
Object
  • Object
show all
Defined in:
lib/indy/source.rb

Overview

A StringIO interface to the underlying log source.

Defined Under Namespace

Classes: FieldMismatchException, Invalid

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(param, log_definition = nil) ⇒ Source

Creates a Source object.

Parameters:

  • param (String, Hash)

    The source content String, filepath String, or :cmd => ‘command’ Hash

Raises:



31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/indy/source.rb', line 31

def initialize(param,log_definition=nil)
  raise Indy::Source::Invalid, "No source specified." if param.nil?
  self.log_definition = log_definition || LogDefinition.new()
  return discover_connection(param) unless param.respond_to?(:keys)
  if param[:cmd]
    set_connection(:cmd, param[:cmd])
  elsif param[:file]
    set_connection(:file, open_or_return_file(param[:file]))
  elsif param[:string]
    set_connection(:string, param[:string])
  end
end

Instance Attribute Details

#connectionObject (readonly)

log source connection string (cmd, filename or log data)



18
19
20
# File 'lib/indy/source.rb', line 18

def connection
  @connection
end

#ioObject (readonly)

the StringIO object



21
22
23
# File 'lib/indy/source.rb', line 21

def io
  @io
end

#log_definitionObject

log definition



12
13
14
# File 'lib/indy/source.rb', line 12

def log_definition
  @log_definition
end

#typeObject (readonly)

log source type. :cmd, :file, or :string



15
16
17
# File 'lib/indy/source.rb', line 15

def type
  @type
end

Instance Method Details

#discover_connection(param) ⇒ Object

Support source being passed in without key indicating type



47
48
49
50
51
52
53
54
55
# File 'lib/indy/source.rb', line 47

def discover_connection(param)
  if param.respond_to?(:read) and param.respond_to?(:rewind)
    set_connection(:file, param)
  elsif param.respond_to?(:to_s) and param.respond_to?(:length)
    set_connection(:string, param)
  else
    raise Indy::Source::Invalid
  end
end

#entriesObject

array of log lines from source



216
217
218
219
# File 'lib/indy/source.rb', line 216

def entries
  load_data unless @entries
  @entries
end

#exec_command(command_string) ⇒ Object

Execute the source’s connection string, returning an IO object

Parameters:

  • command_string (String)

    string of command that will return log contents

Raises:



199
200
201
202
203
# File 'lib/indy/source.rb', line 199

def exec_command(command_string)
  io = IO.popen(command_string)
  raise Indy::Source::Invalid, "No data returned from command string execution" if io.eof?
  io
end

#find(boundary, value, start, stop) ⇒ Object

Binary search for a time condition



180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/indy/source.rb', line 180

def find(boundary,value,start,stop)
  return start if start == stop
  mid_index, mid_time = find_middle(start,stop)
  if mid_time == value
    find_adjacent(boundary,value,start,stop,mid_index)
  elsif mid_time > value
    mid_index -= 1 if mid_index == stop
    find(boundary, value, start, mid_index)
  elsif mid_time < value
    mid_index += 1 if mid_index == start
    find(boundary, value, mid_index, stop)
  end
end

#find_adjacent(boundary, value, start, stop, mid_index) ⇒ Object

Step forward or backward by one, looking for the boundary of the value



148
149
150
151
152
153
154
155
# File 'lib/indy/source.rb', line 148

def find_adjacent(boundary,value,start,stop,mid_index)
  case boundary
  when :first
    (time_at(mid_index,-1) == value) ? find_first(value,start-1,stop) : mid_index
  when :last
    (time_at(mid_index,1) == value) ? find_last(value,start,stop+1) : mid_index
  end
end

#find_first(value, start, stop) ⇒ Object

find index of first record to match value



123
124
125
126
# File 'lib/indy/source.rb', line 123

def find_first(value,start,stop)
  return start if time_at(start) > value
  find(:first,value,start,stop)
end

#find_last(value, start, stop) ⇒ Object

find index of last record to match value



131
132
133
134
# File 'lib/indy/source.rb', line 131

def find_last(value,start,stop)
  return stop if time_at(stop) < value
  find(:last,value,start,stop)
end

#find_middle(start, stop) ⇒ Object

Find index and time at mid point



139
140
141
142
143
# File 'lib/indy/source.rb', line 139

def find_middle(start, stop)
  index = ((stop - start) / 2) + start
  time = time_at(index)
  [index, time]
end

#load_dataObject

read source data and populate instance variables



224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/indy/source.rb', line 224

def load_data
  self.open if @io.nil?
  if @log_definition.multiline
    entire_log = @io.read
    @entries = entire_log.scan(@log_definition.entry_regexp).map{|matchdata|matchdata[0]}
  else
    @entries = @io.readlines
  end
  @io.rewind
  @entries.delete_if {|entry| entry.match(/^\s*$/)}
  @num_entries = @entries.count
end

#num_entriesObject

the number of lines in the source



208
209
210
211
# File 'lib/indy/source.rb', line 208

def num_entries
  load_data unless @num_entries
  @num_entries
end

#open(time_boundaries = nil) ⇒ Object

Return a StringIO object to provide access to the underlying log source



75
76
77
78
79
80
81
82
83
84
85
# File 'lib/indy/source.rb', line 75

def open(time_boundaries=nil)
  begin
    open_method = ('open_' + @type.to_s).intern
    self.send(open_method)
  rescue Exception => e
    raise Indy::Source::Invalid, "Unable to open log source. (#{e.message})"
  end
  load_data
  scope_by_time(time_boundaries) if time_boundaries
  @entries
end

#open_cmdObject



87
88
89
90
# File 'lib/indy/source.rb', line 87

def open_cmd
  @io = StringIO.new(exec_command(@connection).read)
  raise "Failed to execute command (#{@connection.inspect})" if @io.nil?
end

#open_fileObject



92
93
94
95
96
# File 'lib/indy/source.rb', line 92

def open_file
  @connection.rewind
  @io = StringIO.new(@connection.read)
  raise "Failed to open file: #{@connection.inspect}" if @io.nil?
end

#open_or_return_file(param) ⇒ Object

Raises:

  • (ArgumentError)


65
66
67
68
69
70
# File 'lib/indy/source.rb', line 65

def open_or_return_file(param)
  return param if param.respond_to? :pos
  file = File.open(param, 'r')
  raise ArgumentError, "Unable to open file parameter: '#{file}'" unless file.respond_to? :pos
  file
end

#open_stringObject



98
99
100
101
# File 'lib/indy/source.rb', line 98

def open_string
  @io = StringIO.new(@connection)
  raise "Failed to create StringIO from source (#{@connection.inspect})" if @io.nil?
end

#scope_by_time(time_boundaries) ⇒ Object

Return entries that meet time criteria



107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/indy/source.rb', line 107

def scope_by_time(time_boundaries)
  start_time, end_time = time_boundaries
  scope_end = num_entries - 1
  # short circuit the search if possible
  if (time_at(0) > end_time) or (time_at(-1) < start_time)
    @entries = []
    return @entries
  end
  scope_begin = find_first(start_time, 0, scope_end)
  scope_end = find_last(end_time, scope_begin, scope_end)
  @entries = @entries[scope_begin..scope_end]
end

#set_connection(type, value) ⇒ Object

set the source connection type and connection_string



60
61
62
63
# File 'lib/indy/source.rb', line 60

def set_connection(type, value)
  @type = type
  @connection = value
end

#time_at(index, delta = 0) ⇒ Object

Return the time of a log entry index, with an optional offset



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/indy/source.rb', line 160

def time_at(index, delta=0)
  begin
    entry = @entries[index + delta]
    time = @log_definition.parse_entry(entry)[:time]
    result = Indy::Time.parse_date(time, @log_definition.time_format)
  rescue FieldMismatchException => fme
    raise
  rescue Exception => e
    msg = "Unable to parse time from entry. Time value was #{time.inspect}. Original exception was:\n#{e.class}\n"
    raise Indy::Time::ParseException, msg + e.message
  end
  if result.nil?
    raise Indy::Time::ParseException, "Unable to parse datetime. Raw value was #{time.inspect}. Entry was #{entry}."
  end
  result
end