Class: Lumberjack::MongoDevice

Inherits:
Device
  • Object
show all
Defined in:
lib/lumberjack_mongo_device.rb

Overview

Write Lumberjack log entries to a MongoDB collection.

Log entries will be stored as documents in a collection with fields for:

  • time

  • severity (as a string i.e. “DEBUG”)

  • progname

  • pid

  • unit_of_work_id

  • message

Constant Summary collapse

TIME =
"time"
SEVERITY =
"severity"
PROGNAME =
"progname"
PID =
"pid"
UNIT_OF_WORK_ID =
"unit_of_work_id"
MESSAGE =
"message"
DEFAULT_BUFFER_SIZE =
50

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(collection_or_options, options = nil) ⇒ MongoDevice

Initialize the device by passing in either a Mongo::Collection object or a hash of options to create the collection. Available options are:

  • :host - The host name to connect to (defaults to localhost).

  • :port - The port to connect to (defaults to 27017).

  • :db - The database name to use (required).

  • :collection - The collection name to use (required).

  • :username - The username to authenticate with for database connections (optional).

  • :password - The password to authenticate with for database connections (optional).

  • :max - If the collection does not aleady exist it will be capped at this number of records.

  • :size - If the collection does not aleady exist it will be capped at this size in bytes.

  • :buffer_size - The number of entries that will be buffered before they are sent to MongoDB.

If the collection does not already exist, it will be created. If either the :max or :size options are provided, it will be created as a capped collection. Indexes will be created on unit_of_work_id and time.



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/lumberjack_mongo_device.rb', line 47

def initialize(collection_or_options, options = nil)
  if collection_or_options.is_a?(Hash)
    options = collection_or_options.dup
    host = options.delete(:host)
    port = options.delete(:port)
    db_name = options.delete(:db)
    collection = options.delete(:collection)
    username = options.delete(:username)
    password = options.delete(:password)
    max = options.delete(:max)
    size = options.delete(:size)
    
    @buffer_size = options.delete(:buffer_size) || DEFAULT_BUFFER_SIZE
    
    connection = Mongo::Connection.new(host, port, options)
    db = connection.db(db_name)
    db.authenticate(username, password) if username && password
    if db.collections.collect{|coll| coll.name}.include?(collection.to_s)
      @collection = db.collection(collection)
    else
      begin
        @collection = db.create_collection(collection, :capped => (max || size), :max => max, :size => size)
        @collection.ensure_index(:time)
        @collection.ensure_index(:unit_of_work_id)
      rescue Mongo::OperationFailure
        # Create collection can fail if multiple processes try to create it at once.
        @collection = db.collection(collection)
        raise unless @collection
      end
    end
  else
    @collection = collection_or_options
    @buffer_size = options[:buffer_size] if options
    @buffer_size ||= DEFAULT_BUFFER_SIZE
  end
  
  @buffer = []
  @lock = Mutex.new
end

Instance Attribute Details

#buffer_sizeObject

The size of the internal buffer. Log entries are buffered so they can be sent to MongoDB in batches for efficiency.



29
30
31
# File 'lib/lumberjack_mongo_device.rb', line 29

def buffer_size
  @buffer_size
end

#collectionObject (readonly)

Get the MongoDB collection that is being written to.



26
27
28
# File 'lib/lumberjack_mongo_device.rb', line 26

def collection
  @collection
end

Instance Method Details

#closeObject



116
117
118
119
120
121
# File 'lib/lumberjack_mongo_device.rb', line 116

def close
  flush
  @lock.synchronize do
    @collection.db.connection.close
  end
end

#find(selector, options = {}, &block) ⇒ Object

Retrieve Lumberjack::LogEntry objects from the MongoDB collection. If a block is given, it will be yielded to with each entry. Otherwise, it will return an array of all the entries.



125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/lumberjack_mongo_device.rb', line 125

def find(selector, options = {}, &block)
  entries = []
  @collection.find(selector, options) do |cursor|
    cursor.each do |doc|
      entry = LogEntry.new(doc[TIME], doc[SEVERITY], doc[MESSAGE], doc[PROGNAME], doc[PID], doc[UNIT_OF_WORK_ID])
      if block_given?
        yield entry
      else
        entries << entry
      end
    end
  end
  block_given? ? nil : entries
end

#flushObject



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/lumberjack_mongo_device.rb', line 94

def flush
  docs = []
  @lock.synchronize do
    @buffer.each do |entry|
      docs << {:time => entry.time, :severity => entry.severity_label, :progname => entry.progname, :pid => entry.pid, :unit_of_work_id => entry.unit_of_work_id, :message => entry.message}
    end
    begin
      @collection.insert(docs)
    rescue => e
      puts e.inspect
      puts e.backtrace.join("\n")
      $stderr.write("#{e.class.name}: #{e.message}#{' at ' + e.backtrace.first if e.backtrace}")
      @buffer.each do |entry|
        $stderr.puts(entry.to_s)
      end
      $stderr.flush
    ensure
      @buffer.clear
    end
  end
end

#last(number_of_entries = 1) ⇒ Object

Retrieve the last entries from the log.



141
142
143
# File 'lib/lumberjack_mongo_device.rb', line 141

def last(number_of_entries = 1)
  find(nil, :sort => [:_id, :descending], :limit => number_of_entries).reverse
end

#write(entry) ⇒ Object



87
88
89
90
91
92
# File 'lib/lumberjack_mongo_device.rb', line 87

def write(entry)
  @lock.synchronize do
    @buffer << entry
  end
  flush if @buffer.size >= @buffer_size
end