Skip to content

Instantly share code, notes, and snippets.

@padcom
Created January 10, 2012 20:38
Show Gist options
  • Save padcom/1591049 to your computer and use it in GitHub Desktop.
Save padcom/1591049 to your computer and use it in GitHub Desktop.
Tail logs from mongo collection
#!/usr/bin/ruby
require 'rubygems'
require 'mongo'
require 'json/pure'
require 'optparse'
require 'highline/import'
# utility function to convert a BSON:OrderedHash to regular hash
class BSON::OrderedHash
def to_h
inject({}) do |acc, element|
k,v = element
acc[k] = (if v.class == BSON::OrderedHash then v.to_h else v end)
acc
end
end
end
# define options
options = {}
optparse = OptionParser.new do |opts|
# Set a banner, displayed at the top of the help screen.
opts.banner = "Usage: log-tail.rb [options]"
options[:host] = 'localhost'
opts.on('-h', '--host name', 'Database host (default: localhost)') do |host|
options[:host] = host
end
options[:port] = 27017
opts.on('-p', '--port port', 'Database port (default: 27017)') do |port|
options[:port] = port
end
options[:db] = nil
opts.on('-d', '--database name', 'Database (required)') do |db|
options[:db] = db
end
options[:coll] = "log"
opts.on('-c', '--collection name', 'Collection to tail (default: log)') do |coll|
options[:coll] = coll
end
options[:user] = nil
opts.on('-u', '--username user', 'Database username') do |user|
options[:user] = user
end
options[:pass] = nil
opts.on('-P', '--password pass', 'Database password') do |password|
options[:pass] = password
end
options[:tail] = 10
opts.on('-l', '--last num', 'Number of logs to pre-fetch (default: 10)') do |tail|
options[:tail] = tail.to_i
end
options[:verbose] = false
opts.on('-v', '--with-id', 'Disable object id output (default: true)') do
options[:verbose] = true
end
options[:include] = nil
opts.on('-i', '--include-only fields', 'Include only given fields (semicolon separated)') do |fields|
options[:include] = fields.split(";")
end
options[:exclude] = nil
opts.on('-e', '--exclude fields', 'Exclude given fields (semicolon separated)') do |fields|
options[:exclude] = fields.split(";")
end
end
optparse.parse!
# options validation
raise OptionParser::MissingArgument.new("db") if options[:db].nil?
# connect to the database
begin
db = Mongo::Connection.new(options[:host], options[:port]).db(options[:db])
rescue Mongo::ConnectionFailure => e
raise Exception.new("Unable to connect to MongoDB server")
end
# login if needed
if not options[:user].nil?
begin
options[:pass] = ask("Password: ") { |q| q.echo = false } if options[:pass].nil?
db.authenticate(options[:user], options[:pass])
rescue Mongo::AuthenticationError => e
raise Exception.new("Authentication error")
end
end
# retrieve collection
if db.collection_names.include?(options[:coll])
collection = db.collection(options[:coll])
else
raise Exception.new("Collection not found in the given database")
end
# make sure we are not exceeding the size of the collection when skipping
collection_size = collection.count
options[:tail] = collection_size if options[:tail] > collection_size
# create tailable cursor
cursor = Mongo::Cursor.new(collection,
:tailable => true,
:order => [['$natural', 1]]
).skip(collection.count - options[:tail])
# read logs one be one
while not cursor.closed?
begin
if doc = cursor.next_document
doc = doc.to_h
# filter out _id field
doc.delete("_id") unless options[:verbose]
# exclude other fields if the user so desires (include only)
doc.each_key do |key|
doc.delete(key) unless options[:include].index(key)
end unless options[:include].nil?
# exclude other fields if the user so desires (exclude)
options[:exclude].each do |field|
doc.delete(field)
end unless options[:exclude].nil?
# dump to standard output
puts doc.to_json
STDOUT.flush
end
rescue SignalException => e
break
rescue Exception => e
puts e
break
end
end
@padcom
Copy link
Author

padcom commented Jan 10, 2012

Required gems:

  • mongo
  • json
  • highline

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment