Created
January 10, 2012 20:38
-
-
Save padcom/1591049 to your computer and use it in GitHub Desktop.
Tail logs from mongo collection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Required gems: