Boost logo

Boost :

From: Caleb Epstein (caleb.epstein_at_[hidden])
Date: 2005-12-19 17:15:20

On 12/19/05, Gennadiy Rozental <gennadiy.rozental_at_[hidden]> wrote:
> I am all for it ;)

OK, so to that end, please find attached a sketchy implementation of some of
these concepts. The concepts are:

   - Entry - represents a message to be logged. This can be a single
   std::string or a more complex structure, whatever the user likes. Should
   model OutputStreamable, but this requirement is up to the Sink. Filters may
   place additional requirements on the Entry.
   - Filter - a functor that is used to determine if an Entry should be
   sent to a Logger's Sink list. A model of std::unary_function<Entry, bool>
   - Sink - a destination for Entries. Concrete implementations might
   write to a file, debug window, etc. A model of std::unary_function<Entry,
   - Logger - the object to which Entries are sent by the user. Contains
   a Filter and a list of Sinks. When the user sends an Entry to the Logger,
   it will call the Filter functor and, if it returns true, pass it to each of
   the Sinks it holds.

I've attached a sample implementation (log.h) which contains the following
classes in "namespace logging" (I ran into collisions with math.h when I
tried "namespace log"):

   - template<typename Entry> null_filter: a Filter that always returns
   - template<typename Entry> basic_file_sink: a Sink implementation that
   opens a std::ofstream and writes Entries to it
   - template<typename Entry> basic_ostream_sink: a Sink that holds an
   std::ostream reference and writes Entries to it.
   - template<typename Entry> basic_logger: the implementation of the
   Logger concept. Holds a Filter and a std::list of Sinks, where Filter is
   implemented as boost::function<bool(Entry)> and Sink is implemented as
   boost::function<void(Entry)>. Provides non-const reference getters for the
   filter and sinks members, and of course implements the all important "write"
   method that does the real work of checking the Filter and passing the Entry
   to the Sinks if the Filter returned true.

I've also provided (in tuple_log.h) an implementation of the Entry concept
which attempts to implement Gennadiy's ideas of level, category, and
keyword. It is in "namespace tuple_log" and is simply a boost::tuple<level,
category, std::string, std::string> where level and category are just
wrapped enums with streaming operations added (the proposed BOOST_ENUM would
be handy here).

Along with the tuple-style Entry class there is an associated filter
functor. It can be used to filter messages based on level (>= some
threshold), category (matching a bitmask), and keyword (keyword appears in
an "include" list and does not appear in an "exclude" list). There is a
test program (log.cpp) that exercises these classes and attempts to do some

Tested on g++ 3.3.4 on Linux and MSVC8 on Win2000.

Some open issues I'm looking for help with:

   - I'm more or less ignorant when it comes to wide-strings,
   wide-streams and the like. Would these interfaces need to be paramterized
   on CharT, or would/should it be possible to support both narrow and wide
   input with a single interface?
   - I decided that the Entry concept would represent the entire
   message-to-be-logged, including any metadata that goes along with it (
   e.g. level, category, keyword, thread ID, timestamp, __FILE__,
   __LINE__, etc). The Filter implementation is consequently parameterized on
   Entry, which means you need a fully-formed Entry (including the message
   portion) to call "basic_logger::is_enabled" to check to see if an Entry
   should even be formatted for writing.

Thoughts and feedback are most welcome,

Caleb Epstein
caleb dot epstein at gmail dot com

Boost list run by bdawes at, gregod at, cpdaniel at, john at