Utils/Logger: Complete route caching
g0dil [Mon, 29 Oct 2007 17:24:57 +0000 (17:24 +0000)]
git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@478 270642c3-0616-0410-b53a-bc976706d245

16 files changed:
Utils/Logger/AreaRegistry.cc [new file with mode: 0644]
Utils/Logger/AreaRegistry.cci
Utils/Logger/AreaRegistry.hh
Utils/Logger/AreaRegistry.ih
Utils/Logger/Config.hh
Utils/Logger/Config.ih
Utils/Logger/Definitions.hh
Utils/Logger/Log.test.cc
Utils/Logger/Mainpage.dox
Utils/Logger/Parameters.ih
Utils/Logger/StreamRegistry.cci
Utils/Logger/StreamRegistry.hh
Utils/Logger/Target.cc
Utils/Logger/Target.cci
Utils/Logger/Target.cti
Utils/Logger/Target.hh

diff --git a/Utils/Logger/AreaRegistry.cc b/Utils/Logger/AreaRegistry.cc
new file mode 100644 (file)
index 0000000..48c9eaa
--- /dev/null
@@ -0,0 +1,113 @@
+// $Id$
+//
+// Copyright (C) 2007 
+// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
+// Kompetenzzentrum fuer NETwork research (NET)
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief AreaRegistry non-inline non-template implementation */
+
+#include "AreaRegistry.hh"
+#include "AreaRegistry.ih"
+
+// Custom includes
+#include "Target.hh"
+
+//#include "AreaRegistry.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////
+// senf::log::detail::AreaBase
+
+prefix_ void senf::log::detail::AreaBase::updateRoutingCache(Target & target,
+                                                             StreamBase const & stream,
+                                                             unsigned limit)
+    const
+{
+    if (stream.index >= routingCache_.size())
+        routingCache_.resize(stream.index+1);
+    unsigned l (limit);
+    Routes::iterator i (routingCache_[stream.index].routes.begin());
+    Routes::iterator const i_end (routingCache_[stream.index].routes.end());
+    for (; i != i_end; ++i) {
+        if (i->target == &target) {
+            i->limit = limit;
+            break;
+        }
+        if (i->limit < l)
+            l = i->limit;
+    }
+    if (i == i_end) 
+        routingCache_[stream.index].routes.push_back(RouteEntry(limit, &target));
+    else
+        for (; i != i_end; ++i)
+            if (i->limit < l)
+                l = i->limit;
+    routingCache_[stream.index].limit = l;
+}
+
+prefix_ void senf::log::detail::AreaBase::removeRoutingCache(Target & target,
+                                                             StreamBase const & stream)
+    const
+{
+    if (stream.index >= routingCache_.size())
+        return;
+    unsigned l (DISABLED::value);
+    Routes::iterator i (routingCache_[stream.index].routes.begin());
+    Routes::iterator const i_end (routingCache_[stream.index].routes.end());
+    while (i != i_end) {
+        if (i->target == &target)
+            i = routingCache_[stream.index].routes.erase(i);
+        else {
+            if (i->limit < l)
+                l = i->limit;
+            ++i;
+        }
+    }
+    routingCache_[stream.index].limit = l;
+}
+
+prefix_ void senf::log::detail::AreaBase::write(boost::posix_time::ptime timestamp,
+                                                StreamBase const & stream, unsigned level,
+                                                std::string msg)
+    const
+{
+    if (stream.index >= routingCache_.size())
+        return;
+    Routes::iterator i (routingCache_[stream.index].routes.begin());
+    Routes::iterator const i_end (routingCache_[stream.index].routes.end());
+    for (; i != i_end; ++i)
+        i->target->write(timestamp, stream, *this, level, msg);
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "AreaRegistry.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
index fa19ed5..0d9eb37 100644 (file)
@@ -61,6 +61,13 @@ prefix_ void senf::log::AreaRegistry::registerArea(detail::AreaBase const & area
     registry_.insert( std::make_pair(area.v_name(), &area) );
 }
 
+prefix_ senf::log::detail::AreaBase const *
+senf::log::AreaRegistry::lookup(std::string const & name)
+{
+    Registry::iterator i (registry_.find(name));
+    return i == registry_.end() ? 0 : i->second;
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // senf::log::detail::AreaBase
 
@@ -81,18 +88,11 @@ prefix_ void senf::log::detail::AreaBase::init()
     senf::log::AreaRegistry::instance().registerArea(*this);
 }
 
-prefix_ unsigned senf::log::detail::AreaBase::streamLimit(StreamBase const & stream)
-    const
-{
-    return stream.index >= streamLimits_.size() ? DISABLED::value : streamLimits_[stream.index];
-}
-
-prefix_ void senf::log::detail::AreaBase::setStreamLimit(StreamBase const & stream, unsigned value)
+prefix_ unsigned senf::log::detail::AreaBase::limit(StreamBase const & stream)
     const
 {
-    if (stream.index >= streamLimits_.size())
-        streamLimits_.resize(stream.index+1,0u);
-    streamLimits_[stream.index] = value;
+    return stream.index >= routingCache_.size() ? 
+        DISABLED::value : routingCache_[stream.index].limit;
 }
 
 ///////////////////////////////cci.e///////////////////////////////////////
index 6dd3d30..8decd55 100644 (file)
@@ -78,6 +78,7 @@ namespace log {
         AreaRegistry();
 
         void registerArea(detail::AreaBase const & area);
+        detail::AreaBase const * lookup(std::string const & name);
 
         Registry registry_;
 
index 362c3f6..e8773fe 100644 (file)
 // Custom includes
 #include <string>
 #include <vector>
+#include <list>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include "Levels.hh"
 
 ///////////////////////////////ih.p////////////////////////////////////////
 
 namespace senf {
 namespace log {
+
+    class Target;
+
 namespace detail {
 
     class StreamBase;
@@ -48,13 +54,26 @@ namespace detail {
 
         void init();
 
-        unsigned streamLimit(StreamBase const & stream) const;
-        void setStreamLimit(StreamBase const & stream, unsigned value) const;
+        unsigned limit(StreamBase const & stream) const;
+        void updateRoutingCache(Target & target, StreamBase const & stream, unsigned limit) const; 
+        void removeRoutingCache(Target & target, StreamBase const & stream) const; 
+        void write(boost::posix_time::ptime timestamp, StreamBase const & stream, unsigned level,
+              std::string msg) const;
 
     private:
-        typedef std::vector<unsigned> StreamLimits;
-        // mutable since this is a cache and may therefore change at unexpected places ...
-        mutable StreamLimits streamLimits_;
+        struct RouteEntry {
+            RouteEntry(unsigned limit_, Target * target_) : limit(limit_), target(target_) {}
+            unsigned limit;
+            Target * target;
+        };
+        typedef std::list<RouteEntry> Routes;
+        struct CacheEntry {
+            CacheEntry() : limit (DISABLED::value), routes() {}
+            unsigned limit;
+            Routes routes;
+        };
+        typedef std::vector<CacheEntry> RoutingCache;
+        mutable RoutingCache routingCache_;
     };
 
 }}}
index 27a9af8..cd0186a 100644 (file)
     <em>Runtime</em> configuration on the other hand deals with routing all those messages, which
     are enabled at compile time to the logging targets. If a message is not routed, it will be
     discarded. This allows to additionally disable messages at run-time.
-
-    \fixme Restructure compile-time configuration: Only allow stream based configuration using
-        SENF_LOG_CONF. For more complex configuration, accept a macro SENF_LOG_CONFFILE which, if
-        defined, must define the path of a file to be included. Area specific configuration must be
-        done in this include file. The area must be complete (not only a predeclaration) at that
-        point.
  */
 
 namespace senf {
index 6fec502..f8ed4f7 100644 (file)
@@ -94,7 +94,6 @@ namespace detail {
 #   define SLC_elt(r, data, elt) \
         SENF_LOG_CONF_DEFINE elt
 
-    // Need to use fold here to not exhaust the maximum FOR nesting depth ...
     BOOST_PP_SEQ_FOR_EACH(SLC_elt, none, SENF_LOG_CONF)
 
 #   undef SLC_elt
index ecf05b5..0429267 100644 (file)
@@ -77,8 +77,8 @@ namespace log {
 
     /** \brief Define new default log area for the class
 
-        This command declares the containing class to be it's own default log area. It is such like a
-        combination of \ref SENF_LOG_DEF_AREA and \ref SENF_LOG_DEFAULT_AREA with a twist.
+        This command declares the containing class to be it's own default log area. It is such like
+        a combination of \ref SENF_LOG_DEF_AREA and \ref SENF_LOG_DEFAULT_AREA with a twist.
 
         \hideinitializer
      */
index b4a1cbd..0bec6da 100644 (file)
@@ -28,7 +28,8 @@
 
 // We need to put all tests into this single file to not violate the ODR
 
-#define SENF_LOG_CONF (( (senf)(log)(Debug), (_), NOTICE ))
+#define SENF_LOG_CONF (( (senf)(log)(Debug), (_), NOTICE )) \
+                      (( (not_anonymous)(myStream), (not_anonymous)(Foo), VERBOSE ))
 
 // Custom includes
 #include "Logger.hh"
@@ -61,6 +62,7 @@ BOOST_AUTO_UNIT_TEST(logger)
     senf::log::StringTarget target;
 
     target.route<senf::log::Debug>();
+    target.route("not_anonymous::myStream", "not_anonymous::Foo");
 
     // We cannot easily check the exact log string since that includes the current date/time
     
index 8eab9f3..5905c89 100644 (file)
@@ -51,8 +51,7 @@
             // This is a combination of SENF_LOG_DEF_AREA and SENF_LOG_DEFAULT_AREA.
             SENF_LOG_CLASS_AREA();
 
-            // Set default log parameters for this scope. The values here are not really
-            // necessary since these are the global default values
+            // Set default log parameters for this scope.
             SENF_LOG_DEFAULT_STREAM(foo::UserLog);
             SENF_LOG_DEFAULT_LEVEL(senf::log::NOTICE);
 
         design goals
         \li Flexible configuration at compile and runtime
         \li Concise usage and simple interface
-        \li Zero overhead for compile-time disabled log messages I did not find any non-mcaro
-        implementation which was not either completely convoluted, unusable or slow. So I turned to
-        a macro based implementation which can provide all the design goals stated above.
+        \li Zero overhead for compile-time disabled log messages 
+
+        I did not find any non-mcaro implementation which was not either completely convoluted,
+        unusable or slow. So I turned to a macro based implementation which can provide all the
+        design goals stated above.
  */
 
 \f
index 9c6529c..2c17541 100644 (file)
@@ -67,7 +67,7 @@ namespace detail {
     senf::mpl::rv<2> Parameters_select_(AreaBase *);
     template <class Base, class Param>
     struct Parameters_<Base,Param,2> : public Base
-    { typedef Param area; };
+    { typedef Param area; typedef Param area_base; };
 
     senf::mpl::rv<3> Parameters_select_(LevelBase *);
     template <class Base, class Param>
@@ -96,7 +96,7 @@ namespace detail {
         typename boost::disable_if< boost::is_convertible<T*,AliasBase*> >::type * = 0);
     template <class Base, class Param>
     struct Parameters_<Base,Param,6> : public Base
-    { typedef typename Param::SENFLogArea area; };
+    { typedef typename Param::SENFLogArea area; typedef Param area_base; };
 
 #endif
 
@@ -110,13 +110,12 @@ namespace detail {
 
         static bool const compileEnabled = senf::log::Enabled<
             typename Base::stream, 
-            typename Base::area, 
+            typename Base::area_base, 
             level>::value;
 
         static bool enabled() { 
             return compileEnabled
-                && Base::area::instance().streamLimit(Base::stream::instance()) 
-                <= Base::level::value;
+                && Base::area::instance().limit(Base::stream::instance()) <= level::value;
         }
     };
 
index 5bb5b74..aba62ca 100644 (file)
@@ -59,6 +59,13 @@ prefix_ void senf::log::StreamRegistry::registerStream(detail::StreamBase const
     registry_.insert( std::make_pair(stream.v_name(), &stream) );
 }
 
+prefix_ senf::log::detail::StreamBase const *
+senf::log::StreamRegistry::lookup(std::string const & name)
+{
+    Registry::iterator i (registry_.find(name));
+    return i == registry_.end() ? 0 : i->second;
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // senf::log::detail::StreamBase
 
index 6fd6dcd..a47e836 100644 (file)
@@ -65,7 +65,7 @@ namespace log {
         typedef boost::transform_iterator<SelectName, Registry::const_iterator> iterator;
 
 #       ifdef DOXYGEN
-        // Hmm ... doxygen does not understand using declarations ...
+        // Hmm ... doxygen does not understand 'using declarations' ...
         /// Access stream registry singleton instance
         static AreaRegistry & instance();
 #       endif
@@ -79,6 +79,7 @@ namespace log {
         StreamRegistry();
 
         void registerStream(detail::StreamBase const & stream);
+        detail::StreamBase const * lookup(std::string const & name);
 
         Registry registry_;
 
index 997696a..11142a7 100644 (file)
@@ -43,14 +43,48 @@ prefix_ senf::log::Target::Target()
 prefix_ senf::log::Target::~Target()
 {
     while( ! rib_.empty()) {
-        // This is slower but simplifies the area cache handling and removing a target
-        // should be quite seldom
+        // This is terribly slow but simplifies the area cache handling and removing a target should
+        // be quite seldom
         RIB::reverse_iterator i (rib_.rbegin());
-        unroute(i->stream, i->area, i->level);
+        unroute(i->stream, i->area, i->level, i->action);
     }
     TargetRegistry::instance().unregisterTarget(this);
 }
 
+prefix_ void senf::log::Target::updateRoutingCache(detail::StreamBase const * stream,
+                                                   detail::AreaBase const * area)
+{
+    if (! stream) {
+        StreamRegistry::Registry::iterator i (StreamRegistry::instance().registry_.begin());
+        StreamRegistry::Registry::iterator const i_end (StreamRegistry::instance().registry_.end());
+        for (; i != i_end ; ++i)
+            updateRoutingCache(i->second, area);
+        return;
+    }
+    if (! area) {
+        AreaRegistry::Registry::iterator i (AreaRegistry::instance().registry_.begin());
+        AreaRegistry::Registry::iterator const i_end (AreaRegistry::instance().registry_.end());
+        for (; i != i_end ; ++i)
+            updateRoutingCache(stream, i->second);
+        return;
+    }
+    unsigned limit (DISABLED::value);
+    RIB::iterator i (rib_.begin());
+    RIB::iterator const i_end (rib_.end());
+    for(; i != i_end; ++i)
+        if ( (! i->stream || i->stream == stream) &&
+             (! i->area || i->area == area) &&
+             i->action == ACCEPT ) {
+            unsigned l (i->level == NONE::value ? i->stream->defaultRuntimeLimit() : i->level);
+            if (l < limit)
+                limit = l;
+        }
+    if (limit == DISABLED::value)
+        area->removeRoutingCache(*this, *stream);
+    else
+        area->updateRoutingCache(*this, *stream, limit);
+}
+
 prefix_ void senf::log::Target::write(boost::posix_time::ptime timestamp,
                                       detail::StreamBase const & stream,
                                       detail::AreaBase const & area, unsigned level,
@@ -59,10 +93,11 @@ prefix_ void senf::log::Target::write(boost::posix_time::ptime timestamp,
     RIB::iterator i (rib_.begin());
     RIB::iterator const i_end (rib_.end());
     for (; i != i_end; ++i)
-        if ( ( ! i->stream || i->stream == &stream ) &&
-             ( ! i->area || i->area == &area ) &&
+        if ( (! i->stream || i->stream == &stream) &&
+             (! i->area || i->area == &area) &&
              (i->level == NONE::value ? i->stream->defaultRuntimeLimit() : i->level) <= level ) {
-            v_write(timestamp, stream.v_name(), area.v_name(), level, message);
+            if (i->action == ACCEPT)
+                v_write(timestamp, stream.v_name(), area.v_name(), level, message);
             return;
         }
 }
@@ -75,10 +110,7 @@ prefix_ void senf::log::TargetRegistry::write(detail::StreamBase const & stream,
                                               std::string msg)
 {
     boost::posix_time::ptime timestamp (boost::posix_time::microsec_clock::universal_time());
-    Targets::iterator i (targets_.begin());
-    Targets::iterator i_end (targets_.end());
-    for(; i != i_end; ++i)
-        (*i)->write(timestamp, stream, area, level, msg);
+    area.write(timestamp, stream, level, msg);
 }
 
 ///////////////////////////////cc.e////////////////////////////////////////
index 129bef5..f58865e 100644 (file)
 ///////////////////////////////////////////////////////////////////////////
 // senf::log::Target
 
-prefix_ void senf::log::Target::route(detail::StreamBase const * stream,
-                                      detail::AreaBase const * area, unsigned level)
+prefix_ void senf::log::Target::route(std::string const & stream, action_t action)
 {
-    rib_.push_back(RoutingEntry(stream, area, level));
-
-    // Update the area/stream routing cache
-    if (area)
-        updateAreaCache(*area, stream, level);
-    else {
-        AreaRegistry::Registry::iterator i (AreaRegistry::instance().registry_.begin());
-        AreaRegistry::Registry::iterator const i_end (AreaRegistry::instance().registry_.end());
-        for (; i != i_end; ++i)
-            updateAreaCache(*(i->second), stream, level);
-    }
-        
+    detail::StreamBase const * s (StreamRegistry::instance().lookup(stream));
+    if (!s)
+        throw InvalidStreamException();
+    route(s, 0, NONE::value, action);
 }
 
-prefix_ void
-senf::log::Target::updateAreaCache(detail::AreaBase const & area,
-                                   detail::StreamBase const * stream, unsigned level)
+prefix_ void senf::log::Target::route(std::string const & stream, std::string const & area,
+                                      action_t action)
 {
-    if (stream) {
-        if (level < area.streamLimit(*stream))
-            area.setStreamLimit(*stream, level);
-    } else {
-        StreamRegistry::Registry::iterator i (StreamRegistry::instance().registry_.begin());
-        StreamRegistry::Registry::iterator const i_end (StreamRegistry::instance().registry_.end());
-        for(; i != i_end; ++i)
-            if (level < area.streamLimit(*(i->second)))
-                area.setStreamLimit(*(i->second),level);
-    }
+    detail::StreamBase const * s (StreamRegistry::instance().lookup(stream));
+    if (!s)
+        throw InvalidStreamException();
+    detail::AreaBase const * a (AreaRegistry::instance().lookup(area));
+    if (!a)
+        throw InvalidAreaException();
+    route(s, a, NONE::value, action);
+}
+
+prefix_ void senf::log::Target::route(std::string const & stream, unsigned level, action_t action)
+{
+    detail::StreamBase const * s (StreamRegistry::instance().lookup(stream));
+    if (!s)
+        throw InvalidStreamException();
+    route(s, 0, level, action);
+}
+
+prefix_ void senf::log::Target::route(std::string const & stream, std::string const & area,
+                                      unsigned level, action_t action)
+{
+    detail::StreamBase const * s (StreamRegistry::instance().lookup(stream));
+    if (!s)
+        throw InvalidStreamException();
+    detail::AreaBase const * a (AreaRegistry::instance().lookup(area));
+    if (!a)
+        throw InvalidAreaException();
+    route(s, a, level, action);
+}
+
+////////////////////////////////////////
+// private members
+
+prefix_ void senf::log::Target::route(detail::StreamBase const * stream,
+                                      detail::AreaBase const * area, unsigned level,
+                                      action_t action)
+{
+    rib_.push_back(RoutingEntry(stream, area, level, action));
+    if (action == ACCEPT)
+        updateRoutingCache(stream, area);
 }
 
 prefix_ void senf::log::Target::unroute(detail::StreamBase const * stream,
-                                        detail::AreaBase const * area, unsigned level)
+                                        detail::AreaBase const * area, unsigned level, 
+                                        action_t action)
 {
-    rib_.erase(std::remove(rib_.begin(), rib_.end(), RoutingEntry(stream, area, level)),
+    rib_.erase(std::remove(rib_.begin(), rib_.end(), RoutingEntry(stream, area, level, action)),
                rib_.end());
+    if (action == ACCEPT)
+        updateRoutingCache(stream, area);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::log::Target::RoutingEntry
+
+prefix_ senf::log::Target::RoutingEntry::RoutingEntry(detail::StreamBase const * stream_,
+                                                      detail::AreaBase const * area_,
+                                                      unsigned level_, action_t action_)
+    : stream(stream_), area(area_), level(level_), action(action_) 
+{}
+
+prefix_ senf::log::Target::RoutingEntry::RoutingEntry()
+    : stream(0), area(0), level(0), action(ACCEPT) 
+{}
 
-    ///\fixme Update area/stream routing cache
-    // Not doing anything here does not produce incorrect behavior, since removing a route
-    // can never lower the logging limit. Not updating the cache just reduces the performance.
+prefix_ bool senf::log::Target::RoutingEntry::operator==(RoutingEntry const & other)
+{ 
+    return 
+        stream == other.stream && 
+        area == other.area && 
+        level == other.level &&
+        action == other.action; 
 }
 
 ///////////////////////////////////////////////////////////////////////////
index ea7f5e2..a61c703 100644 (file)
 // senf::log::Target
 
 template <class Stream>
-prefix_ void senf::log::Target::route()
+prefix_ void senf::log::Target::route(action_t action)
 {
-    route(&Stream::instance(), 0, NONE::value);
+    route(&Stream::instance(), 0, NONE::value, action);
 }
 
-template <class Stream, class Arg0>
-prefix_ void senf::log::Target::route()
+template <class Stream, class Arg>
+prefix_ void senf::log::Target::route(action_t action)
 {
-    route<Arg0>(&Stream::instance(), static_cast<Arg0*>(0));
+    route<Arg>(&Stream::instance(), static_cast<Arg*>(0), action);
 }
 
 template <class Stream, class Area, class Level>
-prefix_ void senf::log::Target::route()
+prefix_ void senf::log::Target::route(action_t action)
 {
-    route(&Stream::instance(), &Area::instance(), Level::value);
+    route(&Stream::instance(), &Area::instance(), Level::value, action);
 }
 
 ////////////////////////////////////////
@@ -57,16 +57,16 @@ prefix_ void senf::log::Target::route()
 
 template <class Area>
 prefix_ void senf::log::Target::route(detail::StreamBase const * stream,
-                                      detail::AreaBase const *)
+                                      detail::AreaBase const *, action_t action)
 {
-    route(stream, &Area::instance(), NONE::value);
+    route(stream, &Area::instance(), NONE::value, action);
 }
 
 template <class Level>
 prefix_ void senf::log::Target::route(detail::StreamBase const * stream,
-                                      detail::LevelBase const *)
+                                      detail::LevelBase const *, action_t action)
 {
-    route(stream, 0, Level::value);
+    route(stream, 0, Level::value, action);
 }
 
 ///////////////////////////////////////////////////////////////////////////
index 24b6ea3..56cf519 100644 (file)
@@ -60,6 +60,8 @@ namespace log {
         ///////////////////////////////////////////////////////////////////////////
         // Types
 
+        enum action_t { ACCEPT, REJECT };
+
         ///////////////////////////////////////////////////////////////////////////
         ///\name Structors and default members
         ///@{
@@ -70,14 +72,28 @@ namespace log {
         ///@}
 
         template <class Stream>
-        void route();
+        void route(action_t action=ACCEPT);
 
-        template <class Stream, class Arg0>
-        void route();
+        template <class Stream, class Arg>
+        void route(action_t action=ACCEPT);
 
         template <class Stream, class Area, class Level>
-        void route();
+        void route(action_t action=ACCEPT);
+
+        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);
+
+        struct InvalidStreamException : public std::exception
+        { virtual char const * what() const throw() 
+                { return "senf::log::Target::InvalidStreamException"; } };
 
+        struct InvalidAreaException : public std::exception
+        { virtual char const * what() const throw() 
+                { return "senf::log::Target::InvalidAreaException"; } };
+        
     protected:
 
         std::string timestamp();
@@ -85,18 +101,17 @@ namespace log {
     private:
 
         void route(detail::StreamBase const * stream, detail::AreaBase const * area, 
-                   unsigned level);
+                   unsigned level, action_t action);
         void unroute(detail::StreamBase const * stream, detail::AreaBase const * area, 
-                     unsigned level);
+                     unsigned level, action_t action);
 
         template <class Area>
-        void route(detail::StreamBase const * stream, detail::AreaBase const *);
+        void route(detail::StreamBase const * stream, detail::AreaBase const *, action_t action);
 
         template <class Level>
-        void route(detail::StreamBase const * stream, detail::LevelBase const *);
-
-        void updateAreaCache(detail::AreaBase const & area, detail::StreamBase const * stream,
-                             unsigned level);
+        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,
                    detail::AreaBase const & area, unsigned level, std::string const & message);
@@ -116,24 +131,21 @@ namespace log {
         struct RoutingEntry 
         {
             RoutingEntry(detail::StreamBase const * stream_, detail::AreaBase const * area_, 
-                         unsigned level_)
-                : stream(stream_), area(area_), level(level_) {}
-            RoutingEntry() 
-                : stream(0), area(0), level(0) {}
+                         unsigned level_, action_t action_);
+            RoutingEntry();
 
-            bool operator==(RoutingEntry const & other) 
-                { return stream == other.stream && area == other.area && level == other.level; }
+            bool operator==(RoutingEntry const & other);
 
             detail::StreamBase const * stream;
             detail::AreaBase const * area;
-            unsigned level;
+            unsigned level;            action_t action;
         };
 
         typedef std::vector<RoutingEntry> RIB;
 
         RIB rib_;
         
-        friend class TargetRegistry;
+        friend class detail::AreaBase;
     };
 
     /** \brief Target registry