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>
34 #include <senf/Utils/Console/Sysdir.hh>
35 #include <senf/Utils/membind.hh>
37 //#include "Target.mpp"
39 ///////////////////////////////cc.p////////////////////////////////////////
41 ///////////////////////////////////////////////////////////////////////////
47 SENF_CONSOLE_REGISTER_ENUM_MEMBER( Target, action_t, (ACCEPT)(REJECT) );
51 SENF_CONSOLE_REGISTER_ENUM_MEMBER( TargetRegistry, Level,
52 (VERBOSE)(NOTICE)(MESSAGE)(IMPORTANT)(CRITICAL)(FATAL) );
56 prefix_ senf::log::Target::Target(std::string const & name)
58 namespace kw = senf::console::kw;
59 namespace fty = senf::console::factory;
61 detail::TargetRegistry::instance().registerTarget(this, name);
63 .add("list", fty::Command(&Target::consoleList, this)
64 .doc("Show routing table\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"
72 " verbose, notice, message, important, critical, fatal\n"
73 " If the log level is listed as 'default', the streams default limit\n"
75 " ACTION action to take: accept or reject") );
77 .add("route", fty::Command(&Target::consoleRoute, this)
78 .arg("index", "index at which to insert new rule")
79 .arg("parameters", "log parameters. The log parameters select the log stream, log area\n"
80 " and log level. You may specify any combination of these parameterse\n"
81 " in any order. Use the '/sys/log/stream' and '/sys/log/areas' commands\n"
82 " to list all valid streams and areas. Valid log levels are:\n"
83 " VERBOSE NOTICE MESSAGE IMPORTANT CRITICAL FATAL")
84 .arg("action", "routing action, one of: ACCEPT, REJECT",
85 kw::default_value=ACCEPT)
86 .doc("Add routing entry. Log messages are matched against the routing table beginning\n"
87 "with the first entry. The action of the first matching entry determines the\n"
88 "handling of the message.\n"
93 " route all messages with level above each streams default log limit to this\n"
96 " route 1 (my::Class VERBOSE)\n"
97 " route all messages which are in the my::Class area. Insert this route after\n"
100 " route (senf::log::Debug VERBOSE) REJECT\n"
102 " route all messages not in the senf::log::Debug stream to the current area.\n"
104 "The additional optional index argument identifies the position in the routing table\n"
105 "where the new routing entry will be added. Positive numbers count from the\n"
106 "beginning, 0 being the first routing entry. Negative values count from the end.") );
108 .add("route", fty::Command<void (detail::LogParameters, action_t)>(
109 boost::bind(&Target::consoleRoute, this, -1, _1, _2))
111 .arg("action", kw::default_value=ACCEPT) );
114 fty::Command(static_cast<void (Target::*)(int)>(&Target::unroute), this)
115 .arg("index", "index of routing entry to remove")
116 .overloadDoc("Remove routing entry with the given index") );
118 .add("unroute", fty::Command(&Target::consoleUnroute, this)
119 .arg("parameters", "log parameters. The log parameters select the log stream, log area\n"
120 " and log level. You may specify any combination of these parameters\n"
121 " in any order. Use the '/sys/log/stream' and '/sys/log/areas' commands\n"
122 " to list all valid streams and areas. Valid log levels are:\n"
123 " VERBOSE NOTICE MESSAGE IMPORTANT CRITICAL FATAL")
124 .arg("action", "routing action, one of: ACCEPT, REJECT",
125 kw::default_value=ACCEPT)
126 .overloadDoc("Remove the routing entry matching the specified arguments.") );
128 .add("flush", fty::Command(&Target::flush, this)
129 .doc("Remove all routing entries clearing the routing table. This will disable all\n"
130 "logging output on this target.") );
133 prefix_ senf::log::Target::~Target()
135 while( ! rib_.empty()) {
136 // This is slow but simplifies the area cache handling and removing a target should be
138 RIB::reverse_iterator i (rib_.rbegin());
139 unroute(i->stream_, i->area_, i->level_, i->action_);
141 detail::TargetRegistry::instance().unregisterTarget(this);
144 prefix_ void senf::log::Target::route(std::string const & stream, std::string const & area,
145 unsigned level, action_t action, int index)
147 detail::StreamBase const * s (0);
148 if (! stream.empty()) {
149 s = StreamRegistry::instance().lookup(stream);
151 throw InvalidStreamException();
153 detail::AreaBase const * a (0);
154 if (! area.empty()) {
155 a = AreaRegistry::instance().lookup(area);
157 throw InvalidAreaException();
159 route(s, a, level, action, index);
162 prefix_ void senf::log::Target::unroute(std::string const & stream, std::string const & area,
163 unsigned level, action_t action)
165 detail::StreamBase const * s (0);
166 if (! stream.empty()) {
167 s = StreamRegistry::instance().lookup(stream);
169 throw InvalidStreamException();
171 detail::AreaBase const * a (0);
172 if (! area.empty()) {
173 a = AreaRegistry::instance().lookup(area);
175 throw InvalidAreaException();
177 unroute(s, a, level, action);
180 prefix_ void senf::log::Target::unroute(int index)
186 if (RIB::size_type(-index) >= rib_.size())
190 std::advance(i, index);
193 if (RIB::size_type(index+1) >= rib_.size()) {
198 std::advance(i, index);
203 RoutingEntry entry (*i);
205 if (entry.action_ == ACCEPT)
206 updateRoutingCache(entry.stream_, entry.area_);
209 prefix_ void senf::log::Target::flush()
213 RIB::const_iterator i (old.begin());
214 RIB::const_iterator const i_end (old.end());
215 for (; i != i_end; ++i)
216 if (i->action_ == ACCEPT)
217 updateRoutingCache(i->stream_, i->area_);
220 ////////////////////////////////////////
223 prefix_ senf::console::ScopedDirectory<> & senf::log::Target::consoleDir()
225 return consoleDir_();
228 ////////////////////////////////////////
231 prefix_ void senf::log::Target::route(detail::StreamBase const * stream,
232 detail::AreaBase const * area, unsigned level,
233 action_t action, int index)
237 if (RIB::size_type(-index-1) >= rib_.size())
241 std::advance(i, index + 1 );
244 if (RIB::size_type(index) >= rib_.size())
248 std::advance(i, index);
251 rib_.insert(i, RoutingEntry(stream, area, level, action));
252 if (action == ACCEPT)
253 updateRoutingCache(stream, area);
254 // This disables the initial fallback routing
255 detail::TargetRegistry::instance().routed();
258 prefix_ void senf::log::Target::unroute(detail::StreamBase const * stream,
259 detail::AreaBase const * area, unsigned level,
262 RIB::iterator i = std::find(rib_.begin(), rib_.end(),
263 RoutingEntry(stream, area, level, action));
265 unroute(std::distance(rib_.begin(), i));
268 prefix_ void senf::log::Target::updateRoutingCache(detail::StreamBase const * stream,
269 detail::AreaBase const * area)
272 StreamRegistry::Registry::iterator i (StreamRegistry::instance().registry_.begin());
273 StreamRegistry::Registry::iterator const i_end (StreamRegistry::instance().registry_.end());
274 for (; i != i_end ; ++i)
275 updateRoutingCache(i->second, area);
279 AreaRegistry::Registry::iterator i (AreaRegistry::instance().registry_.begin());
280 AreaRegistry::Registry::iterator const i_end (AreaRegistry::instance().registry_.end());
281 for (; i != i_end ; ++i)
282 updateRoutingCache(stream, i->second);
286 // We are globally destructing and the area is gone already ...
288 unsigned limit (DISABLED::value);
289 RIB::iterator i (rib_.begin());
290 RIB::iterator const i_end (rib_.end());
291 for(; i != i_end; ++i)
292 if ( (! i->stream_ || i->stream_ == stream) &&
293 (! i->area_ || i->area_ == area) &&
294 i->action_ == ACCEPT ) {
295 unsigned l (i->level_ == NONE::value ? stream->defaultRuntimeLimit() : i->level_);
299 if (limit == DISABLED::value)
300 area->removeRoutingCache(*this, *stream);
302 area->updateRoutingCache(*this, *stream, limit);
305 prefix_ void senf::log::Target::write(time_type timestamp,
306 detail::StreamBase const & stream,
307 detail::AreaBase const & area, unsigned level,
308 std::string const & message)
310 RIB::iterator i (rib_.begin());
311 RIB::iterator const i_end (rib_.end());
312 for (; i != i_end; ++i)
313 if ( (! i->stream_ || i->stream_ == &stream) &&
314 (! i->area_ || i->area_ == &area) &&
315 (i->level_ == NONE::value ? stream.defaultRuntimeLimit() : i->level_) <= level ) {
316 if (i->action_ == ACCEPT)
317 v_write(timestamp, stream.v_name(), area.v_name(), level, message);
323 std::string formatLabel(std::string const & l)
328 return l.substr(l.size()-29);
332 char const * levelNames[] = {
333 "NONE", "VERBOSE", "NOTICE", "MESSAGE", "IMPORTANT", "CRITICAL", "FATAL", "DISABLED" };
335 char const * levelNamesList[] = {
336 "default", "verbose", "notice", "message", "important", "critical", "fatal", "disabled" };
339 prefix_ void senf::log::Target::consoleList(std::ostream & os)
342 boost::format fmt ("%2d %-29s %-29s %-9s %-6s\n");
343 os << fmt % "#" % "STREAM" % "AREA" % "LEVEL" % "ACTION";
345 for (iterator i (begin()); i != end(); ++i, ++n)
348 % formatLabel(i->stream())
349 % formatLabel(i->area())
350 % levelNamesList[i->level()]
351 % (i->action() == ACCEPT ? "accept" : "reject");
354 prefix_ void senf::log::Target::consoleRoute(int index, detail::LogParameters const & pm, action_t action)
356 route(pm.stream, pm.area, pm.level, action, index);
359 prefix_ void senf::log::Target::consoleUnroute(detail::LogParameters const & pm, action_t action)
361 unroute(pm.stream, pm.area, pm.level, action);
364 ///////////////////////////////////////////////////////////////////////////
365 // senf::log::detail::TargetRegistry
367 prefix_ void senf::log::detail::TargetRegistry::dynamicTarget(std::auto_ptr<Target> target)
369 namespace fty = senf::console::factory;
372 .add("remove", fty::Command<void ()>(
373 boost::bind(&TargetRegistry::consoleRemoveTarget, this, target.get()))
374 .doc("Remove target.") );
375 dynamicTargets_.insert(target.release());
378 prefix_ void senf::log::detail::TargetRegistry::registerTarget(Target * target,
379 std::string const & name)
381 targets_.insert(target);
382 consoleDir_().add(name, target->consoleDir_());
385 prefix_ void senf::log::detail::TargetRegistry::write(StreamBase const & stream,
386 AreaBase const & area, unsigned level,
387 std::string const & msg)
389 if (fallbackRouting_) {
390 if (level >= stream.defaultRuntimeLimit())
391 static_cast<Target &>(ConsoleTarget::instance()).v_write(
392 TimeSource::now(), stream.v_name(), area.v_name(), level, msg );
395 area.write( TimeSource::now(), stream, level, msg );
398 prefix_ void senf::log::detail::TargetRegistry::consoleRemoveTarget(Target * target)
400 dynamicTargets_.erase(target);
404 prefix_ senf::log::detail::TargetRegistry::TargetRegistry()
405 : fallbackRouting_(true)
407 namespace kw = senf::console::kw;
408 namespace fty = senf::console::factory;
410 console::sysdir().add("log", consoleDir_());
412 .add("areas", fty::Command(&TargetRegistry::consoleAreas, this)
413 .doc("List all areas") );
415 .add("streams", fty::Command(&TargetRegistry::consoleStreams, this)
416 .doc("List all streams with the streams default runtime log level limit.") );
418 .add("message", fty::Command(&TargetRegistry::consoleWrite, this)
419 .arg("parameters", "log parameters. The log parameters select the log stream, log area\n"
420 " and log level. You may specify any combination of these parameterse\n"
421 " in any order. Use the '/sys/log/stream' and '/sys/log/areas' commands\n"
422 " to list all valid streams and areas. Valid log levels are:\n"
423 " VERBOSE NOTICE MESSAGE IMPORTANT CRITICAL FATAL",
424 kw::default_value = LogParameters::defaultParameters())
425 .arg("message", "message to write")
426 .doc("Write log message.\n"
429 " message \"Test\";\n"
430 " message (senf::log::DefaultArea NOTICE) \"Test notice\";\n"
431 " message (FATAL) \"Program on fire\";\n"
432 " message (VERBOSE senf::log::Debug) \"Debug message\";") );
434 .add("self", fty::Command(&TargetRegistry::consoleSelf, this)
435 .doc("Get the log directory of the current network client. Example usage:\n"
437 "Just get the log config directory\n"
439 " <Directory '/sys/log/client-xxx.xxx.xxx.xxx:xxx'>\n"
441 "Route all messages to the currently connected client\n"
442 " $ /sys/log/self { route () ); }") );
445 prefix_ senf::log::detail::TargetRegistry::~TargetRegistry()
447 Targets::iterator i (dynamicTargets_.begin());
448 Targets::iterator const i_end (dynamicTargets_.end());
449 for (; i != i_end; ++i)
453 prefix_ void senf::log::detail::TargetRegistry::consoleAreas(std::ostream & os)
455 AreaRegistry::iterator i (AreaRegistry::instance().begin());
456 AreaRegistry::iterator const i_end (AreaRegistry::instance().end());
457 for (; i != i_end; ++i)
461 prefix_ void senf::log::detail::TargetRegistry::consoleStreams(std::ostream & os)
463 StreamRegistry::iterator i (StreamRegistry::instance().begin());
464 StreamRegistry::iterator const i_end (StreamRegistry::instance().end());
465 for (; i != i_end; ++i) {
467 << levelNames[StreamRegistry::instance().lookup(*i)->defaultRuntimeLimit()] << "\n";
471 prefix_ void senf::log::detail::TargetRegistry::consoleWrite(LogParameters pm,
472 std::string const & msg)
475 write(*pm.stream, *pm.area, pm.level, msg);
478 prefix_ boost::shared_ptr<senf::console::DirectoryNode>
479 senf::log::detail::TargetRegistry::consoleSelf(std::ostream & os)
481 return senf::console::Client::get(os).consoleDir().node().thisptr();
484 ///////////////////////////////////////////////////////////////////////////
485 // senf::log::detail::LogParameters
487 prefix_ void senf::log::detail::LogParameters::clear()
494 prefix_ void senf::log::detail::LogParameters::setDefaults()
497 stream = & senf::log::Debug::instance();
499 area = & senf::log::DefaultArea::instance();
500 if (level == NONE::value)
501 level = MESSAGE::value;
504 prefix_ senf::log::detail::LogParameters::LogParameters
505 senf::log::detail::LogParameters::defaultParameters()
513 ///////////////////////////////////////////////////////////////////////////
516 prefix_ std::ostream & senf::log::operator<<(std::ostream & os, senf::log::Target::action_t const & action)
518 if (action == Target::ACCEPT) os << "ACCEPT";
519 else if (action == Target::REJECT) os << "REJECT";
520 else os << "unknown action";
526 void parseParamToken(std::string const & value, senf::log::detail::LogParameters & out)
528 senf::log::detail::StreamBase const * s (
529 senf::log::StreamRegistry::instance().lookup(value));
532 throw senf::console::SyntaxErrorException("duplicate stream parameter");
537 senf::log::detail::AreaBase const * a (
538 senf::log::AreaRegistry::instance().lookup(value));
541 throw senf::console::SyntaxErrorException("duplicate area parameter");
547 std::find(levelNames+1, levelNames+sizeof(levelNames)/sizeof(levelNames[0])-1, value));
548 if (i == levelNames+sizeof(levelNames)/sizeof(levelNames[0])-1)
549 throw senf::console::SyntaxErrorException("invalid log parameter");
550 if (out.level != senf::log::NONE::value)
551 throw senf::console::SyntaxErrorException("duplicate level parameter");
552 out.level = i-levelNames;
557 prefix_ std::ostream & senf::log::detail::operator<<(std::ostream & os,
558 LogParameters const & pm)
562 os << pm.stream->v_name();
564 if (pm.stream) os << ' ';
565 os << pm.area->v_name();
566 if (pm.level != NONE::value)
567 if (pm.stream || pm.area) os << ' ';
568 os << levelNames[pm.level];
573 prefix_ void senf::log::detail::
574 senf_console_parse_argument(console::ParseCommandInfo::TokensRange const & tokens,
579 for (console::ParseCommandInfo::TokensRange::iterator i (tokens.begin());
580 i != tokens.end(); ++i)
581 parseParamToken(i->value(), out);
584 //////////////////////////////////////////////////////////////////////////
585 // I need to put this here, otherwise the file target will not be registered
586 // if it is not used ... :-(
588 senf::log::FileTarget::RegisterConsole senf::log::FileTarget::RegisterConsole::instance;
589 senf::log::SyslogTarget::RegisterConsole senf::log::SyslogTarget::RegisterConsole::instance;
590 senf::log::SyslogUDPTarget::RegisterConsole senf::log::SyslogUDPTarget::RegisterConsole::instance;
592 ///////////////////////////////cc.e////////////////////////////////////////
594 //#include "Target.mpp"
600 // comment-column: 40
601 // c-file-style: "senf"
602 // indent-tabs-mode: nil
603 // ispell-local-dictionary: "american"
604 // compile-command: "scons -u test"