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