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