X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=Utils%2FLogger%2FTarget.hh;h=ff8fc5467fe0cb7df75feed8c8805a9d14be373e;hb=456ee576285b76aa46240f8001f426757810dcc1;hp=56cf519b877be646f7e8b2e65c0c728f1ffecc55;hpb=f6f670f2dbc82b77db29df6cd452f2b351b9662a;p=senf.git diff --git a/Utils/Logger/Target.hh b/Utils/Logger/Target.hh index 56cf519..ff8fc54 100644 --- a/Utils/Logger/Target.hh +++ b/Utils/Logger/Target.hh @@ -1,8 +1,8 @@ // $Id$ // -// Copyright (C) 2007 -// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS) -// Kompetenzzentrum fuer Satelitenkommunikation (SatCom) +// Copyright (C) 2007 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY // Stefan Bund // // This program is free software; you can redistribute it and/or modify @@ -28,31 +28,99 @@ // Custom includes #include -#include +#include #include +#include #include "../singleton.hh" #include "../mpl.hh" #include "StreamRegistry.hh" -#include "AreaRegistry.hh" +#include "../Exception.hh" +#include "TimeSource.hh" //#include "Target.mpp" ///////////////////////////////hh.p//////////////////////////////////////// -namespace senf { -namespace log { +/** \defgroup targets Targets - class TargetRegistry; + Targets receive log messages and write them to some destination: The console, a log file, an SQL + DB and so on. Every target is derived from the \ref senf::log::Target base class. This base + class provides the target with the necessary routing infrastructure. The different targets only + differ in the way, they write the data. - /** \brief Logging target base class + \see senf::log::Target + */ - All enabled log messages are eventually routed to one or more logging targets. It is the - responsibility of the logging target to write the log messages somewhere: onto the console, - to a file, to mail them to the administrator or whatever. To this end, the logging target is - passed the log message and a complete set of logging parameters (\e stream, \e area and \e - level). +namespace senf { +namespace log { + + namespace detail { class TargetRegistry; } + namespace detail { class AreaBase; } - \fixme optionally Integrate with Scheduler / ClockService to reduce number of gettimeofday() - calls. + /** \brief Logging target base class + + Targets are the final destination of %log messages. Every message is eventually routed to one + or several targets. + + \section target_routing Routing + + Each target manages a routing table. The message meta-data (stream, area and level) is + matched against this table. If an entry matches, the action associated with this entry is + taken (either \c ACCEPT or \c REJECT). + + Every target manages it's own routing table. Conceptually, every routing message will be + routed to every target where it will then be matched against each targets routing table (the + implementation is more efficient and utilizes a routing cache). + + Each routing entry consists of the following parameters + \li (optional) \e stream. The entry will match only messages directed at that stream + \li (optional) \e area. If the area is specified, only messages directed at that area are + matched, otherwise any area will be allowed + \li (optional) \e level. If the log level is specified, messages will be accepted if their + level is at least that value. If the value is not specified, the limit will be taken + from the stream's default value. + + Each parameter (stream, area and level) has two representations: A static (compile time + constant) representation, which is the representation also used in the log statements, and a + dynamic representation, which may be used for manipulating the routing table. + + The static representation is used, when passing routing parameters via template arguments: + \code + target.route(senf::log::Target::REJECT); + target.route(); + \endcode + The identical routing statements may be expressed using dynamic routing via: + \code + target.route("foo::SomeStream", "", senf::log::NOTICE::value, senf::log::Target::REJECT); + target.route("foo::SomeStream"); + \endcode + The static representation has the benefit of being compile-time type checked: Invalid + routing parameters will be caught while compiling the code. The dynamic representation is + more flexible as it allows to adjust routing from user input (e.g. configuration files). + + The different object representations are: + \li The \e streams is statically represented by it's name, which is the name of a class + defined with \ref SENF_LOG_DEFINE_STREAM. The dynamic representation is a string + representation of this name. + \li The \e area is statically represented by it's name, which again is the name of a class + defined with \ref SENF_LOG_DEFINE_STREAM. The dynamic representation again is a string + representation of this class's name. The dynamic representation represents an absent + area with the empty string. + \li The \e level is statically represented by a level class from \ref + loglevels. Dynamically, it is represented by an unsigned integer number, the \c value + member of that class. + + \section target_impl Implementing new targets + + To implement a new target type, you need to derive from senf::log::Target and implement the + single \c v_write member. This member will be called whenever a message should be output. + + The target may process in any arbitrary way: reformat, writing it into an SQL DB, whatever + can be envisioned. However, there is one important limitation: The \c v_write call must not + block. So for more complex scenarios, additional measures must be taken (e.g. writing a %log + backend daemon which receives the messages via UDP and processes them). Of course, in rare + cases messages might be lost but this cannot be avoided. + + \see \ref targets */ class Target : private boost::noncopyable { @@ -60,121 +128,248 @@ namespace log { /////////////////////////////////////////////////////////////////////////// // Types - enum action_t { ACCEPT, REJECT }; + /** \brief Routing action - /////////////////////////////////////////////////////////////////////////// - ///\name Structors and default members - ///@{ + Every routing entry is associated with a routing action. This action is final (for this + target. Each target is processed independently). + */ + enum action_t { + ACCEPT /** Output message */ + , REJECT /** Suppress message output */ + }; - Target(); - virtual ~Target(); + /** \brief Target routing entry - ///@} + A single routing entry matches messages against their \e stream, \e area and \e + level. If the entry matches, the given \e action is performed. + + \see senf::log::Target + */ + struct RoutingEntry + { + std::string stream() const; ///< Stream to match + std::string area() const; ///< Area to match (empty of unspecified) + unsigned level() const; ///< Level to match (senf::log::NONE::value if unspecified) + action_t action() const; ///< Action to take + +# ifdef DOXYGEN + private: +# endif - template - void route(action_t action=ACCEPT); + RoutingEntry(); + bool operator==(RoutingEntry const & other); - template - void route(action_t action=ACCEPT); + private: + RoutingEntry(detail::StreamBase const * stream, detail::AreaBase const * area, + unsigned level, action_t action); - template - void route(action_t action=ACCEPT); + detail::StreamBase const * stream_; + detail::AreaBase const * area_; + unsigned level_; + action_t action_; + + friend class Target; + }; - void route(std::string const & stream, action_t action=ACCEPT); - void route(std::string const & stream, std::string const & area, action_t action=ACCEPT); - void route(std::string const & stream, unsigned level, action_t action=ACCEPT); - void route(std::string const & stream, std::string const & area, unsigned level, - action_t action=ACCEPT); + private: + typedef std::vector RIB; - struct InvalidStreamException : public std::exception - { virtual char const * what() const throw() - { return "senf::log::Target::InvalidStreamException"; } }; + public: + typedef RIB::const_iterator iterator; ///< Routing table iterator - struct InvalidAreaException : public std::exception - { virtual char const * what() const throw() - { return "senf::log::Target::InvalidAreaException"; } }; - - protected: + /////////////////////////////////////////////////////////////////////////// + ///\name Structors and default members + ///@{ - std::string timestamp(); + Target(); + virtual ~Target(); - private: + ///@} + /////////////////////////////////////////////////////////////////////////// + ///\name Routing + ///\{ + +# ifdef DOXYGEN + + template void route( + action_t action = ACCEPT, int index = -1); ///< Add route (static) + /**< Add a route for the given combination of \a Stream, \a + Area and \a Level. All parameters (\a Stream, \a Area + and \a Level) are optional (the template signature is + shown simplified here). Examples: + \code + target.route(); + target.route(); + target.route(); + target.route(); + target.route(); + \endcode + + See the class description for information on the \a + action and \a index parameters + + \tparam Stream stream to match + \tparam Area area to match + \tparam Level level, matches messages with + at least the given level. + \param[in] action routing action to take + \param[in] index position of new route in the routing + table */ + +# endif + + void route(std::string const & stream, std::string const & area = "", + unsigned level = NONE::value, action_t action = ACCEPT, int index = -1); + ///< Add route (dynamic) + /**< Add a route for the given combination of \a stream, \a + area and \a level. All parameters (\a Stream, \a Area + and \a Level) are optional and may be omitted by + setting them to the empty string or the + senf::log::NONE::value respectively. + + See the class description for information on the \a + action and \a index parameters + + \throws InvalidStreamException if the given \a stream + is not found in the StreamRegistry + \throws InvalidAreaException if the given \a area is + not found in the AreaRegistry + + \param[in] stream stream to match + \param[in] area area to match + \param[in] level level, matches messages with at least + the given level. + \param[in] action routing action to take + \param[in] index position of new route in the routing + table */ + +# ifdef DOXYGEN + + template + void unroute(action_t action = ACCEPT); + ///< Remove route (static) + /**< This member removes an arbitrary routing entry. The + template parameters are the same as for the + corresponding \ref route() call. + + The routing table is searched for a route exactly + matching the given specification. If such a route is + found, it will be removed, otherwise the call will be + ignored + + \tparam Stream stream to match + \tparam Area area to match + \tparam Level level, matches messages with + at least the given level. + \param[in] action routing action to take */ + +# endif + + void unroute(std::string const & stream, std::string const & area = "", + unsigned level = NONE::value, action_t action = ACCEPT); + ///< Remove route (dynamic) + /**< This member removes an arbitrary routing entry. The \a + stream parameter is mandatory while either \a area or + \a level may be left unspecified by setting them to the + empty string or senf::log::NONE::value respectively. + + The routing table is searched for a route exactly + matching the given specification. If such a route is + found, it will be removed, otherwise the call will be + ignored + + \param[in] stream stream to match + \param[in] area area to match + \param[in] level level, matches messages with at least + the given level. + \param[in] action routing action to take */ + void unroute(int index=-1); ///< Remove route (indexed) + /**< This call will remove the route with the given index. + + See the class documentation for more information on + indexing. + + \param[in] index index of routing entry to remove */ + +# ifndef DOXYGEN + + template + void route(action_t action = ACCEPT, int index = -1); + template + void route(action_t action = ACCEPT, int index = -1); + template + void route(action_t action = ACCEPT, int index = -1); + + template + void unroute(action_t action = ACCEPT); + template + void unroute(action_t action = ACCEPT); + template + void unroute(action_t action = ACCEPT); + +# endif + + ///\} + + /** \brief Exception: Invalid stream */ + struct InvalidStreamException : public senf::Exception + { InvalidStreamException() + : senf::Exception("senf::log::Target::InvalidStreamException"){} }; + + /** \brief Exception: Invalid area */ + struct InvalidAreaException : public senf::Exception + { InvalidAreaException() + : senf::Exception("senf::log::Target::InvalidAreaException"){} }; + iterator begin() const; ///< Iterator to beginning of routing table + iterator end() const; ///< Iterator past the end of routing table + + private: void route(detail::StreamBase const * stream, detail::AreaBase const * area, - unsigned level, action_t action); + unsigned level, action_t action, int index); void unroute(detail::StreamBase const * stream, detail::AreaBase const * area, unsigned level, action_t action); - template - void route(detail::StreamBase const * stream, detail::AreaBase const *, action_t action); - - template - void route(detail::StreamBase const * stream, detail::LevelBase const *, action_t action); - void updateRoutingCache(detail::StreamBase const * stream, detail::AreaBase const * area); - void write(boost::posix_time::ptime timestamp, detail::StreamBase const & stream, + void write(time_type timestamp, detail::StreamBase const & stream, detail::AreaBase const & area, unsigned level, std::string const & message); # ifdef DOXYGEN protected: # endif - virtual void v_write(boost::posix_time::ptime, std::string const & stream, + virtual void v_write(time_type timestamp, std::string const & stream, std::string const & area, unsigned level, std::string const & message) = 0; + ///< Called to write out the routing message + /**< This member must be defined in the derived class to + somehow format and write the %log message. -# ifdef DOXYGEN - private: -# endif - - struct RoutingEntry - { - RoutingEntry(detail::StreamBase const * stream_, detail::AreaBase const * area_, - unsigned level_, action_t action_); - RoutingEntry(); + Every %log message always possesses a complete set of + meta information (\a stream, \a area and \a level). - bool operator==(RoutingEntry const & other); + \note This member must \e not block since it may be + called from any unknown context. This prohibits + simple logging over NFS or many other network + protocols. - detail::StreamBase const * stream; - detail::AreaBase const * area; - unsigned level; action_t action; - }; + \param[in] timestamp %log message timing information + \param[in] stream message stream + \param[in] area message area + \param[in] level message level + \param[in] message the message string */ - typedef std::vector RIB; +# ifdef DOXYGEN + private: +# endif RIB rib_; friend class detail::AreaBase; + friend class detail::TargetRegistry; }; - /** \brief Target registry - - The TargetRegistry keeps a record of all existing targets. - */ - class TargetRegistry - : public senf::singleton - { - public: - using senf::singleton::instance; - - void write(detail::StreamBase const & stream, detail::AreaBase const & area, - unsigned level, std::string msg); - - private: - void registerTarget(Target * target); - void unregisterTarget(Target * target); - - typedef std::set Targets; - Targets targets_; - - friend class Target; - }; - - - template - void write(std::string msg); - }} ///////////////////////////////hh.e////////////////////////////////////////