Utils/Console: Reset GenericNode::parent_ when destroying parent
[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", senf::membind(&Target::consoleRoute, this))
73         .arg("index", "index at which to insert new rule")
74         .arg("parameters", "log parameters. The log parameters select the log stream, log area\n"
75              "              and log level. You may specify any combination of these parameterse\n"
76              "              in any order. Use the '/sys/log/stream' and '/sys/log/areas' commands\n"
77              "              to list all valid streams and areas. Valid log levels are:\n"
78              "                  VERBOSE NOTICE MESSAGE IMPORTANT CRITICAL FATAL")
79         .arg("action", "routing action, one of: ACCEPT, REJECT",
80              kw::default_value=ACCEPT)
81         .doc("Add routing entry. Log messages are matched against the routing table beginning\n"
82              "with the first entry. The action of the first matching entry determines the\n"
83              "handling of the message.\n"
84              "\n"
85              "Examples:\n"
86              "\n"
87              "    route ()\n"
88              "        route all messages to this target.\n"
89              "\n"
90              "    route 1 (my::Class)\n"
91              "        route all messages which are in the my::Class area. Insert this route after\n"
92              "        the first route,\n"
93              "\n"
94              "    route (senf::log::Debug VERBOSE) REJECT\n"
95              "    route (VERBOSE)\n"
96              "        route all messages not in the senf::log::Debug stream to the current area.\n"
97              "\n"
98              "The additional optional index argument identifies the position in the routing table\n"
99              "where the new routing entry will be added. Positive numbers count from the\n"
100              "beginning, 0 being the first routing entry. Negative values count from the end.");
101     consoleDir_().add("route", boost::function<void (detail::LogParameters, action_t)>(
102                           boost::bind(&Target::consoleRoute, this, -1, _1, _2)))
103         .arg("parameters")
104         .arg("action", kw::default_value=ACCEPT);
105     consoleDir_().add("unroute",
106                       senf::membind(static_cast<void (Target::*)(int)>(&Target::unroute), this))
107         .arg("index", "index of routing entry to remove")
108         .overloadDoc("Remove routing entry with the given index");
109     consoleDir_().add("unroute", senf::membind(&Target::consoleUnroute, this))
110         .arg("parameters", "log parameters. The log parameters select the log stream, log area\n"
111              "              and log level. You may specify any combination of these parameterse\n"
112              "              in any order. Use the '/sys/log/stream' and '/sys/log/areas' commands\n"
113              "              to list all valid streams and areas. Valid log levels are:\n"
114              "                  VERBOSE NOTICE MESSAGE IMPORTANT CRITICAL FATAL")
115         .arg("action", "routing action, one of: ACCEPT, REJECT",
116              kw::default_value=ACCEPT)
117         .overloadDoc("Remove the routing entry matching the specified arguments.");
118     consoleDir_().add("flush", senf::membind(&Target::flush, this))
119         .doc("Remove all routing entries clearing the routing table. This will disable all\n"
120              "logging output on this target.");
121 }
122
123 prefix_ senf::log::Target::~Target()
124 {
125     while( ! rib_.empty()) {
126         // This is slow but simplifies the area cache handling and removing a target should be
127         // relatively seldom
128         RIB::reverse_iterator i (rib_.rbegin());
129         unroute(i->stream_, i->area_, i->level_, i->action_);
130     }
131     detail::TargetRegistry::instance().unregisterTarget(this);
132 }
133
134 prefix_ void senf::log::Target::route(std::string const & stream, std::string const & area,
135                                       unsigned level, action_t action, int index)
136 {
137     detail::StreamBase const * s (0);
138     if (! stream.empty()) {
139         s = StreamRegistry::instance().lookup(stream);
140         if (!s)
141             throw InvalidStreamException();
142     }
143     detail::AreaBase const * a (0);
144     if (! area.empty()) {
145         a = AreaRegistry::instance().lookup(area);
146         if (!a)
147             throw InvalidAreaException();
148     }
149     route(s, a, level, action, index);
150 }
151
152 prefix_ void senf::log::Target::unroute(std::string const & stream, std::string const & area,
153                                         unsigned level, action_t action)
154 {
155     detail::StreamBase const * s (0);
156     if (! stream.empty()) {
157         s = StreamRegistry::instance().lookup(stream);
158         if (!s)
159             throw InvalidStreamException();
160     }
161     detail::AreaBase const * a (0);
162     if (! area.empty()) {
163         a = AreaRegistry::instance().lookup(area);
164         if (!a)
165             throw InvalidAreaException();
166     }
167     unroute(s, a, level, action);
168 }
169
170 prefix_ void senf::log::Target::unroute(int index)
171 {
172     if (rib_.empty())
173         return;
174     RIB::iterator i;
175     if (index < 0) {
176         if (RIB::size_type(-index) >= rib_.size())
177             i = rib_.begin();
178         else {
179             i = rib_.end();
180             std::advance(i, index);
181         }
182     } else {
183         if (RIB::size_type(index+1) >= rib_.size()) {
184             i = rib_.end();
185             --i;
186         } else {
187             i = rib_.begin();
188             std::advance(i, index);
189         }
190     }
191     if (i == rib_.end())
192         return;
193     RoutingEntry entry (*i);
194     rib_.erase(i);
195     if (entry.action_ == ACCEPT)
196         updateRoutingCache(entry.stream_, entry.area_);
197 }
198
199 prefix_ void senf::log::Target::flush()
200 {
201     RIB old;
202     rib_.swap(old);
203     RIB::const_iterator i (old.begin());
204     RIB::const_iterator const i_end (old.end());
205     for (; i != i_end; ++i)
206         if (i->action_ == ACCEPT)
207             updateRoutingCache(i->stream_, i->area_);
208 }
209
210 ////////////////////////////////////////
211 // protected members
212
213 prefix_ senf::console::ScopedDirectory<> & senf::log::Target::consoleDir()
214 {
215     return consoleDir_();
216 }
217
218 ////////////////////////////////////////
219 // private members
220
221 prefix_ void senf::log::Target::route(detail::StreamBase const * stream,
222                                       detail::AreaBase const * area, unsigned level,
223                                       action_t action, int index)
224 {
225     RIB::iterator i;
226     if (index < 0) {
227         if (RIB::size_type(-index-1) >= rib_.size())
228             i = rib_.begin();
229         else {
230             i = rib_.end();
231             std::advance(i, index + 1 );
232         }
233     } else {
234         if (RIB::size_type(index) >= rib_.size())
235             i = rib_.end();
236         else {
237             i = rib_.begin();
238             std::advance(i, index);
239         }
240     }
241     rib_.insert(i, RoutingEntry(stream, area, level, action));
242     if (action == ACCEPT)
243         updateRoutingCache(stream, area);
244     // This disables the initial fallback routing
245     detail::TargetRegistry::instance().routed();
246 }
247
248 prefix_ void senf::log::Target::unroute(detail::StreamBase const * stream,
249                                         detail::AreaBase const * area, unsigned level,
250                                         action_t action)
251 {
252     RIB::iterator i = std::find(rib_.begin(), rib_.end(),
253                                 RoutingEntry(stream, area, level, action));
254     if (i != rib_.end())
255         unroute(std::distance(rib_.begin(), i));
256 }
257
258 prefix_ void senf::log::Target::updateRoutingCache(detail::StreamBase const * stream,
259                                                    detail::AreaBase const * area)
260 {
261     if (! stream) {
262         StreamRegistry::Registry::iterator i (StreamRegistry::instance().registry_.begin());
263         StreamRegistry::Registry::iterator const i_end (StreamRegistry::instance().registry_.end());
264         for (; i != i_end ; ++i)
265             updateRoutingCache(i->second, area);
266         return;
267     }
268     if (! area) {
269         AreaRegistry::Registry::iterator i (AreaRegistry::instance().registry_.begin());
270         AreaRegistry::Registry::iterator const i_end (AreaRegistry::instance().registry_.end());
271         for (; i != i_end ; ++i)
272             updateRoutingCache(stream, i->second);
273         return;
274     }
275     unsigned limit (DISABLED::value);
276     RIB::iterator i (rib_.begin());
277     RIB::iterator const i_end (rib_.end());
278     for(; i != i_end; ++i)
279         if ( (! i->stream_ || i->stream_ == stream) &&
280              (! i->area_ || i->area_ == area) &&
281              i->action_ == ACCEPT ) {
282             unsigned l (i->level_ == NONE::value ? stream->defaultRuntimeLimit() : i->level_);
283             if (l < limit)
284                 limit = l;
285         }
286     if (limit == DISABLED::value)
287         area->removeRoutingCache(*this, *stream);
288     else
289         area->updateRoutingCache(*this, *stream, limit);
290 }
291
292 prefix_ void senf::log::Target::write(time_type timestamp,
293                                       detail::StreamBase const & stream,
294                                       detail::AreaBase const & area, unsigned level,
295                                       std::string const & message)
296 {
297     RIB::iterator i (rib_.begin());
298     RIB::iterator const i_end (rib_.end());
299     for (; i != i_end; ++i)
300         if ( (! i->stream_ || i->stream_ == &stream) &&
301              (! i->area_ || i->area_ == &area) &&
302              (i->level_ == NONE::value ? stream.defaultRuntimeLimit() : i->level_) <= level ) {
303             if (i->action_ == ACCEPT)
304                 v_write(timestamp, stream.v_name(), area.v_name(), level, message);
305             return;
306         }
307 }
308
309 namespace {
310     std::string formatLabel(std::string const & l)
311     {
312         if (l.empty())
313             return "-";
314         if (l.size() > 29)
315             return l.substr(l.size()-29);
316         return l;
317     }
318 }
319
320 prefix_ void senf::log::Target::consoleList(std::ostream & os)
321 {
322     static char const * levels[] = { 
323         "none", "verbose", "notice", "message", "important", "critical", "fatal", "disabled" };
324
325     boost::format fmt ("%2d %-29s %-29s %-9s %-6s\n");
326     os << fmt % "#" % "STREAM" % "AREA" % "LEVEL" % "ACTION";
327     unsigned n (0);
328     for (iterator i (begin()); i != end(); ++i, ++n)
329         os << fmt 
330             % n 
331             % formatLabel(i->stream())
332             % formatLabel(i->area())
333             % levels[i->level()]
334             % (i->action() == ACCEPT ? "accept" : "reject");
335 }
336
337 prefix_ void senf::log::Target::consoleRoute(int index, detail::LogParameters const & pm, action_t action)
338 {
339     route(pm.stream, pm.area, pm.level, action, index);
340 }
341
342 prefix_ void senf::log::Target::consoleUnroute(detail::LogParameters const & pm, action_t action)
343 {
344     unroute(pm.stream, pm.area, pm.level, action);
345 }
346
347 ///////////////////////////////////////////////////////////////////////////
348 // senf::log::detail::TargetRegistry
349
350 prefix_ void senf::log::detail::TargetRegistry::dynamicTarget(std::auto_ptr<Target> target)
351 {
352     target->consoleDir().add("remove", boost::function<void ()>(
353                                  boost::bind(
354                                      &TargetRegistry::consoleRemoveTarget, this, target.get())))
355         .doc("Remove target.");
356     dynamicTargets_.insert(target.release());
357 }
358
359 prefix_ void senf::log::detail::TargetRegistry::registerTarget(Target * target,
360                                                                std::string const & name)
361 {
362     targets_.insert(target);
363     consoleDir_().add(name, target->consoleDir_());
364 }
365
366 prefix_ void senf::log::detail::TargetRegistry::write(StreamBase const & stream,
367                                                       AreaBase const & area, unsigned level,
368                                                       std::string const & msg)
369 {
370     if (fallbackRouting_) {
371         if (level >= stream.defaultRuntimeLimit())
372             static_cast<Target &>(ConsoleTarget::instance()).v_write(
373                 TimeSource::now(), stream.v_name(), area.v_name(), level, msg );
374     }
375     else
376         area.write( TimeSource::now(), stream, level, msg );
377 }
378
379 prefix_ void senf::log::detail::TargetRegistry::consoleRemoveTarget(Target * target)
380 {
381     dynamicTargets_.erase(target);
382     delete target;
383 }
384
385 prefix_ senf::log::detail::TargetRegistry::TargetRegistry()
386     : fallbackRouting_(true)
387 {
388     namespace kw = senf::console::kw;
389
390     console::sysdir().add("log", consoleDir_());
391     consoleDir_().add("areas", senf::membind(&TargetRegistry::consoleAreas, this))
392         .doc("List all areas");
393     consoleDir_().add("streams", senf::membind(&TargetRegistry::consoleStreams, this))
394         .doc("List all streams");
395     consoleDir_().add("message", senf::membind(&TargetRegistry::consoleWrite, this))
396         .arg("parameters", "log parameters. The log parameters select the log stream, log area\n"
397              "              and log level. You may specify any combination of these parameterse\n"
398              "              in any order. Use the '/sys/log/stream' and '/sys/log/areas' commands\n"
399              "              to list all valid streams and areas. Valid log levels are:\n"
400              "                  VERBOSE NOTICE MESSAGE IMPORTANT CRITICAL FATAL",
401              kw::default_value = LogParameters::defaultParameters())
402         .arg("message", "message to write")
403         .doc("Write log message.\n"
404              "\n"
405              "Examples:\n"
406              "    message \"Test\";\n"
407              "    message (senf::log::DefaultArea NOTICE) \"Test notice\";\n"
408              "    message (FATAL) \"Program on fire\";\n"
409              "    message (VERBOSE senf::log::Debug) \"Debug message\";");
410     consoleDir_().add("self", senf::membind(&TargetRegistry::consoleSelf, this))
411         .doc("Get the log directory of the current network client. Example usage:\n"
412              "\n"
413              "Just get the log config directory\n"
414              "    $ /sys/log/self\n"
415              "    <Directory '/sys/log/client-xxx.xxx.xxx.xxx:xxx'>\n"
416              "\n"
417              "Route all messages to the currently connected client\n"
418              "    $ /sys/log/self { route (); }");
419 }
420
421 prefix_ senf::log::detail::TargetRegistry::~TargetRegistry()
422 {
423     Targets::iterator i (dynamicTargets_.begin());
424     Targets::iterator const i_end (dynamicTargets_.end());
425     for (; i != i_end; ++i)
426         delete *i;
427 }
428
429 prefix_ void senf::log::detail::TargetRegistry::consoleAreas(std::ostream & os)
430 {
431     AreaRegistry::iterator i (AreaRegistry::instance().begin());
432     AreaRegistry::iterator const i_end (AreaRegistry::instance().end());
433     for (; i != i_end; ++i)
434         os << *i << "\n";
435 }
436
437 prefix_ void senf::log::detail::TargetRegistry::consoleStreams(std::ostream & os)
438 {
439     StreamRegistry::iterator i (StreamRegistry::instance().begin());
440     StreamRegistry::iterator const i_end (StreamRegistry::instance().end());
441     for (; i != i_end; ++i)
442         os << *i << "\n";
443 }
444
445 prefix_ void senf::log::detail::TargetRegistry::consoleWrite(LogParameters pm,
446                                                              std::string const & msg)
447 {
448     pm.setDefaults();
449     write(*pm.stream, *pm.area, pm.level, msg);
450 }
451
452 prefix_ boost::shared_ptr<senf::console::DirectoryNode>
453 senf::log::detail::TargetRegistry::consoleSelf(std::ostream & os)
454 {
455     return senf::console::Client::get(os).consoleDir().node().thisptr();
456 }                                            
457
458 ///////////////////////////////////////////////////////////////////////////
459 // senf::log::detail::LogParameters
460
461 prefix_ void senf::log::detail::LogParameters::clear()
462 {
463     stream = 0;
464     area = 0;
465     level = NONE::value;
466 }
467
468 prefix_ void senf::log::detail::LogParameters::setDefaults()
469 {
470     if (! stream)
471         stream = & senf::log::Debug::instance();
472     if (! area)
473         area = & senf::log::DefaultArea::instance();
474     if (level == NONE::value)
475         level = MESSAGE::value;
476 }
477
478 prefix_ senf::log::detail::LogParameters::LogParameters
479 senf::log::detail::LogParameters::defaultParameters()
480 {
481     LogParameters pm;
482     pm.clear();
483     pm.setDefaults();
484     return pm;
485 }
486
487 ///////////////////////////////////////////////////////////////////////////
488 // namespace members
489
490 prefix_ std::ostream & senf::log::operator<<(std::ostream & os, senf::log::Target::action_t const & action)
491 {
492     if( action == Target::ACCEPT) os << "ACCEPT";
493     else if( action == Target::REJECT) os << "REJECT";
494     else os << "unknown action";
495     return os;
496 }
497
498 namespace {
499
500     char const * levelNames[] = { 
501         "NONE", "VERBOSE", "NOTICE", "MESSAGE", "IMPORTANT", "CRITICAL", "FATAL", "DISABLED" };
502
503     void parseParamToken(std::string const & value, senf::log::detail::LogParameters & out)
504     {
505         senf::log::detail::StreamBase const * s (
506             senf::log::StreamRegistry::instance().lookup(value));
507         if (s) {
508             if (out.stream)
509                 throw senf::console::SyntaxErrorException("duplicate stream parameter");
510             out.stream = s;
511             return;
512         }
513
514         senf::log::detail::AreaBase const * a (
515             senf::log::AreaRegistry::instance().lookup(value));
516         if (a) {
517             if (out.area)
518                 throw senf::console::SyntaxErrorException("duplicate area parameter");
519             out.area = a;
520             return;
521         }
522         
523         char const ** i (
524             std::find(levelNames+1, levelNames+sizeof(levelNames)/sizeof(levelNames[0])-1, value));
525         if (i == levelNames+sizeof(levelNames)/sizeof(levelNames[0])-1)
526             throw senf::console::SyntaxErrorException("invalid log parameter");
527         if (out.level != senf::log::NONE::value)
528             throw senf::console::SyntaxErrorException("duplicate level parameter");
529         out.level = i-levelNames;
530     }
531
532 }
533
534 prefix_ std::ostream & senf::log::detail::operator<<(std::ostream & os,
535                                                      LogParameters const & pm)
536 {
537     os << '(';
538     if (pm.stream)
539         os << pm.stream->v_name();
540     if (pm.area)
541         if (pm.stream) os << ' ';
542         os << pm.area->v_name();
543     if (pm.level != NONE::value)
544         if (pm.stream || pm.area) os << ' ';
545         os << levelNames[pm.level];
546     os << ')';
547     return os;
548 }
549
550 prefix_ void senf::log::detail::
551 senf_console_parse_argument(console::ParseCommandInfo::TokensRange const & tokens,
552                             LogParameters & out)
553 {
554     out.clear();
555     
556     for (console::ParseCommandInfo::TokensRange::iterator i (tokens.begin());
557          i != tokens.end(); ++i)
558         parseParamToken(i->value(), out);
559 }
560
561 //////////////////////////////////////////////////////////////////////////
562 // I need to put this here, otherwise the file target will not be registered
563 // if it is not used ... :-(
564
565 senf::log::FileTarget::RegisterConsole senf::log::FileTarget::RegisterConsole::instance;
566 senf::log::SyslogTarget::RegisterConsole senf::log::SyslogTarget::RegisterConsole::instance;
567 senf::log::SyslogUDPTarget::RegisterConsole senf::log::SyslogUDPTarget::RegisterConsole::instance;
568
569 ///////////////////////////////cc.e////////////////////////////////////////
570 #undef prefix_
571 //#include "Target.mpp"
572
573
574 // Local Variables:
575 // mode: c++
576 // fill-column: 100
577 // comment-column: 40
578 // c-file-style: "senf"
579 // indent-tabs-mode: nil
580 // ispell-local-dictionary: "american"
581 // compile-command: "scons -u test"
582 // End: