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