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 =
0

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. Default is not to buffer.

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



123
124
125
126
127
128
# File 'lib/lumberjack_mongo_device.rb', line 123

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.



132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/lumberjack_mongo_device.rb', line 132

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



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/lumberjack_mongo_device.rb', line 101

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.



148
149
150
# File 'lib/lumberjack_mongo_device.rb', line 148

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

#write(entry) ⇒ Object



94
95
96
97
98
99
# File 'lib/lumberjack_mongo_device.rb', line 94

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