Utils/Logger: SyslogTarget
g0dil [Fri, 3 Oct 2008 08:37:15 +0000 (08:37 +0000)]
Utils/Logger: More consistently support optional stream spec

git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@923 270642c3-0616-0410-b53a-bc976706d245

Utils/Logger/Config.hh
Utils/Logger/Config.ih
Utils/Logger/IOStreamTarget.cc
Utils/Logger/SyslogTarget.cc [new file with mode: 0644]
Utils/Logger/SyslogTarget.cci [new file with mode: 0644]
Utils/Logger/SyslogTarget.hh [new file with mode: 0644]
Utils/Logger/SyslogTarget.test.cc [new file with mode: 0644]
Utils/Logger/Target.cc
Utils/Logger/Target.cci
Utils/Logger/Target.hh

index 34566b1..cd6ccc6 100644 (file)
     <pre>
     g++ ... -DSENF_LOG_CONF="(( (senf)(log)(Debug),(_),DISABLED ))
                              (( (senf)(log)(Debug),(foo)(SomeClass),VERBOSE ))
-                             (( (foo)(Transactions),(_),NOTICE ))" ...
+                             (( (_),(_),NOTICE ))" ...
     </pre>
     The value is relatively complex; It's a Boost.Preprocessor style sequence of tuples, of which
     the first and second elements are again sequences. What this boils down to, is that it allows to
-    configure compile time logging limits based on stream and optional area. 
+    configure compile time logging limits based on stream and optional area.
 
     The above example disables all debug logging by setting the default log limit for all areas on
-    the \c senf::log::Debug stream to \c DISABLED. It then re-enables debug logging only within the
-    \c foo::SomeClass area, where it is set to \c VERBOSE. Furthermore, the limit on the \c
-    foo::Transactions stream is set to \c NOTICE.
+    the \c senf::log::Debug stream to \c DISABLED. It enables debug logging only within the \c
+    foo::SomeClass area, where it is set to \c VERBOSE. Lastly, the global compile time limit is set
+    to \c NOTICE.
 
     There are two standard uses for this configuration: Either to disable most logging in final
     builds by changing the compile time limit to something like senf::log::IMPORTANT or to enable
     g++ ... -DSENF_LOG_CONF="(( (senf)(log)(Verbose), (some)(Area), VERBOSE ))"
     </pre>
 
+    All the entries specified via \c SENF_LOG_CONF are applied in a fixed order:
+    
+    \li First the entries which have both a stream and an area specified are checked
+    \li next all entries with area but no stream given are checked
+    \li followed by all entries with a given stream but no area
+    \li and lastly if no match was found until now, a generic entry without stream and area is
+        checked
+    \li if no matching entry is found, the default compile time limit of the stream is used
+
+    So an area specification has precedence over a stream specification.
+
+    \warning Enabling a message at compile time does \e not ensure, the message is shown. You
+        additionally need to \e route the message (see next chapter). This is especially true for \c
+        VERBOSE messages, which are default disabled at runtime.
 
     \see \ref SENF_LOG_CONF
 
@@ -83,8 +97,8 @@
 
     The runtime configuration is performed by routing messages to one or more logging targets:
     \code
-    senf::log::ConsoleLog & consoleLog (senf::log::ConsoleLog::instance());
-    senf::log::FileLog fileLog ("my.log");
+    senf::log::ConsoleTarget & consoleLog (senf::log::ConsoleTarget::instance());
+    senf::log::FileTarget fileLog ("my.log");
 
     consoleLog.route<senf::log::Debug>();
     consoleLog.route<foo::Transactions, foo::SomeClass>(senf::log::Target::REJECT);
     The routing statements are processed by the targets in order, the first matching rule will
     decide a log messages fate for that target.
 
+    \warning You can \e only route those messages at runtime which have been compile-time
+        enabled. By default, \c VERBOSE messages are \e disabled at compile time. They must be
+        enabled \e explicitly by setting \c SENF_LOG_CONF so they can be routed.
+
     \section config_fallback Fallback routing
     
     There are two cases, where this setup may lead to inadvertently lost log messages:
@@ -147,28 +165,30 @@ namespace log {
 
         \par ""
             <table class="ebnf">
-            <tr><td>conf</td>         <td>::= \e element \e element* \n</td></tr>
-            <tr><td>element</td>      <td>::= <tt>((</tt> \e stream <tt>,</tt> \e optional_area <tt>,</tt> \e level <tt>))</tt> \n</td></tr>
-            <tr><td>stream</td>       <td>::= \e scope_seq \n</td></tr>
-            <tr><td>optional_area</td><td>::= <tt>(_)</tt> | \e scope_seq \n</td></tr>
-            <tr><td>level</td>        <td>::= \c VERBOSE | \c NOTICE | \c MESSAGE | \c IMPORTANT | \c CRITICAL | \c DISABLED \n</td></tr>
-            <tr><td>scope_seq</td>    <td>::= \e scope \e scope* \n</td></tr>
-            <tr><td>scope</td>        <td>::= <tt>(</tt> \e name <tt>)</tt> \n</td></tr>
-            <tr><td>name</td>         <td>::= arbitrary C++ identifier</td></tr>
+            <tr><td>conf</td>            <td>::= \e element \e element* \n</td></tr>
+            <tr><td>element</td>         <td>::= <tt>((</tt> \e optional_stream <tt>,</tt> \e optional_area <tt>,</tt> \e level <tt>))</tt> \n</td></tr>
+            <tr><td>optional_stream</td> <td>::= <tt>(_)</tt> | \e scope_seq \n</td></tr>
+            <tr><td>optional_area</td>   <td>::= <tt>(_)</tt> | \e scope_seq \n</td></tr>
+            <tr><td>level</td>           <td>::= \c VERBOSE | \c NOTICE | \c MESSAGE | \c IMPORTANT | \c CRITICAL | \c DISABLED \n</td></tr>
+            <tr><td>scope_seq</td>       <td>::= \e scope \e scope* \n</td></tr>
+            <tr><td>scope</td>           <td>::= <tt>(</tt> \e name <tt>)</tt> \n</td></tr>
+            <tr><td>name</td>            <td>::= arbitrary C++ identifier</td></tr>
             </table>
 
-        \ref SENF_LOG_CONF is a Boost.Preprocessor style sequence of 3-tuples. Each tuple applies to
-        a specific stream which is defined by the first tuple element \e stream. 
+        \ref SENF_LOG_CONF is a Boost.Preprocessor style sequence of 3-tuples. 
+
+        The first tuple element \e optional_stream specifies the stream to match. If this is
+        <tt>(_)</tt>, the entry will match any stream.
 
         The next tuple element, \e optional_area optionally restricts the entry to match only the
-        given area. 
+        given area. If set to <tt>(_)</tt>, the area is left unrestricted.
 
         The last tuple element \e level defines the compile time log level. Messages with a level
         below this are discarded at compile time.
 
-        Both \e stream and \e optional_area are given as a \e scope_seq. A scope sequence is a fully
-        qualified C++ identifier placed into a sequence: <tt>foo::bar::baz</tt> is represented by
-        <tt>(foo)(bar)(baz)</tt>.
+        Both \e optional_stream and \e optional_area are given as a \e scope_seq. A scope sequence
+        is a fully qualified C++ identifier placed into a sequence: <tt>foo::bar::baz</tt> is
+        represented by <tt>(foo)(bar)(baz)</tt>.
      */
 #   define SENF_LOG_CONF
 
index 1ead3fc..f2f6777 100644 (file)
@@ -44,18 +44,30 @@ namespace detail {
     struct na {};
 
     /// Internal: Compile time configuration for given \a Stream and \a Area
-    template <class Stream, class Area>
+    template <class Stream, class Area, class QueryStream=Stream, bool Query=true>
     struct Config
     {
-        typedef typename Config<Stream,na>::compileLimit compileLimit;
+        typedef typename Config<na, Area, Stream, Query>::compileLimit compileLimit;
     };
 
 #   ifndef DOXYGEN
 
-    template <class Stream>
-    struct Config<Stream, na>
+    template <class Area, class QueryStream, bool Query>
+    struct Config<na, Area, QueryStream, Query>
     {
-        typedef typename Stream::compileLimit compileLimit;
+        typedef typename Config<QueryStream, na, QueryStream, Query>::compileLimit compileLimit;
+    };
+
+    template <class Stream, class QueryStream, bool Query>
+    struct Config<Stream, na, QueryStream, Query>
+    {
+        typedef typename Config<na, na, QueryStream, Query>::compileLimit compileLimit;
+    };
+
+    template <class QueryStream, bool Query>
+    struct Config<na, na, QueryStream, Query>
+    {
+        typedef typename QueryStream::compileLimit compileLimit;
     };
 
 #   endif
@@ -88,9 +100,11 @@ namespace detail {
     SENF_LOG_PREDECL(stream)                                                                      \
     SENF_LOG_PREDECL(area)                                                                        \
     namespace senf { namespace log { namespace detail {                                           \
-        template <>                                                                               \
+        template <class QueryStream>                                                              \
         struct Config< SENF_LOG_SEQ_TO_NAME(stream),                                              \
-                       SENF_LOG_SEQ_TO_NAME(area) >                                               \
+                       SENF_LOG_SEQ_TO_NAME(area),                                                \
+                       QueryStream,                                                               \
+                       true >                                                                     \
         { typedef senf::log::level compileLimit; };                                               \
     }}}
 
index 7a57ec2..7394455 100644 (file)
@@ -39,7 +39,7 @@
 ///////////////////////////////////////////////////////////////////////////
 // senf::log::IOStreamTarget
 
-const char * const senf::log::IOStreamTarget::LEVELNAMES_[8] = {
+char const * const senf::log::IOStreamTarget::LEVELNAMES_[8] = {
         "NONE", "VERBOSE", "NOTICE", "MESSAGE", "IMPORTANT", "CRITICAL", "FATAL", "DISABLED" };
 
 prefix_ senf::log::IOStreamTarget::IOStreamTarget(std::ostream & os)
diff --git a/Utils/Logger/SyslogTarget.cc b/Utils/Logger/SyslogTarget.cc
new file mode 100644 (file)
index 0000000..61212ad
--- /dev/null
@@ -0,0 +1,61 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     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 SyslogTarget non-inline non-template implementation */
+
+#include "SyslogTarget.hh"
+//#include "SyslogTarget.ih"
+
+// Custom includes
+
+//#include "SyslogTarget.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+int const senf::log::SyslogTarget::LEVELMAP_[8] = {
+    0, LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_CRIT, LOG_EMERG, 0 };
+
+prefix_ void senf::log::SyslogTarget::v_write(time_type timestamp, std::string const & stream,
+                                              std::string const & area, unsigned level,
+                                              std::string const & message)
+{
+    if (area != "senf::log::DefaultArea")
+        syslog(facility_ | LEVELMAP_[level], "[%s] %s", area.c_str(), message.c_str());
+    else
+        syslog(facility_ | LEVELMAP_[level], "%s", message.c_str());
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "SyslogTarget.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:
diff --git a/Utils/Logger/SyslogTarget.cci b/Utils/Logger/SyslogTarget.cci
new file mode 100644 (file)
index 0000000..fe71ce3
--- /dev/null
@@ -0,0 +1,49 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     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 SyslogTarget inline non-template implementation */
+
+//#include "SyslogTarget.ih"
+
+// Custom includes
+
+#define prefix_ inline
+///////////////////////////////cci.p///////////////////////////////////////
+
+prefix_ senf::log::SyslogTarget::SyslogTarget(int facility)
+    : facility_ (facility)
+{}
+
+///////////////////////////////cci.e///////////////////////////////////////
+#undef prefix_
+
+\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:
diff --git a/Utils/Logger/SyslogTarget.hh b/Utils/Logger/SyslogTarget.hh
new file mode 100644 (file)
index 0000000..bb1aa6c
--- /dev/null
@@ -0,0 +1,83 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     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 SyslogTarget public header */
+
+#ifndef HH_SyslogTarget_
+#define HH_SyslogTarget_ 1
+
+// Custom includes
+#include <syslog.h>
+#include "Target.hh"
+
+//#include "SyslogTarget.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace senf {
+namespace log {
+    
+    class SyslogTarget 
+        : public Target
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        // Types
+
+
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///@{
+
+        explicit SyslogTarget(int facility = LOG_USER);
+
+        ///@}
+        ///////////////////////////////////////////////////////////////////////////
+
+    protected:
+        void v_write(time_type timestamp, std::string const & stream, 
+                     std::string const & area, unsigned level, 
+                     std::string const & message);
+
+    private:
+        int facility_;
+        static int const LEVELMAP_[8];
+    };
+
+}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+#include "SyslogTarget.cci"
+//#include "SyslogTarget.ct"
+//#include "SyslogTarget.cti"
+#endif
+
+\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:
diff --git a/Utils/Logger/SyslogTarget.test.cc b/Utils/Logger/SyslogTarget.test.cc
new file mode 100644 (file)
index 0000000..ed11e88
--- /dev/null
@@ -0,0 +1,59 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     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 SyslogTarget.test unit tests */
+
+//#include "SyslogTarget.test.hh"
+//#include "SyslogTarget.test.ih"
+
+// Custom includes
+#include "Logger.hh"
+
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/test/test_tools.hpp>
+
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+BOOST_AUTO_UNIT_TEST(syslogTarget)
+{
+    senf::log::SyslogTarget syslog;
+    
+    syslog.route();
+    BOOST_WARN_MESSAGE( false, "Check the system log for the test message" );
+    SENF_LOG(("SENF syslog test message"));
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+
+\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 d352836..eafaabf 100644 (file)
@@ -174,7 +174,7 @@ prefix_ void senf::log::Target::updateRoutingCache(detail::StreamBase const * st
         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_);
+            unsigned l (i->level_ == NONE::value ? stream->defaultRuntimeLimit() : i->level_);
             if (l < limit)
                 limit = l;
         }
@@ -194,7 +194,7 @@ prefix_ void senf::log::Target::write(time_type timestamp,
     for (; i != i_end; ++i)
         if ( (! i->stream_ || i->stream_ == &stream) &&
              (! i->area_ || i->area_ == &area) &&
-             (i->level_ == NONE::value ? i->stream_->defaultRuntimeLimit() : i->level_) <= level ) {
+             (i->level_ == NONE::value ? stream.defaultRuntimeLimit() : i->level_) <= level ) {
             if (i->action_ == ACCEPT)
                 v_write(timestamp, stream.v_name(), area.v_name(), level, message);
             return;
index ba36a95..c785497 100644 (file)
 ///////////////////////////////////////////////////////////////////////////
 // senf::log::Target
 
+prefix_ void senf::log::Target::route(action_t action, int index)
+{
+    route(0, 0, NONE::value, action, index);
+}
+
+prefix_ void senf::log::Target::unroute(action_t action)
+{
+    unroute(0, 0, NONE::value, action);
+}
+
 prefix_ senf::log::Target::iterator senf::log::Target::begin()
     const
 {
index ff8fc54..ad7e7b2 100644 (file)
@@ -58,8 +58,8 @@ namespace log {
 
     /** \brief Logging target base class
         
-        Targets are the final destination of %log messages. Every message is eventually routed to one
-        or several targets.
+        Targets are the final destination of %log messages. Every message is eventually routed to
+        one or several targets.
 
         \section target_routing Routing
 
@@ -67,12 +67,13 @@ namespace log {
         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
+        Every target manages it's own routing table. Conceptually, every 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 stream. If specified, 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
@@ -87,11 +88,13 @@ namespace log {
         \code
         target.route<foo::SomeStream, senf::log::NOTICE>(senf::log::Target::REJECT);
         target.route<foo::SomeStream>();
+        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");
+        target.route();
         \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
@@ -114,11 +117,11 @@ namespace log {
         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.
+        The target may process the message in any arbitrary way: reformat it, write it into an SQL
+        DB, whatever can be envisioned. However, there is one important limitation: The \c v_write
+        call must \e 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
       */
@@ -196,12 +199,15 @@ namespace log {
                                         /**< 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:
+                                             shown simplified here). So possible commands are:
                                              \code
-                                             target.route<SomeLevel>();
+                                             target.route();
                                              target.route<SomeStream>();
-                                             target.route<SomeStream, SomeLevel>();
+                                             target.route<SomeArea>();
+                                             target.route<SomeLevel>();
                                              target.route<SomeStream, SomeArea>();
+                                             target.route<SomeStream, SomeLevel>();
+                                             target.route<SomeArea, SomeLevel>();
                                              target.route<SomeStream, SomeArea, SomeLevel>();
                                              \endcode
 
@@ -293,6 +299,7 @@ namespace log {
 
 #       ifndef DOXYGEN
 
+        void route(action_t action = ACCEPT, int index = -1);
         template <class A1>
         void route(action_t action = ACCEPT, int index = -1);
         template <class A1, class A2>
@@ -300,6 +307,7 @@ namespace log {
         template <class A1, class A2, class A3>
         void route(action_t action = ACCEPT, int index = -1);
 
+        void unroute(action_t action = ACCEPT);
         template <class A1>
         void unroute(action_t action = ACCEPT);
         template <class A1, class A2>