Skip to content

Instantly share code, notes, and snippets.

Created May 15, 2013 03:32
Show Gist options
  • Save anonymous/5581474 to your computer and use it in GitHub Desktop.
Save anonymous/5581474 to your computer and use it in GitHub Desktop.
# Camelog is a Ruby gem that helps Rails developers to browse easily in the application logs.
# It works as a live log file parser, so it's completely independent from Rails.
# Allow to choice what kind of request you want to see.
# It's in early development, so many things will change and many features are comming soon.
# Assets requests is also a GET, but Camelog calls it ASSET.
# By default it shows all requests ignoring ASSETS.
# require 'thread' allows me to use Queue.new.
# 'file-tail' is to "tail" the Rails log file.
# 'io/console' will be used to track user's keyboard.
# 'camelog/request' defines the request object used in this class.
require 'thread'
require 'file-tail'
require 'io/console'
require 'camelog/request'
# Should pass the application path in the initialization and then call run.
# Four loops will run in parallel using Threads.
# listen_keyboard, wait_command, stream_log and stream_parsed_log (main).
class Camelog
def initialize(app_path)
# Finds the application log path and set the default @types_to_show
# help and exit if the user used the help in ARGV.
@app_path = app_path
@pow_log_file = app_path + "log/development.log"
@types_to_show = ['GET', 'POST', 'PUT', 'DELETE']
true
end
def run
# Set the default fundamental attributes.
# Then start the threads.
@cached_request = nil
@requests_queue = Queue.new
@keyboard_queue = Queue.new
@all_requests = Array.new
Thread.new { listen_keyboard }
Thread.new { wait_command }
Thread.new { stream_log }
stream_parsed_log
end
def listen_keyboard
# Watch the user's keyboard while running.
# Every character goes to @keyboard_queue
loop do
char = STDIN.getch
@keyboard_queue << char if char
end
end
def wait_command
# Consumes @keyboard_queue and try to find a valid Camelog command.
# When it finds a command, set the respective types in @types_to_show.
# Once has a command it also ensures that stream_parsed_log will show the right requests.
# And refresh_display after that.
# last_command is used to prevent that the same command runs twice.
last_command = nil
loop do
char = @keyboard_queue.shift
if char != last_command
case char
when 'q'
show "\n" + "Exit."
show ""
exit
when 'a'
@types_to_show = ['GET', 'POST', 'PUT', 'DELETE', 'ASSET']
command = true
when 'g'
@types_to_show = ['GET']
command = true
when 'p'
@types_to_show = ['POST']
command = true
when 'u'
@types_to_show = ['PUT']
command = true
when 'd'
@types_to_show = ['DELETE']
command = true
when 's'
@types_to_show = ['ASSET']
command = true
when '/'
@types_to_show = ['GET', 'POST', 'PUT', 'DELETE']
command = true
when 'h'
help
last_command = char
end
refresh_display if command
last_command = char if command
command = false
end
end
end
def refresh_display
# This method is called when the user press a new command.
# It will refresh the console screen with the requests of the user's choice type.
# If no requests_to_show, refresh the screen with a waiting message.
requests_to_show = filter_to_show(@all_requests)
if requests_to_show.empty?
screen_jump
show 'No (' + current_request_option + ') requests types yet.'
show 'Watching...'
show '-------------------------------------------------'
show ''
else
output_text = ''
requests_to_show.each do |request|
output_text += request.text + "\n"
end
screen_jump
show 'Refreshed to see ' + current_request_option + ' requests.'
show '-------------------------------------------------'
show ''
show output_text
end
end
def current_request_option
# A trikcy way to get a descripton of what is the current view option.
if @types_to_show.count == 5
'All'
elsif @types_to_show.count == 4
'All ignoring ASSETS'
else
# Will return 'POST' or 'GET' or 'PUT' or ...
@types_to_show[0]
end
end
def filter_to_show(request_list)
# Recieves an array of Request objects.
# Outputs another array containing just the current @types_to_show requests.
output = Array.new
request_list.each do |request|
output << request if @types_to_show.include? request.type
end
output
end
def stream_log
# Stream inside the log file, line by line, sending each line to the parser method.
File.open(@pow_log_file) do |log|
log.extend(File::Tail)
log.interval = 10
log.backward(10)
log.tail { |line| parse(line) }
end
end
def parse(line)
# It's recieves the sequential lines of the log file.
# Once it's passed in order, the parser knows how to separate each request.
# Each request goes into @requests_queue (for stream_parsed_log consume) and in @all_requests.
if line
# Looks for a cached request.
if @cached_request
# It there's a cached request, the follow lines should go inside it until the next blank line.
# line.length < 3 means that the line is blank.
if line.length < 3
# Blank line, so the request is completed.
# Save it in @requests_queue and clear the cache.
@requests_queue << @cached_request
@all_requests << @cached_request
@cached_request = nil
else
# Append the line the the cached request text.
@cached_request.text += line
end
else
# If no cached request, it will match the line for a new one.
if line.include? '/assets/'
@cached_request = Request.new('ASSET', line)
elsif line.include? 'Started GET'
@cached_request = Request.new('GET', line)
elsif line.include? 'Started POST'
@cached_request = Request.new('POST', line)
elsif line.include? 'Started PUT'
@cached_request = Request.new('PUT', line)
elsif line.include? 'Started DELETE'
@cached_request = Request.new('DELETE', line)
end
end
end
end
def stream_parsed_log
# Consumes the @requests_queue and show the request.
# Unless the type don't belongs to @types_to_show.
show ''
loop do
request = @requests_queue.shift
if request
if show_request?(request)
show request.text + "\n"
end
end
end
end
def show_request?(request)
# Return true if the passed request type are present in @types_to_show
@types_to_show.include? request.type
end
def screen_jump
25.times do
puts ''
end
end
def show(str)
# Once has a STDIN.getch loop running (listen_keyboard) my console goes into raw mode.
# Print strings in raw mode is such a bad idea.
# So this method turns the raw mode off temporarily.
system "stty -raw echo"
puts str
system "stty raw -echo"
end
def help
system "stty -raw echo"
puts ''
puts 'Camelog usage:'
puts '$ camelog ~/my/railsapp'
puts ''
puts 'Camelog consider GET requests in /assets/ as ASSET type, instead of GET.'
puts 'As default it shows all requests, ignoring ASSETS.'
puts ''
puts 'Press one of these keys to navigate:'
puts 'q - Exit the program.'
puts 'a - Watch all types of requests.'
puts 'g - Watch only GET requests.'
puts 'p - Watch only POST requests.'
puts 'u - Watch only PUT requests.'
puts 'd - Watch only DELETE requests.'
puts 's - Watch only ASSET requests.'
puts '/ - Return to default mode. (Show all ignoring ASSETS)'
puts 'h - Show this message.'
puts ''
puts 'Enjoy.'
puts ''
system "stty raw -echo"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment