4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
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
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.
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.
19 // The Original Code is Fraunhofer FOKUS code.
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.
26 // Stefan Bund <g0dil@berlios.de>
29 \brief Target non-inline non-template implementation */
36 #include <boost/format.hpp>
37 #include "ConsoleTarget.hh"
38 #include <senf/Utils/Console/Console.hh>
40 //#include "Target.mpp"
42 //-/////////////////////////////////////////////////////////////////////////////////////////////////
44 //-/////////////////////////////////////////////////////////////////////////////////////////////////
50 SENF_CONSOLE_REGISTER_ENUM_MEMBER( Target, action_t, (ACCEPT)(REJECT) );
54 SENF_CONSOLE_REGISTER_ENUM_MEMBER( TargetRegistry, Level,
55 (VERBOSE)(NOTICE)(MESSAGE)(IMPORTANT)(CRITICAL)(FATAL) );
59 prefix_ senf::log::Target::Target(std::string const & name)
61 namespace kw = console::kw;
62 namespace fty = console::factory;
64 detail::TargetRegistry::instance().registerTarget(this, name);
66 .add("list", fty::Command(&Target::consoleList, this)
67 .doc("Show routing table\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"
75 " verbose, notice, message, important, critical, fatal\n"
76 " If the log level is listed as 'default', the streams default limit\n"
78 " ACTION action to take: accept or reject") );
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"
96 " route all messages with level above each streams default log limit to this\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"
103 " route (senf::log::Debug VERBOSE) REJECT\n"
105 " route all messages not in the senf::log::Debug stream to the current area.\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.") );
111 .add("route", fty::Command<void (detail::LogParameters, action_t)>(
112 boost::bind(&Target::consoleRoute, this, -1, _1, _2))
114 .arg("action", kw::default_value=ACCEPT) );
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") );
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.") );
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.") );
136 prefix_ senf::log::Target::~Target()
138 while( ! rib_.empty()) {
139 // This is slow but simplifies the area cache handling and removing a target should be
141 RIB::reverse_iterator i (rib_.rbegin());
142 unroute(i->stream_, i->area_, i->level_, i->action_);
144 detail::TargetRegistry::instance().unregisterTarget(this);
147 prefix_ void senf::log::Target::route(std::string const & stream, std::string const & area,
148 unsigned level, action_t action, int index)
150 detail::StreamBase const * s (0);
151 if (! stream.empty()) {
152 s = StreamRegistry::instance().lookup(stream);
154 throw InvalidStreamException();
156 detail::AreaBase const * a (0);
157 if (! area.empty()) {
158 a = AreaRegistry::instance().lookup(area);
160 throw InvalidAreaException();
162 route(s, a, level, action, index);
165 prefix_ void senf::log::Target::unroute(std::string const & stream, std::string const & area,
166 unsigned level, action_t action)
168 detail::StreamBase const * s (0);
169 if (! stream.empty()) {
170 s = StreamRegistry::instance().lookup(stream);
172 throw InvalidStreamException();
174 detail::AreaBase const * a (0);
175 if (! area.empty()) {
176 a = AreaRegistry::instance().lookup(area);
178 throw InvalidAreaException();
180 unroute(s, a, level, action);
183 prefix_ void senf::log::Target::unroute(int index)
189 if (RIB::size_type(-index) >= rib_.size())
193 std::advance(i, index);
196 if (RIB::size_type(index+1) >= rib_.size()) {
201 std::advance(i, index);
206 RoutingEntry entry (*i);
208 if (entry.action_ == ACCEPT)
209 updateRoutingCache(entry.stream_, entry.area_);
212 prefix_ void senf::log::Target::flush()
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_);
223 //-/////////////////////////////////////////////////////////////////////////////////////////////////
226 prefix_ senf::console::ScopedDirectory<> & senf::log::Target::consoleDir()
228 return consoleDir_();
231 //-/////////////////////////////////////////////////////////////////////////////////////////////////
234 prefix_ void senf::log::Target::route(detail::StreamBase const * stream,
235 detail::AreaBase const * area, unsigned level,
236 action_t action, int index)
240 if (RIB::size_type(-index-1) >= rib_.size())
244 std::advance(i, index + 1 );
247 if (RIB::size_type(index) >= rib_.size())
251 std::advance(i, index);
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();
261 prefix_ void senf::log::Target::unroute(detail::StreamBase const * stream,
262 detail::AreaBase const * area, unsigned level,
265 RIB::iterator i = std::find(rib_.begin(), rib_.end(),
266 RoutingEntry(stream, area, level, action));
268 unroute(std::distance(rib_.begin(), i));
271 prefix_ void senf::log::Target::updateRoutingCache(detail::StreamBase const * stream,
272 detail::AreaBase const * area)
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);
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);
289 // We are globally destructing and the area is gone already ...
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_);
302 if (limit == DISABLED::value)
303 area->removeRoutingCache(*this, *stream);
305 area->updateRoutingCache(*this, *stream, limit);
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)
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);
326 std::string formatLabel(std::string const & l)
331 return l.substr(l.size()-29);
335 char const * levelNames[] = {
336 "NONE", "VERBOSE", "NOTICE", "MESSAGE", "IMPORTANT", "CRITICAL", "FATAL", "DISABLED" };
338 char const * levelNamesList[] = {
339 "default", "verbose", "notice", "message", "important", "critical", "fatal", "disabled" };
342 prefix_ void senf::log::Target::consoleList(std::ostream & os)
345 boost::format fmt ("%2d %-29s %-29s %-9s %-6s\n");
346 os << fmt % "#" % "STREAM" % "AREA" % "LEVEL" % "ACTION";
348 for (iterator i (begin()); i != end(); ++i, ++n)
351 % formatLabel(i->stream())
352 % formatLabel(i->area())
353 % levelNamesList[i->level()]
354 % (i->action() == ACCEPT ? "accept" : "reject");
357 prefix_ void senf::log::Target::consoleRoute(int index, detail::LogParameters const & pm, action_t action)
359 route(pm.stream, pm.area, pm.level, action, index);
362 prefix_ void senf::log::Target::consoleUnroute(detail::LogParameters const & pm, action_t action)
364 unroute(pm.stream, pm.area, pm.level, action);
367 //-/////////////////////////////////////////////////////////////////////////////////////////////////
368 // senf::log::detail::TargetRegistry
370 prefix_ void senf::log::detail::TargetRegistry::dynamicTarget(std::auto_ptr<Target> target)
372 namespace fty = console::factory;
375 .add("remove", fty::Command<void ()>(
376 boost::bind(&TargetRegistry::consoleRemoveTarget, this, target.get()))
377 .doc("Remove target.") );
378 dynamicTargets_.insert(target.release());
381 prefix_ void senf::log::detail::TargetRegistry::registerTarget(Target * target,
382 std::string const & name)
384 targets_.insert(target);
385 consoleDir_().add(name, target->consoleDir_());
388 prefix_ void senf::log::detail::TargetRegistry::write(StreamBase const & stream,
389 AreaBase const & area, unsigned level,
390 std::string const & msg)
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 );
398 area.write( TimeSource::now(), stream, level, msg );
401 prefix_ void senf::log::detail::TargetRegistry::consoleRemoveTarget(Target * target)
403 dynamicTargets_.erase(target);
407 prefix_ senf::log::detail::TargetRegistry::TargetRegistry()
408 : fallbackRouting_(true)
410 namespace kw = console::kw;
411 namespace fty = console::factory;
413 console::sysdir().add("log", consoleDir_());
415 .add("areas", fty::Command(&TargetRegistry::consoleAreas, this)
416 .doc("List all areas") );
418 .add("streams", fty::Command(&TargetRegistry::consoleStreams, this)
419 .doc("List all streams with the streams default runtime log level limit.") );
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"
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\";") );
437 .add("self", fty::Command(&TargetRegistry::consoleSelf, this)
438 .doc("Get the log directory of the current network client. Example usage:\n"
440 "Just get the log config directory\n"
442 " <Directory '/sys/log/client-xxx.xxx.xxx.xxx:xxx'>\n"
444 "Route all messages to the currently connected client\n"
445 " $ /sys/log/self { route () ); }") );
448 prefix_ senf::log::detail::TargetRegistry::~TargetRegistry()
450 Targets::iterator i (dynamicTargets_.begin());
451 Targets::iterator const i_end (dynamicTargets_.end());
452 for (; i != i_end; ++i)
456 prefix_ void senf::log::detail::TargetRegistry::consoleAreas(std::ostream & os)
458 AreaRegistry::iterator i (AreaRegistry::instance().begin());
459 AreaRegistry::iterator const i_end (AreaRegistry::instance().end());
460 for (; i != i_end; ++i)
464 prefix_ void senf::log::detail::TargetRegistry::consoleStreams(std::ostream & os)
466 StreamRegistry::iterator i (StreamRegistry::instance().begin());
467 StreamRegistry::iterator const i_end (StreamRegistry::instance().end());
468 for (; i != i_end; ++i) {
470 << levelNames[StreamRegistry::instance().lookup(*i)->defaultRuntimeLimit()] << "\n";
474 prefix_ void senf::log::detail::TargetRegistry::consoleWrite(LogParameters pm,
475 std::string const & msg)
478 write(*pm.stream, *pm.area, pm.level, msg);
481 prefix_ boost::shared_ptr<senf::console::DirectoryNode>
482 senf::log::detail::TargetRegistry::consoleSelf(std::ostream & os)
484 return senf::console::Client::get(os).consoleDir().node().thisptr();
487 //-/////////////////////////////////////////////////////////////////////////////////////////////////
488 // senf::log::detail::LogParameters
490 prefix_ void senf::log::detail::LogParameters::clear()
497 prefix_ void senf::log::detail::LogParameters::setDefaults()
500 stream = & senf::log::Debug::instance();
502 area = & senf::log::DefaultArea::instance();
503 if (level == NONE::value)
504 level = MESSAGE::value;
507 prefix_ senf::log::detail::LogParameters senf::log::detail::LogParameters::defaultParameters()
515 //-/////////////////////////////////////////////////////////////////////////////////////////////////
518 prefix_ std::ostream & senf::log::operator<<(std::ostream & os, senf::log::Target::action_t const & action)
520 if (action == Target::ACCEPT) os << "ACCEPT";
521 else if (action == Target::REJECT) os << "REJECT";
522 else os << "unknown action";
528 void parseParamToken(std::string const & value, senf::log::detail::LogParameters & out)
530 senf::log::detail::StreamBase const * s (
531 senf::log::StreamRegistry::instance().lookup(value));
534 throw senf::console::SyntaxErrorException("duplicate stream parameter");
539 senf::log::detail::AreaBase const * a (
540 senf::log::AreaRegistry::instance().lookup(value));
543 throw senf::console::SyntaxErrorException("duplicate area parameter");
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;
559 prefix_ std::ostream & senf::log::detail::operator<<(std::ostream & os,
560 LogParameters const & pm)
564 os << pm.stream->v_name();
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];
575 prefix_ void senf::log::detail::
576 senf_console_parse_argument(console::ParseCommandInfo::TokensRange const & tokens,
581 for (console::ParseCommandInfo::TokensRange::iterator i (tokens.begin());
582 i != tokens.end(); ++i)
583 parseParamToken(i->value(), out);
586 //-/////////////////////////////////////////////////////////////////////////////////////////////////
587 // I need to put this here, otherwise the file target will not be registered
588 // if it is not used ... :-(
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;
594 //-/////////////////////////////////////////////////////////////////////////////////////////////////
596 //#include "Target.mpp"
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"