4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 // Stefan Bund <g0dil@berlios.de>
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.
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.
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.
24 \brief Target non-inline non-template implementation */
31 #include <boost/format.hpp>
32 #include "ConsoleTarget.hh"
33 #include <senf/Utils/Console/Console.hh>
35 //#include "Target.mpp"
37 //-/////////////////////////////////////////////////////////////////////////////////////////////////
39 //-/////////////////////////////////////////////////////////////////////////////////////////////////
45 SENF_CONSOLE_REGISTER_ENUM_MEMBER( Target, action_t, (ACCEPT)(REJECT) );
49 SENF_CONSOLE_REGISTER_ENUM_MEMBER( TargetRegistry, Level,
50 (VERBOSE)(NOTICE)(MESSAGE)(IMPORTANT)(CRITICAL)(FATAL) );
54 prefix_ senf::log::Target::Target(std::string const & name)
56 namespace kw = console::kw;
57 namespace fty = console::factory;
59 detail::TargetRegistry::instance().registerTarget(this, name);
61 .add("list", fty::Command(&Target::consoleList, this)
62 .doc("Show routing table\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"
70 " verbose, notice, message, important, critical, fatal\n"
71 " If the log level is listed as 'default', the streams default limit\n"
73 " ACTION action to take: accept or reject") );
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 parameters\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"
91 " route all messages with level above each streams default log limit to this\n"
94 " route 1 (my::Class VERBOSE)\n"
95 " route all messages which are in the my::Class area. Insert this route after\n"
98 " route (senf::log::Debug VERBOSE) REJECT\n"
100 " route all messages not in the senf::log::Debug stream to the current area.\n"
102 "The additional optional index argument identifies the position in the routing table\n"
103 "where the new routing entry will be added. Positive numbers count from the\n"
104 "beginning, 0 being the first routing entry. Negative values count from the end.") );
106 .add("route", fty::Command<void (detail::LogParameters, action_t)>(
107 boost::bind(&Target::consoleRoute, this, -1, _1, _2))
109 .arg("action", kw::default_value=ACCEPT) );
112 fty::Command(static_cast<void (Target::*)(int)>(&Target::unroute), this)
113 .arg("index", "index of routing entry to remove")
114 .overloadDoc("Remove routing entry with the given index") );
116 .add("unroute", fty::Command(&Target::consoleUnroute, this)
117 .arg("parameters", "log parameters. The log parameters select the log stream, log area\n"
118 " and log level. You may specify any combination of these parameters\n"
119 " in any order. Use the '/sys/log/stream' and '/sys/log/areas' commands\n"
120 " to list all valid streams and areas. Valid log levels are:\n"
121 " VERBOSE NOTICE MESSAGE IMPORTANT CRITICAL FATAL")
122 .arg("action", "routing action, one of: ACCEPT, REJECT",
123 kw::default_value=ACCEPT)
124 .overloadDoc("Remove the routing entry matching the specified arguments.") );
126 .add("flush", fty::Command(&Target::flush, this)
127 .doc("Remove all routing entries clearing the routing table. This will disable all\n"
128 "logging output on this target.") );
131 prefix_ senf::log::Target::~Target()
133 while( ! rib_.empty()) {
134 // This is slow but simplifies the area cache handling and removing a target should be
136 RIB::reverse_iterator i (rib_.rbegin());
137 unroute(i->stream_, i->area_, i->level_, i->action_);
139 detail::TargetRegistry::instance().unregisterTarget(this);
142 prefix_ void senf::log::Target::route(std::string const & stream, std::string const & area,
143 unsigned level, action_t action, int index)
145 detail::StreamBase const * s (0);
146 if (! stream.empty()) {
147 s = StreamRegistry::instance().lookup(stream);
149 throw InvalidStreamException();
151 detail::AreaBase const * a (0);
152 if (! area.empty()) {
153 a = AreaRegistry::instance().lookup(area);
155 throw InvalidAreaException();
157 route(s, a, level, action, index);
160 prefix_ void senf::log::Target::unroute(std::string const & stream, std::string const & area,
161 unsigned level, action_t action)
163 detail::StreamBase const * s (0);
164 if (! stream.empty()) {
165 s = StreamRegistry::instance().lookup(stream);
167 throw InvalidStreamException();
169 detail::AreaBase const * a (0);
170 if (! area.empty()) {
171 a = AreaRegistry::instance().lookup(area);
173 throw InvalidAreaException();
175 unroute(s, a, level, action);
178 prefix_ void senf::log::Target::unroute(int index)
184 if (RIB::size_type(-index) >= rib_.size())
188 std::advance(i, index);
191 if (RIB::size_type(index+1) >= rib_.size()) {
196 std::advance(i, index);
201 RoutingEntry entry (*i);
203 if (entry.action_ == ACCEPT)
204 updateRoutingCache(entry.stream_, entry.area_);
207 prefix_ void senf::log::Target::flush()
211 RIB::const_iterator i (old.begin());
212 RIB::const_iterator const i_end (old.end());
213 for (; i != i_end; ++i)
214 if (i->action_ == ACCEPT)
215 updateRoutingCache(i->stream_, i->area_);
218 //-/////////////////////////////////////////////////////////////////////////////////////////////////
221 prefix_ senf::console::ScopedDirectory<> & senf::log::Target::consoleDir()
223 return consoleDir_();
226 //-/////////////////////////////////////////////////////////////////////////////////////////////////
229 prefix_ void senf::log::Target::route(detail::StreamBase const * stream,
230 detail::AreaBase const * area, unsigned level,
231 action_t action, int index)
235 if (RIB::size_type(-index-1) >= rib_.size())
239 std::advance(i, index + 1 );
242 if (RIB::size_type(index) >= rib_.size())
246 std::advance(i, index);
249 rib_.insert(i, RoutingEntry(stream, area, level, action));
250 if (action == ACCEPT)
251 updateRoutingCache(stream, area);
252 // This disables the initial fallback routing
253 detail::TargetRegistry::instance().routed();
256 prefix_ void senf::log::Target::unroute(detail::StreamBase const * stream,
257 detail::AreaBase const * area, unsigned level,
260 RIB::iterator i = std::find(rib_.begin(), rib_.end(),
261 RoutingEntry(stream, area, level, action));
263 unroute(std::distance(rib_.begin(), i));
266 prefix_ void senf::log::Target::updateRoutingCache(detail::StreamBase const * stream,
267 detail::AreaBase const * area)
270 StreamRegistry::Registry::iterator i (StreamRegistry::instance().registry_.begin());
271 StreamRegistry::Registry::iterator const i_end (StreamRegistry::instance().registry_.end());
272 for (; i != i_end ; ++i)
273 updateRoutingCache(i->second, area);
277 AreaRegistry::Registry::iterator i (AreaRegistry::instance().registry_.begin());
278 AreaRegistry::Registry::iterator const i_end (AreaRegistry::instance().registry_.end());
279 for (; i != i_end ; ++i)
280 updateRoutingCache(stream, i->second);
284 // We are globally destructing and the area is gone already ...
286 unsigned limit (DISABLED::value);
287 RIB::iterator i (rib_.begin());
288 RIB::iterator const i_end (rib_.end());
289 for (; i != i_end; ++i)
290 if ( (! i->stream_ || i->stream_ == stream) &&
291 (! i->area_ || i->area_ == area) &&
292 i->action_ == ACCEPT ) {
293 unsigned l (i->level_ == NONE::value ? stream->defaultRuntimeLimit() : i->level_);
297 if (limit == DISABLED::value)
298 area->removeRoutingCache(*this, *stream);
300 area->updateRoutingCache(*this, *stream, limit);
303 prefix_ void senf::log::Target::write(time_type timestamp,
304 detail::StreamBase const & stream,
305 detail::AreaBase const & area, unsigned level,
306 std::string const & message)
308 RIB::iterator i (rib_.begin());
309 RIB::iterator const i_end (rib_.end());
310 for (; i != i_end; ++i)
311 if ( (! i->stream_ || i->stream_ == &stream) &&
312 (! i->area_ || i->area_ == &area) &&
313 (i->level_ == NONE::value ? stream.defaultRuntimeLimit() : i->level_) <= level ) {
314 if (i->action_ == ACCEPT)
315 v_write(timestamp, stream.v_name(), area.v_name(), level, message);
321 std::string formatLabel(std::string const & l)
326 return l.substr(l.size()-29);
330 char const * levelNames[] = {
331 "NONE", "VERBOSE", "NOTICE", "MESSAGE", "IMPORTANT", "CRITICAL", "FATAL", "DISABLED" };
333 char const * levelNamesList[] = {
334 "default", "verbose", "notice", "message", "important", "critical", "fatal", "disabled" };
337 prefix_ void senf::log::Target::consoleList(std::ostream & os)
340 boost::format fmt ("%2d %-29s %-29s %-9s %-6s\n");
341 os << fmt % "#" % "STREAM" % "AREA" % "LEVEL" % "ACTION";
343 for (iterator i (begin()); i != end(); ++i, ++n)
346 % formatLabel(i->stream())
347 % formatLabel(i->area())
348 % levelNamesList[i->level()]
349 % (i->action() == ACCEPT ? "accept" : "reject");
352 prefix_ void senf::log::Target::consoleRoute(int index, detail::LogParameters const & pm, action_t action)
354 route(pm.stream, pm.area, pm.level, action, index);
357 prefix_ void senf::log::Target::consoleUnroute(detail::LogParameters const & pm, action_t action)
359 unroute(pm.stream, pm.area, pm.level, action);
362 //-/////////////////////////////////////////////////////////////////////////////////////////////////
363 // senf::log::detail::TargetRegistry
365 prefix_ void senf::log::detail::TargetRegistry::dynamicTarget(std::auto_ptr<Target> target)
367 namespace fty = console::factory;
370 .add("remove", fty::Command<void ()>(
371 boost::bind(&TargetRegistry::consoleRemoveTarget, this, target.get()))
372 .doc("Remove target.") );
373 dynamicTargets_.insert(target.release());
376 prefix_ void senf::log::detail::TargetRegistry::registerTarget(Target * target,
377 std::string const & name)
379 targets_.insert(target);
380 consoleDir_().add(name, target->consoleDir_());
383 prefix_ void senf::log::detail::TargetRegistry::write(StreamBase const & stream,
384 AreaBase const & area, unsigned level,
385 std::string const & msg)
387 if (fallbackRouting_) {
388 if (level >= stream.defaultRuntimeLimit())
389 static_cast<Target &>(ConsoleTarget::instance()).v_write(
390 TimeSource::now(), stream.v_name(), area.v_name(), level, msg );
393 area.write( TimeSource::now(), stream, level, msg );
396 prefix_ void senf::log::detail::TargetRegistry::consoleRemoveTarget(Target * target)
398 dynamicTargets_.erase(target);
402 prefix_ senf::log::detail::TargetRegistry::TargetRegistry()
403 : fallbackRouting_(true)
405 namespace kw = console::kw;
406 namespace fty = console::factory;
408 console::sysdir().add("log", consoleDir_());
410 .add("areas", fty::Command(&TargetRegistry::consoleAreas, this)
411 .doc("List all areas") );
413 .add("streams", fty::Command(&TargetRegistry::consoleStreams, this)
414 .doc("List all streams with the streams default runtime log level limit.") );
416 .add("message", fty::Command(&TargetRegistry::consoleWrite, this)
417 .arg("parameters", "log parameters. The log parameters select the log stream, log area\n"
418 " and log level. You may specify any combination of these parameters\n"
419 " in any order. Use the '/sys/log/stream' and '/sys/log/areas' commands\n"
420 " to list all valid streams and areas. Valid log levels are:\n"
421 " VERBOSE NOTICE MESSAGE IMPORTANT CRITICAL FATAL",
422 kw::default_value = LogParameters::defaultParameters())
423 .arg("message", "message to write")
424 .doc("Write log message.\n"
427 " message \"Test\";\n"
428 " message (senf::log::DefaultArea NOTICE) \"Test notice\";\n"
429 " message (FATAL) \"Program on fire\";\n"
430 " message (VERBOSE senf::log::Debug) \"Debug message\";") );
432 .add("self", fty::Command(&TargetRegistry::consoleSelf, this)
433 .doc("Get the log directory of the current network client. Example usage:\n"
435 "Just get the log config directory\n"
437 " <Directory '/sys/log/client-xxx.xxx.xxx.xxx:xxx'>\n"
439 "Route all messages to the currently connected client\n"
440 " $ /sys/log/self { route () ); }") );
443 prefix_ senf::log::detail::TargetRegistry::~TargetRegistry()
445 Targets::iterator i (dynamicTargets_.begin());
446 Targets::iterator const i_end (dynamicTargets_.end());
447 for (; i != i_end; ++i)
451 prefix_ void senf::log::detail::TargetRegistry::consoleAreas(std::ostream & os)
453 AreaRegistry::iterator i (AreaRegistry::instance().begin());
454 AreaRegistry::iterator const i_end (AreaRegistry::instance().end());
455 for (; i != i_end; ++i)
459 prefix_ void senf::log::detail::TargetRegistry::consoleStreams(std::ostream & os)
461 StreamRegistry::iterator i (StreamRegistry::instance().begin());
462 StreamRegistry::iterator const i_end (StreamRegistry::instance().end());
463 for (; i != i_end; ++i) {
465 << levelNames[StreamRegistry::instance().lookup(*i)->defaultRuntimeLimit()] << "\n";
469 prefix_ void senf::log::detail::TargetRegistry::consoleWrite(LogParameters pm,
470 std::string const & msg)
473 write(*pm.stream, *pm.area, pm.level, msg);
476 prefix_ boost::shared_ptr<senf::console::DirectoryNode>
477 senf::log::detail::TargetRegistry::consoleSelf(std::ostream & os)
479 return senf::console::Client::get(os).consoleDir().node().thisptr();
482 //-/////////////////////////////////////////////////////////////////////////////////////////////////
483 // senf::log::detail::LogParameters
485 prefix_ void senf::log::detail::LogParameters::clear()
492 prefix_ void senf::log::detail::LogParameters::setDefaults()
495 stream = & senf::log::Debug::instance();
497 area = & senf::log::DefaultArea::instance();
498 if (level == NONE::value)
499 level = MESSAGE::value;
502 prefix_ senf::log::detail::LogParameters senf::log::detail::LogParameters::defaultParameters()
510 //-/////////////////////////////////////////////////////////////////////////////////////////////////
513 prefix_ std::ostream & senf::log::operator<<(std::ostream & os, senf::log::Target::action_t const & action)
515 if (action == Target::ACCEPT) os << "ACCEPT";
516 else if (action == Target::REJECT) os << "REJECT";
517 else os << "unknown action";
523 void parseParamToken(std::string const & value, senf::log::detail::LogParameters & out)
525 senf::log::detail::StreamBase const * s (
526 senf::log::StreamRegistry::instance().lookup(value));
529 throw senf::console::SyntaxErrorException("duplicate stream parameter");
534 senf::log::detail::AreaBase const * a (
535 senf::log::AreaRegistry::instance().lookup(value));
538 throw senf::console::SyntaxErrorException("duplicate area parameter");
544 std::find(levelNames+1, levelNames+sizeof(levelNames)/sizeof(levelNames[0])-1, value));
545 if (i == levelNames+sizeof(levelNames)/sizeof(levelNames[0])-1)
546 throw senf::console::SyntaxErrorException("invalid log parameter");
547 if (out.level != senf::log::NONE::value)
548 throw senf::console::SyntaxErrorException("duplicate level parameter");
549 out.level = i-levelNames;
554 prefix_ std::ostream & senf::log::detail::operator<<(std::ostream & os,
555 LogParameters const & pm)
559 os << pm.stream->v_name();
561 if (pm.stream) os << ' ';
562 os << pm.area->v_name();
563 if (pm.level != NONE::value)
564 if (pm.stream || pm.area) os << ' ';
565 os << levelNames[pm.level];
570 prefix_ void senf::log::detail::
571 senf_console_parse_argument(console::ParseCommandInfo::TokensRange const & tokens,
576 for (console::ParseCommandInfo::TokensRange::iterator i (tokens.begin());
577 i != tokens.end(); ++i)
578 parseParamToken(i->value(), out);
581 //-/////////////////////////////////////////////////////////////////////////////////////////////////
582 // I need to put this here, otherwise the file target will not be registered
583 // if it is not used ... :-(
585 senf::log::FileTarget::RegisterConsole senf::log::FileTarget::RegisterConsole::instance;
586 senf::log::SyslogTarget::RegisterConsole senf::log::SyslogTarget::RegisterConsole::instance;
587 senf::log::SyslogUDPTarget::RegisterConsole senf::log::SyslogUDPTarget::RegisterConsole::instance;
589 //-/////////////////////////////////////////////////////////////////////////////////////////////////
591 //#include "Target.mpp"
597 // comment-column: 40
598 // c-file-style: "senf"
599 // indent-tabs-mode: nil
600 // ispell-local-dictionary: "american"
601 // compile-command: "scons -u test"