Class: RailsLogDeinterleaver::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_log_deinterleaver/parser.rb

Instance Method Summary collapse

Constructor Details

#initialize(filename, options = {}) ⇒ Parser

Returns a new instance of Parser.



7
8
9
10
11
12
13
14
15
16
# File 'lib/rails_log_deinterleaver/parser.rb', line 7

def initialize(filename, options={})
  @filename = filename
  @options = options
  @options[:request_timeout] ||= 30
  @options[:date_time_format] ||= "%b %d %H:%M:%S"
  @options[:start_request_regex] ||= /\[\d+\]: Started/
  @options[:end_request_regex] ||= /\[\d+\]: Completed/
  @options[:output] ||= $stdout
  @pids = {}
end

Instance Method Details

#ensure_timeouts_loggedObject



75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/rails_log_deinterleaver/parser.rb', line 75

def ensure_timeouts_logged
  expired_time = Time.now - @options[:request_timeout]
  # Loop over a clone of @pids because there is a chance that the parent thread
  # will want to add to the @pids Hash whilst this is running, triggering an exception.
  @pids.dup.each do |pid, details|
    if details[:last_seen_at] <= expired_time
      output_logs_for_pid(pid)
    end
  end

  sleep 1
  self.ensure_timeouts_logged
end

#output_logs_for_pid(pid) ⇒ Object

Output all lines of the log for a single request, followed by an empty blank line.



62
63
64
65
# File 'lib/rails_log_deinterleaver/parser.rb', line 62

def output_logs_for_pid(pid)
  @options[:output].puts (@pids[pid][:lines] << '')
  @pids.delete(pid)
end

#pid_for_line(line) ⇒ Object



67
68
69
# File 'lib/rails_log_deinterleaver/parser.rb', line 67

def pid_for_line(line)
  line.match(/\[(\d+)\]/).captures.first.to_i
end

#process_line(line) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/rails_log_deinterleaver/parser.rb', line 33

def process_line(line)
  # It is possible for the file to contain UTF-8 byte streams. Get rid of them here.
  # String#encode does not do anything if source and destination encoding are the same,
  # so encode to UTF-16 before converting back to UTF-8.
  line = line.encode('UTF-16', :invalid => :replace, :replace => '').encode('UTF-8')
  pid = pid_for_line(line)

  if line.match(@options[:start_request_regex])
    if @pids[pid]
      # Already a request started but not finished for this pid. (Should be rare)
      # Log what we have to start a new request
      output_logs_for_pid(pid)
    end

    @pids[pid] = {lines: []}
  end

  return unless @pids[pid]

  @pids[pid][:last_seen_at] = Time.now
  @pids[pid][:lines].push(line)

  if line.match(@options[:end_request_regex])
    output_logs_for_pid(pid)
  end
end

#runObject



18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/rails_log_deinterleaver/parser.rb', line 18

def run
  Thread.new do
    self.ensure_timeouts_logged
  end

  File.open(@filename, 'r:UTF-8') do |log|
    log.extend(File::Tail)
    log.interval = 0.1
    log.backward(@options[:backward]) if @options[:backward]
    log.tail do |line|
      process_line line
    end
  end
end

#time_for_line(line) ⇒ Object



71
72
73
# File 'lib/rails_log_deinterleaver/parser.rb', line 71

def time_for_line(line)
  DateTime.strptime(line, @options[:date_time_format])
end