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