ca0574912d4d519d21708b89bd5e12ef28cd3898
[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 }
411
412 prefix_ senf::log::detail::TargetRegistry::~TargetRegistry()
413 {
414     Targets::iterator i (dynamicTargets_.begin());
415     Targets::iterator const i_end (dynamicTargets_.end());
416     for (; i != i_end; ++i)
417         delete *i;
418 }
419
420 prefix_ void senf::log::detail::TargetRegistry::consoleAreas(std::ostream & os)
421 {
422     AreaRegistry::iterator i (AreaRegistry::instance().begin());
423     AreaRegistry::iterator const i_end (AreaRegistry::instance().end());
424     for (; i != i_end; ++i)
425         os << *i << "\n";
426 }
427
428 prefix_ void senf::log::detail::TargetRegistry::consoleStreams(std::ostream & os)
429 {
430     StreamRegistry::iterator i (StreamRegistry::instance().begin());
431     StreamRegistry::iterator const i_end (StreamRegistry::instance().end());
432     for (; i != i_end; ++i)
433         os << *i << "\n";
434 }
435
436 prefix_ void senf::log::detail::TargetRegistry::consoleWrite(LogParameters pm,
437                                                              std::string const & msg)
438 {
439     pm.setDefaults();
440     write(*pm.stream, *pm.area, pm.level, msg);
441 }
442
443 ///////////////////////////////////////////////////////////////////////////
444 // senf::log::detail::LogParameters
445
446 prefix_ void senf::log::detail::LogParameters::clear()
447 {
448     stream = 0;
449     area = 0;
450     level = NONE::value;
451 }
452
453 prefix_ void senf::log::detail::LogParameters::setDefaults()
454 {
455     if (! stream)
456         stream = & senf::log::Debug::instance();
457     if (! area)
458         area = & senf::log::DefaultArea::instance();
459     if (level == NONE::value)
460         level = MESSAGE::value;
461 }
462
463 prefix_ senf::log::detail::LogParameters::LogParameters
464 senf::log::detail::LogParameters::defaultParameters()
465 {
466     LogParameters pm;
467     pm.clear();
468     pm.setDefaults();
469     return pm;
470 }
471
472 ///////////////////////////////////////////////////////////////////////////
473 // namespace members
474
475 prefix_ std::ostream & senf::log::operator<<(std::ostream & os, senf::log::Target::action_t const & action)
476 {
477     if( action == Target::ACCEPT) os << "ACCEPT";
478     else if( action == Target::REJECT) os << "REJECT";
479     else os << "unknown action";
480     return os;
481 }
482
483 namespace {
484
485     char const * levelNames[] = { 
486         "NONE", "VERBOSE", "NOTICE", "MESSAGE", "IMPORTANT", "CRITICAL", "FATAL", "DISABLED" };
487
488     void parseParamToken(std::string const & value, senf::log::detail::LogParameters & out)
489     {
490         senf::log::detail::StreamBase const * s (
491             senf::log::StreamRegistry::instance().lookup(value));
492         if (s) {
493             if (out.stream)
494                 throw senf::console::SyntaxErrorException("duplicate stream parameter");
495             out.stream = s;
496             return;
497         }
498
499         senf::log::detail::AreaBase const * a (
500             senf::log::AreaRegistry::instance().lookup(value));
501         if (a) {
502             if (out.area)
503                 throw senf::console::SyntaxErrorException("duplicate area parameter");
504             out.area = a;
505             return;
506         }
507         
508         char const ** i (
509             std::find(levelNames+1, levelNames+sizeof(levelNames)/sizeof(levelNames[0])-1, value));
510         if (i == levelNames+sizeof(levelNames)/sizeof(levelNames[0])-1)
511             throw senf::console::SyntaxErrorException("invalid log parameter");
512         if (out.level != senf::log::NONE::value)
513             throw senf::console::SyntaxErrorException("duplicate level parameter");
514         out.level = i-levelNames;
515     }
516
517 }
518
519 prefix_ std::ostream & senf::log::detail::operator<<(std::ostream & os,
520                                                      LogParameters const & pm)
521 {
522     os << '(';
523     if (pm.stream)
524         os << pm.stream->v_name();
525     if (pm.area)
526         if (pm.stream) os << ' ';
527         os << pm.area->v_name();
528     if (pm.level != NONE::value)
529         if (pm.stream || pm.area) os << ' ';
530         os << levelNames[pm.level];
531     os << ')';
532     return os;
533 }
534
535 prefix_ void senf::log::detail::
536 senf_console_parse_argument(console::ParseCommandInfo::TokensRange const & tokens,
537                             LogParameters & out)
538 {
539     out.clear();
540     
541     for (console::ParseCommandInfo::TokensRange::iterator i (tokens.begin());
542          i != tokens.end(); ++i)
543         parseParamToken(i->value(), out);
544 }
545
546 //////////////////////////////////////////////////////////////////////////
547 // I need to put this here, otherwise the file target will not be registered
548 // if it is not used ... :-(
549
550 senf::log::FileTarget::RegisterConsole senf::log::FileTarget::RegisterConsole::instance;
551 senf::log::SyslogTarget::RegisterConsole senf::log::SyslogTarget::RegisterConsole::instance;
552 senf::log::SyslogUDPTarget::RegisterConsole senf::log::SyslogUDPTarget::RegisterConsole::instance;
553
554 ///////////////////////////////cc.e////////////////////////////////////////
555 #undef prefix_
556 //#include "Target.mpp"
557
558
559 // Local Variables:
560 // mode: c++
561 // fill-column: 100
562 // comment-column: 40
563 // c-file-style: "senf"
564 // indent-tabs-mode: nil
565 // ispell-local-dictionary: "american"
566 // compile-command: "scons -u test"
567 // End: