
On 12/19/05, Gennadiy Rozental <gennadiy.rozental@thomson.com> 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, void> - 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 true. - 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 benchmarking. 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