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