Socket/Protocols/INet: Implement address input streaming
[senf.git] / Utils / Logger / Target.cc
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 non-inline non-template implementation */
25
26 #include "Target.hh"
27 #include "Target.ih"
28
29 // Custom includes
30 #include <algorithm>
31 #include <boost/format.hpp>
32 #include "ConsoleTarget.hh"
33 #include "../Console/Console.hh"
34 #include "../Console/Sysdir.hh"
35 #include "../membind.hh"
36
37 //#include "Target.mpp"
38 #define prefix_
39 ///////////////////////////////cc.p////////////////////////////////////////
40
41 ///////////////////////////////////////////////////////////////////////////
42 // senf::log::Target
43
44 namespace senf {
45 namespace log {
46
47     SENF_CONSOLE_REGISTER_ENUM_MEMBER( Target, action_t, (ACCEPT)(REJECT) );
48
49 namespace detail {
50
51     SENF_CONSOLE_REGISTER_ENUM_MEMBER( TargetRegistry, Level, 
52                                        (VERBOSE)(NOTICE)(MESSAGE)(IMPORTANT)(CRITICAL)(FATAL) );
53
54 }}}
55
56 prefix_ senf::log::Target::Target(std::string const & name)
57 {
58     namespace kw = senf::console::kw;
59
60     detail::TargetRegistry::instance().registerTarget(this, name);
61     consoleDir_().add("list", senf::membind(&Target::consoleList, this))
62         .doc("Show routing table\n"
63              "\n"
64              "Columns:\n"
65              "    #       rule index\n"
66              "    STREAM  stream to match, empty to match all streams\n"
67              "    AREA    area to match, empty to match all targets\n"
68              "    LEVEL   match messages with level above this. Log levels in increasing order\n"
69              "            are:\n"
70              "                verbose, notice, message, important, critical, fatal\n"
71              "    ACTION  action to take: accept or reject");
72     consoleDir_().add("route", 
73                       boost::function<void (std::string const &, std::string const &,
74                                             detail::TargetRegistry::Level, action_t, int)>(
75                                                 senf::membind(
76                                                     static_cast<void (Target::*)(
77                                                         std::string const &, std::string const &,
78                                                         unsigned, action_t, int)>(&Target::route),
79                                                     this)))
80         .arg("stream", 
81              "stream to match or empty to match any stream\n"
82              "              use '/sys/log/streams' to list all available streams",
83              kw::default_value="")
84         .arg("area", 
85              "area to match or empty to match any area\n"
86              "              use '/sys/log/areas' to list all available areas",
87              kw::default_value="")
88         .arg("level", "log level, one of: VERBOSE, NOTICE, MESSAGE, IMPORTANT, CRITICAL, FATAL",
89              kw::default_value=detail::TargetRegistry::VERBOSE)
90         .arg("action", "routing action, one of: ACCEPT, REJECT",
91              kw::default_value=ACCEPT)
92         .arg("index", "index at which to insert new rule",
93              kw::default_value=-1)
94         .doc("Add routing entry. Log messages are matched against the routing table beginning\n"
95              "with the first entry. The action of the first matching entry determines the\n"
96              "handling of the message.\n"
97              "\n"
98              "Examples:\n"
99              "\n"
100              "    route\n"
101              "        route all messages to this target.\n"
102              "\n"
103              "    route \"\" my::Class\n"
104              "        route all messages which are in the my::Class area.\n"
105              "\n"
106              "    route senf::log::Debug \"\" VERBOSE REJECT\n"
107              "    route \"\" \"\" VERBOSE\n"
108              "        route all messages not in the senf::log::Debug stream to the current area.\n"
109              "\n"
110              "The additional optional index argument identifies the position in the routing table\n"
111              "where the new routing entry will be added. Positive numbers count from the\n"
112              "beginning, 0 being the first routing entry. Negative values count from the end.\n");
113     consoleDir_().add("unroute",
114                       senf::membind(static_cast<void (Target::*)(int)>(&Target::unroute), this))
115         .arg("index", "index of routing entry to remove")
116         .overloadDoc("Remove routing entry with the given index");
117     consoleDir_().add("unroute", 
118                       boost::function<void (std::string const &, std::string const &,
119                                             detail::TargetRegistry::Level, action_t)>(
120                                                 senf::membind(
121                                                     static_cast<void (Target::*)(
122                                                         std::string const &, std::string const &,
123                                                         unsigned, action_t)>(&Target::unroute),
124                                                     this)))
125         .arg("stream", "stream to match or empty to match any stream",
126              kw::default_value="")
127         .arg("area", "area to match or empty to match any area",
128              kw::default_value="")
129         .arg("level", "log level, one of: VERBOSE, NOTICE, MESSAGE, IMPORTANT, CRITICAL, FATAL",
130              kw::default_value=detail::TargetRegistry::VERBOSE)
131         .arg("action", "routing action, one of: ACCEPT, REJECT",
132              kw::default_value=ACCEPT)
133         .overloadDoc("Remove the routing entry matching the specified arguments.");
134     consoleDir_().add("flush", senf::membind(&Target::flush, this))
135         .doc("Remove all routing entries clearing the routing table. This will disable all\n"
136              "logging output on this target.");
137 }
138
139 prefix_ senf::log::Target::~Target()
140 {
141     while( ! rib_.empty()) {
142         // This is slow but simplifies the area cache handling and removing a target should be
143         // relatively seldom
144         RIB::reverse_iterator i (rib_.rbegin());
145         unroute(i->stream_, i->area_, i->level_, i->action_);
146     }
147     detail::TargetRegistry::instance().unregisterTarget(this);
148 }
149
150 prefix_ void senf::log::Target::route(std::string const & stream, std::string const & area,
151                                       unsigned level, action_t action, int index)
152 {
153     detail::StreamBase const * s (0);
154     if (! stream.empty()) {
155         s = StreamRegistry::instance().lookup(stream);
156         if (!s)
157             throw InvalidStreamException();
158     }
159     detail::AreaBase const * a (0);
160     if (! area.empty()) {
161         a = AreaRegistry::instance().lookup(area);
162         if (!a)
163             throw InvalidAreaException();
164     }
165     route(s, a, level, action, index);
166 }
167
168 prefix_ void senf::log::Target::unroute(std::string const & stream, std::string const & area,
169                                         unsigned level, action_t action)
170 {
171     detail::StreamBase const * s (0);
172     if (! stream.empty()) {
173         s = StreamRegistry::instance().lookup(stream);
174         if (!s)
175             throw InvalidStreamException();
176     }
177     detail::AreaBase const * a (0);
178     if (! area.empty()) {
179         a = AreaRegistry::instance().lookup(area);
180         if (!a)
181             throw InvalidAreaException();
182     }
183     unroute(s, a, level, action);
184 }
185
186 prefix_ void senf::log::Target::unroute(int index)
187 {
188     if (rib_.empty())
189         return;
190     RIB::iterator i;
191     if (index < 0) {
192         if (RIB::size_type(-index) >= rib_.size())
193             i = rib_.begin();
194         else {
195             i = rib_.end();
196             std::advance(i, index);
197         }
198     } else {
199         if (RIB::size_type(index+1) >= rib_.size()) {
200             i = rib_.end();
201             --i;
202         } else {
203             i = rib_.begin();
204             std::advance(i, index);
205         }
206     }
207     if (i == rib_.end())
208         return;
209     RoutingEntry entry (*i);
210     rib_.erase(i);
211     if (entry.action_ == ACCEPT)
212         updateRoutingCache(entry.stream_, entry.area_);
213 }
214
215 prefix_ void senf::log::Target::flush()
216 {
217     RIB old;
218     rib_.swap(old);
219     RIB::const_iterator i (old.begin());
220     RIB::const_iterator const i_end (old.end());
221     for (; i != i_end; ++i)
222         if (i->action_ == ACCEPT)
223             updateRoutingCache(i->stream_, i->area_);
224 }
225
226 ////////////////////////////////////////
227 // protected members
228
229 prefix_ senf::log::detail::TargetRegistry::TargetRegistry()
230     : fallbackRouting_(true)
231 {
232     namespace kw = senf::console::kw;
233
234     console::sysdir().add("log", consoleDir_());
235     consoleDir_().add("areas", senf::membind(&TargetRegistry::consoleAreas, this))
236         .doc("List all areas");
237     consoleDir_().add("streams", senf::membind(&TargetRegistry::consoleStreams, this))
238         .doc("List all streams");
239     consoleDir_().add("message", senf::membind(&TargetRegistry::consoleWrite, this))
240         .arg("stream", "stream to write message to",
241              kw::default_value = "senf::log::Debug")
242         .arg("area","area to write message to",
243              kw::default_value = "senf::log::DefaultArea")
244         .arg("level", "log level, one of:\n"
245              "                  VERBOSE, NOTICE, MESSAGE, IMPORTANT, CRITICAL, FATAL",
246              kw::default_value = MESSAGE)
247         .arg("message", "message to write")
248         .doc("Write log message");
249 }
250
251 prefix_ senf::log::detail::TargetRegistry::~TargetRegistry()
252 {
253     Targets::iterator i (dynamicTargets_.begin());
254     Targets::iterator const i_end (dynamicTargets_.end());
255     for (; i != i_end; ++i)
256         delete *i;
257 }
258
259 prefix_ void senf::log::detail::TargetRegistry::consoleAreas(std::ostream & os)
260 {
261     AreaRegistry::iterator i (AreaRegistry::instance().begin());
262     AreaRegistry::iterator const i_end (AreaRegistry::instance().end());
263     for (; i != i_end; ++i)
264         os << *i << "\n";
265 }
266
267 prefix_ void senf::log::detail::TargetRegistry::consoleStreams(std::ostream & os)
268 {
269     StreamRegistry::iterator i (StreamRegistry::instance().begin());
270     StreamRegistry::iterator const i_end (StreamRegistry::instance().end());
271     for (; i != i_end; ++i)
272         os << *i << "\n";
273 }
274
275 prefix_ void senf::log::detail::TargetRegistry::consoleWrite(std::string const & stream,
276                                                              std::string const & area,
277                                                              Level level,
278                                                              std::string const & msg)
279 {
280     detail::StreamBase const * s (StreamRegistry::instance().lookup(stream));
281     if (!s)
282         throw Target::InvalidStreamException();
283     detail::AreaBase const * a (AreaRegistry::instance().lookup(area));
284     if (!a)
285         throw Target::InvalidAreaException();
286     write(*s, *a, level, msg);
287 }
288
289 prefix_ senf::console::ScopedDirectory<> & senf::log::Target::consoleDir()
290 {
291     return consoleDir_();
292 }
293
294 ////////////////////////////////////////
295 // private members
296
297 prefix_ void senf::log::Target::route(detail::StreamBase const * stream,
298                                       detail::AreaBase const * area, unsigned level,
299                                       action_t action, int index)
300 {
301     RIB::iterator i;
302     if (index < 0) {
303         if (RIB::size_type(-index-1) >= rib_.size())
304             i = rib_.begin();
305         else {
306             i = rib_.end();
307             std::advance(i, index + 1 );
308         }
309     } else {
310         if (RIB::size_type(index) >= rib_.size())
311             i = rib_.end();
312         else {
313             i = rib_.begin();
314             std::advance(i, index);
315         }
316     }
317     rib_.insert(i, RoutingEntry(stream, area, level, action));
318     if (action == ACCEPT)
319         updateRoutingCache(stream, area);
320     // This disables the initial fallback routing
321     detail::TargetRegistry::instance().routed();
322 }
323
324 prefix_ void senf::log::Target::unroute(detail::StreamBase const * stream,
325                                         detail::AreaBase const * area, unsigned level,
326                                         action_t action)
327 {
328     RIB::iterator i = std::find(rib_.begin(), rib_.end(),
329                                 RoutingEntry(stream, area, level, action));
330     if (i != rib_.end())
331         unroute(std::distance(rib_.begin(), i));
332 }
333
334 prefix_ void senf::log::Target::updateRoutingCache(detail::StreamBase const * stream,
335                                                    detail::AreaBase const * area)
336 {
337     if (! stream) {
338         StreamRegistry::Registry::iterator i (StreamRegistry::instance().registry_.begin());
339         StreamRegistry::Registry::iterator const i_end (StreamRegistry::instance().registry_.end());
340         for (; i != i_end ; ++i)
341             updateRoutingCache(i->second, area);
342         return;
343     }
344     if (! area) {
345         AreaRegistry::Registry::iterator i (AreaRegistry::instance().registry_.begin());
346         AreaRegistry::Registry::iterator const i_end (AreaRegistry::instance().registry_.end());
347         for (; i != i_end ; ++i)
348             updateRoutingCache(stream, i->second);
349         return;
350     }
351     unsigned limit (DISABLED::value);
352     RIB::iterator i (rib_.begin());
353     RIB::iterator const i_end (rib_.end());
354     for(; i != i_end; ++i)
355         if ( (! i->stream_ || i->stream_ == stream) &&
356              (! i->area_ || i->area_ == area) &&
357              i->action_ == ACCEPT ) {
358             unsigned l (i->level_ == NONE::value ? stream->defaultRuntimeLimit() : i->level_);
359             if (l < limit)
360                 limit = l;
361         }
362     if (limit == DISABLED::value)
363         area->removeRoutingCache(*this, *stream);
364     else
365         area->updateRoutingCache(*this, *stream, limit);
366 }
367
368 prefix_ void senf::log::Target::write(time_type timestamp,
369                                       detail::StreamBase const & stream,
370                                       detail::AreaBase const & area, unsigned level,
371                                       std::string const & message)
372 {
373     RIB::iterator i (rib_.begin());
374     RIB::iterator const i_end (rib_.end());
375     for (; i != i_end; ++i)
376         if ( (! i->stream_ || i->stream_ == &stream) &&
377              (! i->area_ || i->area_ == &area) &&
378              (i->level_ == NONE::value ? stream.defaultRuntimeLimit() : i->level_) <= level ) {
379             if (i->action_ == ACCEPT)
380                 v_write(timestamp, stream.v_name(), area.v_name(), level, message);
381             return;
382         }
383 }
384
385 namespace {
386     std::string formatLabel(std::string const & l)
387     {
388         if (l.empty())
389             return "-";
390         if (l.size() > 29)
391             return l.substr(l.size()-29);
392         return l;
393     }
394 }
395
396 prefix_ void senf::log::Target::consoleList(std::ostream & os)
397 {
398     static char const * levels[] = { 
399         "none", "verbose", "notice", "message", "important", "critical", "fatal", "disabled" };
400
401     boost::format fmt ("%2d %-29s %-29s %-9s %-6s\n");
402     os << fmt % "#" % "STREAM" % "AREA" % "LEVEL" % "ACTION";
403     unsigned n (0);
404     for (iterator i (begin()); i != end(); ++i, ++n)
405         os << fmt 
406             % n 
407             % formatLabel(i->stream())
408             % formatLabel(i->area())
409             % levels[i->level()]
410             % (i->action() == ACCEPT ? "accept" : "reject");
411 }
412
413 ///////////////////////////////////////////////////////////////////////////
414 // senf::log::detail::TargetRegistry
415
416 prefix_ void senf::log::detail::TargetRegistry::dynamicTarget(std::auto_ptr<Target> target)
417 {
418     target->consoleDir().add("remove", boost::function<void ()>(
419                                  boost::bind(
420                                      &TargetRegistry::consoleRemoveTarget, this, target.get())))
421         .doc("Remove target.");
422     dynamicTargets_.insert(target.release());
423 }
424
425 prefix_ void senf::log::detail::TargetRegistry::registerTarget(Target * target,
426                                                                std::string const & name)
427 {
428     targets_.insert(target);
429     consoleDir_().add(name, target->consoleDir_());
430 }
431
432 prefix_ void senf::log::detail::TargetRegistry::write(StreamBase const & stream,
433                                                       AreaBase const & area, unsigned level,
434                                                       std::string const & msg)
435 {
436     if (fallbackRouting_) {
437         if (level >= stream.defaultRuntimeLimit())
438             static_cast<Target &>(ConsoleTarget::instance()).v_write(
439                 TimeSource::now(), stream.v_name(), area.v_name(), level, msg );
440     }
441     else
442         area.write( TimeSource::now(), stream, level, msg );
443 }
444
445 prefix_ void senf::log::detail::TargetRegistry::consoleRemoveTarget(Target * target)
446 {
447     dynamicTargets_.erase(target);
448     delete target;
449 }
450
451 ///////////////////////////////////////////////////////////////////////////
452 // namespace members
453
454 prefix_ std::ostream & senf::log::operator<<(std::ostream & os, senf::log::Target::action_t const & action)
455 {
456     if( action == Target::ACCEPT) os << "ACCEPT";
457     else if( action == Target::REJECT) os << "REJECT";
458     else os << "unknown action";
459     return os;
460 }
461
462 //////////////////////////////////////////////////////////////////////////
463 // I need to put this here, otherwise the file target will not be registered
464 // if it is not used ... :-(
465
466 senf::log::FileTarget::RegisterConsole senf::log::FileTarget::RegisterConsole::instance;
467 senf::log::SyslogTarget::RegisterConsole senf::log::SyslogTarget::RegisterConsole::instance;
468 senf::log::SyslogUDPTarget::RegisterConsole senf::log::SyslogUDPTarget::RegisterConsole::instance;
469
470 ///////////////////////////////cc.e////////////////////////////////////////
471 #undef prefix_
472 //#include "Target.mpp"
473
474
475 // Local Variables:
476 // mode: c++
477 // fill-column: 100
478 // comment-column: 40
479 // c-file-style: "senf"
480 // indent-tabs-mode: nil
481 // ispell-local-dictionary: "american"
482 // compile-command: "scons -u test"
483 // End: