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 acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk