Utils: Removed ErrnoException and implemented generic Exception base-class
[senf.git] / Utils / Logger / Target.hh
1 // $Id$
2 //
3 // Copyright (C) 2007
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief Target public header */
25
26 #ifndef HH_Target_
27 #define HH_Target_ 1
28
29 // Custom includes
30 #include <set>
31 #include <boost/date_time/posix_time/posix_time.hpp>
32 #include <boost/utility.hpp>
33 #include <boost/type_traits/is_convertible.hpp>
34 #include "../singleton.hh"
35 #include "../mpl.hh"
36 #include "StreamRegistry.hh"
37 #include "AreaRegistry.hh"
38 #include "../Exception.hh"
39
40 //#include "Target.mpp"
41 ///////////////////////////////hh.p////////////////////////////////////////
42
43 /** \defgroup targets Targets
44
45     Targets receive log messages and write them to some destination: The console, a log file, an SQL
46     DB and so on. Every target is derived from the \ref senf::log::Target base class. This base
47     class provides the target with the necessary routing infrastructure. The different targets only
48     differ in the way, they write the data.
49
50     \see senf::log::Target
51  */
52
53 namespace senf {
54 namespace log {
55     
56     namespace detail { class TargetRegistry; }
57
58     /** \brief Logging target base class
59         
60         Targets are the final destination of %log messages. Every message is eventually routed to one
61         or several targets.
62
63         \section target_routing Routing
64
65         Each target manages a routing table. The message meta-data (stream, area and level) is
66         matched against this table. If an entry matches, the action associated with this entry is
67         taken (either \c ACCEPT or \c REJECT).
68
69         Every target manages it's own routing table. Conceptually, every routing message will be
70         routed to every target where it will then be matched against each targets routing table (the
71         implementation is more efficient and utilizes a routing cache).
72
73         Each routing entry consists of the following parameters
74         \li (mandatory) \e stream. The entry will match only messages directed at that stream
75         \li (optional) \e area. If the area is specified, only messages directed at that area are
76             matched, otherwise any area will be allowed
77         \li (optional) \e level. If the log level is specified, messages will be accepted if their
78             level is at least that value. If the value is not specified, the limit will be taken
79             from the stream's default value.
80
81         Each parameter (stream, area and level) has two representations: A static (compile time
82         constant) representation, which is the representation also used in the log statements, and a
83         dynamic representation, which may be used for manipulating the routing table.
84
85         The static representation is used, when passing routing parameters via template arguments:
86         \code
87         target.route<foo::SomeStream, senf::log::NOTICE>(senf::log::Target::REJECT);
88         target.route<foo::SomeStream>();
89         \endcode
90         The identical routing statements may be expressed using dynamic routing via:
91         \code
92         target.route("foo::SomeStream", "", senf::log::NOTICE::value, senf::log::Target::REJECT);
93         target.route("foo::SomeStream");
94         \endcode
95         The static representation has the benefit of being compile-time type checked: Invalid
96         routing parameters will be caught while compiling the code. The dynamic representation is
97         more flexible as it allows to adjust routing from user input (e.g. configuration files).
98
99         The different object representations are:
100         \li The \e streams is statically represented by it's name, which is the name of a class
101             defined with \ref SENF_LOG_DEF_STREAM. The dynamic representation is a string
102             representation of this name.
103         \li The \e area is statically represented by it's name, which again is the name of a class
104             defined with \ref SENF_LOG_DEF_STREAM. The dynamic representation again is a string
105             representation of this class's name. The dynamic representation represents an absent
106             area with the empty string.
107         \li The \e level is statically represented by a level class from \ref
108             loglevels. Dynamically, it is represented by an unsigned integer number, the \c value
109             member of that class.
110
111         \section target_impl Implementing new targets
112
113         To implement a new target type, you need to derive from senf::log::Target and implement the
114         single \c v_write member. This member will be called whenever a message should be output. 
115
116         The target may process in any arbitrary way: reformat, writing it into an SQL DB, whatever
117         can be envisioned. However, there is one important limitation: The \c v_write call must not
118         block. So for more complex scenarios, additional measures must be taken (e.g. writing a %log
119         backend daemon which receives the messages via UDP and processes them). Of course, in rare
120         cases messages might be lost but this cannot be avoided.
121
122         \see \ref targets
123       */
124     class Target : private boost::noncopyable
125     {
126     public:
127         ///////////////////////////////////////////////////////////////////////////
128         // Types
129
130         /** \brief Routing action
131
132             Every routing entry is associated with a routing action. This action is final (for this
133             target. Each target is processed independently).
134          */
135         enum action_t { 
136             ACCEPT /** Output message */
137           , REJECT /** Suppress message output */
138         };
139
140         /** \brief Target routing entry
141
142             A single routing entry matches messages against their \e stream, \e area and \e
143             level. If the entry matches, the given \e action is performed.
144
145             \see senf::log::Target
146          */
147         struct RoutingEntry 
148         {
149             std::string stream() const; ///< Stream to match
150             std::string area() const;   ///< Area to match (empty of unspecified)
151             unsigned level() const;     ///< Level to match (senf::log::NONE::value if unspecified)
152             action_t action() const;    ///< Action to take
153             
154 #           ifdef DOXYGEN
155         private:
156 #           endif
157
158             RoutingEntry();
159             bool operator==(RoutingEntry const & other);
160
161         private:
162             RoutingEntry(detail::StreamBase const * stream, detail::AreaBase const * area, 
163                          unsigned level, action_t action);
164
165             detail::StreamBase const * stream_;
166             detail::AreaBase const * area_;
167             unsigned level_;
168             action_t action_;
169             
170             friend class Target;
171         };
172
173     private:
174         typedef std::vector<RoutingEntry> RIB;
175
176     public:
177         typedef RIB::const_iterator iterator; ///< Routing table iterator
178
179         ///////////////////////////////////////////////////////////////////////////
180         ///\name Structors and default members
181         ///@{
182
183         Target();
184         virtual ~Target();
185
186         ///@}
187         ///////////////////////////////////////////////////////////////////////////
188         ///\name Routing
189         ///\{
190
191 #       ifdef DOXYGEN
192
193         template <class Stream, class Area, class Level> void route(
194             action_t action = ACCEPT, int index = -1); ///< Add route (static)
195                                         /**< Add a route for the given combination of \a Stream, \a
196                                              Area and \a Level. The \a Stream parameter is mandatory
197                                              while either \a Area or \a Level are optional (the
198                                              template signature is shown simplified here):
199                                              \code
200                                              target.route<SomeStream>();
201                                              target.route<SomeStream, SomeLevel>();
202                                              target.route<SomeStream, SomeArea>();
203                                              target.route<SomeStream, SomeArea, SomeLevel>();
204                                              \endcode
205
206                                              See the class description for information on the \a
207                                              action and \a index parameters 
208
209                                              \tparam Stream mandatory stream to match
210                                              \tparam Area optional area to match
211                                              \tparam Level optional level, matches messages with
212                                                  at least the given level. 
213                                              \param[in] action routing action to take
214                                              \param[in] index position of new route in the routing
215                                                  table */
216
217 #       endif
218
219         void route(std::string const & stream, std::string const & area = "", 
220                    unsigned level = NONE::value, action_t action = ACCEPT, int index = -1);
221                                         ///< Add route (dynamic)
222                                         /**< Add a route for the given combination of \a stream, \a
223                                              area and \a level. The \a stream parameter is mandatory
224                                              while either \a area or \a level may be left
225                                              unspecified by setting them to the empty string or
226                                              senf::log::NONE::value respectively.
227
228                                              See the class description for information on the \a
229                                              action and \a index parameters 
230
231                                              \throws InvalidStreamException if the given \a stream
232                                                  is not found in the StreamRegistry
233                                              \throws InvalidAreaException if the given \a area is
234                                                  not found in the AreaRegistry
235
236                                              \param[in] stream mandatory stream to match
237                                              \param[in] area optional area to match
238                                              \param[in] level optional level, matches messages with
239                                                  at least the given level.
240                                              \param[in] action routing action to take
241                                              \param[in] index position of new route in the routing
242                                                  table */
243
244 #       ifdef DOXYGEN
245
246         template <class Stream, class Area, class Level> 
247         void unroute(action_t action = ACCEPT);
248                                         ///< Remove route (static)
249                                         /**< This member removes an arbitrary routing entry. The
250                                              template parameters are the same as for the
251                                              corresponding \ref route() call.
252
253                                              The routing table is searched for a route exactly
254                                              matching the given specification. If such a route is
255                                              found, it will be removed, otherwise the call will be
256                                              ignored
257
258                                              \tparam Stream mandatory stream to match
259                                              \tparam Area optional area to match
260                                              \tparam Level optional level, matches messages with
261                                                  at least the given level. 
262                                              \param[in] action routing action to take */
263
264 #       endif
265
266         void unroute(std::string const & stream, std::string const & area = "", 
267                      unsigned level = NONE::value, action_t action = ACCEPT);
268                                         ///< Remove route (dynamic)
269                                         /**< This member removes an arbitrary routing entry. The \a
270                                              stream parameter is mandatory while either \a area or
271                                              \a level may be left unspecified by setting them to the
272                                              empty string or senf::log::NONE::value respectively.
273
274                                              The routing table is searched for a route exactly
275                                              matching the given specification. If such a route is
276                                              found, it will be removed, otherwise the call will be
277                                              ignored
278
279                                              \param[in] stream mandatory stream to match
280                                              \param[in] area optional area to match
281                                              \param[in] level optional level, matches messages with
282                                                  at least the given level.
283                                              \param[in] action routing action to take */
284         void unroute(int index=-1);     ///< Remove route (indexed)
285                                         /**< This call will remove the route with the given index.
286                                              
287                                              See the class documentation for more information on
288                                              indexing. 
289
290                                              \param[in] index index of routing entry to remove */
291
292 #       ifndef DOXYGEN
293
294         template <class Stream> void route(
295             action_t action = ACCEPT, int index = -1);
296         template <class Stream, class Level> void route(
297             action_t action = ACCEPT, int index = -1,
298             typename boost::enable_if< boost::is_convertible<Level*,
299                                                              detail::LevelBase *> >::type * = 0);
300         template <class Stream, class Area> void route(
301             action_t action = ACCEPT, int index = -1,
302             typename boost::enable_if< boost::is_convertible<Area*,
303                                                              detail::AreaBase *> >::type * = 0);
304         template <class Stream, class AreaClass> void route(
305             action_t action = ACCEPT, int index = -1,
306             typename boost::enable_if< boost::is_convertible<typename AreaClass::SENFLogArea *,
307                                                              detail::AreaBase *> >::type * = 0);
308         template <class Stream, class Area, class Level> void route(
309             action_t action = ACCEPT, int index = -1,
310             typename boost::enable_if< boost::is_convertible<Area *,
311                                                              detail::AreaBase *> >::type * = 0);
312         template <class Stream, class AreaClass, class Level> void route(
313             action_t action = ACCEPT, int index = -1,
314             typename boost::enable_if< boost::is_convertible<typename AreaClass::SENFLogArea *,
315                                                              detail::AreaBase *> >::type * = 0);
316
317         template <class Stream> void unroute(
318             action_t action = ACCEPT);
319         template <class Stream, class Level> void unroute(
320             action_t action = ACCEPT,
321             typename boost::enable_if< boost::is_convertible<Level*,
322                                                              detail::LevelBase *> >::type * = 0);
323         template <class Stream, class Area> void unroute(
324             action_t action = ACCEPT,
325             typename boost::enable_if< boost::is_convertible<Area*,
326                                                              detail::AreaBase *> >::type * = 0);
327         template <class Stream, class AreaClass> void unroute(
328             action_t action = ACCEPT,
329             typename boost::enable_if< boost::is_convertible<typename AreaClass::SENFLogArea *,
330                                                              detail::AreaBase *> >::type * = 0);
331         template <class Stream, class Area, class Level> void unroute(
332             action_t action = ACCEPT,
333             typename boost::enable_if< boost::is_convertible<Area*,
334                                                              detail::AreaBase *> >::type * = 0);
335         template <class Stream, class AreaClass, class Level> void unroute(
336             action_t action = ACCEPT,
337             typename boost::enable_if< boost::is_convertible<typename AreaClass::SENFLogArea *,
338                                                              detail::AreaBase *> >::type * = 0);
339
340 #       endif
341
342         ///\}
343
344         /** \brief Exception: Invalid stream */
345         struct InvalidStreamException : public senf::Exception
346         { InvalidStreamException() 
347               : senf::Exception("senf::log::Target::InvalidStreamException"){} };
348         
349         /** \brief Exception: Invalid area */
350         struct InvalidAreaException : public senf::Exception
351         { InvalidAreaException() 
352               : senf::Exception("senf::log::Target::InvalidAreaException"){} };
353
354         iterator begin() const;         ///< Iterator to beginning of routing table
355         iterator end() const;           ///< Iterator past the end of routing table
356         
357     private:
358         void route(detail::StreamBase const * stream, detail::AreaBase const * area, 
359                    unsigned level, action_t action, int index);
360         void unroute(detail::StreamBase const * stream, detail::AreaBase const * area, 
361                      unsigned level, action_t action);
362
363         void updateRoutingCache(detail::StreamBase const * stream, detail::AreaBase const * area);
364
365         void write(boost::posix_time::ptime timestamp, detail::StreamBase const & stream,
366                    detail::AreaBase const & area, unsigned level, std::string const & message);
367
368 #   ifdef DOXYGEN
369     protected:
370 #   endif
371
372         virtual void v_write(boost::posix_time::ptime timestamp, std::string const & stream, 
373                              std::string const & area, unsigned level, 
374                              std::string const & message) = 0;
375                                         ///< Called to write out the routing message
376                                         /**< This member must be defined in the derived class to
377                                              somehow format and write the %log message. 
378
379                                              Every %log message always possesses a complete set of
380                                              meta information (\a stream, \a area and \a level).
381
382                                              \note This member must \e not block since it may be
383                                                  called from any unknown context. This prohibits
384                                                  simple logging over NFS or many other network
385                                                  protocols.
386
387                                              \param[in] timestamp %log message timing information
388                                              \param[in] stream message stream
389                                              \param[in] area message area
390                                              \param[in] level message level
391                                              \param[in] message the message string */
392
393 #   ifdef DOXYGEN
394     private:
395 #   endif
396
397         RIB rib_;
398         
399         friend class detail::AreaBase;
400         friend class detail::TargetRegistry;
401     };
402
403     /** \brief Log message time source abstract base class
404
405         Instances derived from TimeSource provide the Logging library with the current date/time
406         value. The \c operator() member must be implemented to return the current universal time
407         (UTC).
408
409         A new TimeSource may be installed using \ref senf::log::timeSource().
410
411         \ingroup config
412      */
413     struct TimeSource
414     {
415         virtual ~TimeSource();
416         virtual boost::posix_time::ptime operator()() const = 0;
417     };
418
419     /** \brief Default %log message time source
420
421         This time source is installed by default and uses gettimeofday() (via the Boost.DateTime
422         library) to get the current universal time.
423         
424         \ingroup config
425      */
426     struct SystemTimeSource : public TimeSource
427     {
428         virtual boost::posix_time::ptime operator()() const;
429     };
430
431     /** \brief Change log message time source
432
433         Set the log message time source to \a source. The logging library will take ownership of \e
434         source and will take care to free it, if necessary.
435
436         Since the time source class will in almost all cases be default constructible, see the
437         template overload for a simpler interface.
438
439         \ingroup config
440      */
441     void timeSource(std::auto_ptr<TimeSource> source);
442
443     /** \brief Change log message time source
444
445         Set the log message time source to (an instance of) \a Source.  \a Source must be default
446         constructible, otherwise use the non-template senf::log::timeSource() overload.
447
448         \ingroup config
449      */
450     template <class Source> void timeSource();
451
452 }}
453
454 ///////////////////////////////hh.e////////////////////////////////////////
455 #include "Target.cci"
456 //#include "Target.ct"
457 #include "Target.cti"
458 #endif
459
460 \f
461 // Local Variables:
462 // mode: c++
463 // fill-column: 100
464 // comment-column: 40
465 // c-file-style: "senf"
466 // indent-tabs-mode: nil
467 // ispell-local-dictionary: "american"
468 // compile-command: "scons -u test"
469 // End: