class RailsLogDeinterleaver::Parser

Public Class Methods

new(filename, options={}) click to toggle source
# 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

Public Instance Methods

ensure_timeouts_logged() click to toggle source
# 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) click to toggle source

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

# 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) click to toggle source
# 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) click to toggle source
# 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
run() click to toggle source
# 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) click to toggle source
# File lib/rails_log_deinterleaver/parser.rb, line 71
def time_for_line(line)
  DateTime.strptime(line, @options[:date_time_format])
end