Utils/Logger: Implement areas and streams commands
[senf.git] / Utils / Logger / Target.cc
1 // $Id$
2 //
3 // Copyright (C) 2007
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
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.
12 //
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.
17 //
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.
22
23 /** \file
24     \brief Target non-inline non-template implementation */
25
26 #include "Target.hh"
27 #include "Target.ih"
28
29 // Custom includes
30 #include <algorithm>
31 #include <boost/format.hpp>
32 #include "ConsoleTarget.hh"
33 #include "../Console/Console.hh"
34 #include "../Console/Sysdir.hh"
35 #include "../membind.hh"
36
37 //#include "Target.mpp"
38 #define prefix_
39 ///////////////////////////////cc.p////////////////////////////////////////
40
41 ///////////////////////////////////////////////////////////////////////////
42 // senf::log::Target
43
44 namespace senf {
45 namespace log {
46
47     SENF_CONSOLE_REGISTER_ENUM_MEMBER( Target, action_t, (ACCEPT)(REJECT) );
48
49 }}
50
51 namespace {
52 namespace local {
53
54     enum Level { 
55         VERBOSE = senf::log::VERBOSE::value, 
56         NOTICE = senf::log::NOTICE::value, 
57         MESSAGE = senf::log::MESSAGE::value, 
58         IMPORTANT = senf::log::IMPORTANT::value, 
59         CRITICAL = senf::log::CRITICAL::value, 
60         FATAL = senf::log::FATAL::value
61     };
62     SENF_CONSOLE_REGISTER_ENUM( Level, 
63                                 (VERBOSE)(NOTICE)(MESSAGE)(IMPORTANT)(CRITICAL)(FATAL) );
64     
65 }}
66
67 prefix_ senf::log::Target::Target(std::string const & name)
68 {
69     namespace kw = senf::console::kw;
70
71     detail::TargetRegistry::instance().registerTarget(this, name);
72     consoleDir_().add("list", senf::membind(&Target::consoleList, this))
73         .doc("Show routing table\n"
74              "\n"
75              "Columns:\n"
76              "    #       rule index\n"
77              "    STREAM  stream to match, empty to match all streams\n"
78              "    AREA    area to match, empty to match all targets\n"
79              "    LEVEL   match messages with level above this. Log levels in increasing order\n"
80              "            are:\n"
81              "                verbose, notice, message, important, critical, fatal\n"
82              "    ACTION  action to take: accept or reject");
83     consoleDir_().add("route", 
84                       boost::function<void (std::string const &, std::string const &,
85                                             local::Level, action_t, int)>(
86                                                 senf::membind(
87                                                     static_cast<void (Target::*)(
88                                                         std::string const &, std::string const &,
89                                                         unsigned, action_t, int)>(&Target::route),
90                                                     this)))
91         .arg("stream", 
92              "stream to match or empty to match any stream\n"
93              "              use '/sys/log/streams' to list all available streams",
94              kw::default_value="")
95         .arg("area", 
96              "area to match or empty to match any area\n"
97              "              use '/sys/log/areas' to list all available areas",
98              kw::default_value="")
99         .arg("level", "log level, one of: VERBOSE, NOTICE, MESSAGE, IMPORTANT, CRITICAL, FATAL",
100              kw::default_value=local::VERBOSE)
101         .arg("action", "routing action, one of: ACCEPT, REJECT",
102              kw::default_value=ACCEPT)
103         .arg("index", "index at which to insert new rule",
104              kw::default_value=-1)
105         .doc("Add routing entry. Log messages are matched against the routing table beginning\n"
106              "with the first entry. The action of the first matching entry determines the\n"
107              "handling of the message.\n"
108              "\n"
109              "Examples:\n"
110              "\n"
111              "    route\n"
112              "        route all messages to this target.\n"
113              "\n"
114              "    route \"\" my::Class\n"
115              "        route all messages which are in the my::Class area.\n"
116              "\n"
117              "    route senf::log::Debug \"\" VERBOSE REJECT\n"
118              "    route \"\" \"\" VERBOSE\n"
119              "        route all messages not in the senf::log::Debug stream to the current area.\n"
120              "\n"
121              "The additional optional index argument identifies the position in the routing table\n"
122              "where the new routing entry will be added. Positive numbers count from the\n"
123              "beginning, 0 being the first routing entry. Negative values count from the end.\n");
124     consoleDir_().add("unroute",
125                       senf::membind(static_cast<void (Target::*)(int)>(&Target::unroute), this))
126         .arg("index", "index of routing entry to remove")
127         .overloadDoc("Remove routing entry with the given index");
128     consoleDir_().add("unroute", 
129                       boost::function<void (std::string const &, std::string const &,
130                                             local::Level, action_t)>(
131                                                 senf::membind(
132                                                     static_cast<void (Target::*)(
133                                                         std::string const &, std::string const &,
134                                                         unsigned, action_t)>(&Target::unroute),
135                                                     this)))
136         .arg("stream", "stream to match or empty to match any stream",
137              kw::default_value="")
138         .arg("area", "area to match or empty to match any area",
139              kw::default_value="")
140         .arg("level", "log level, one of: VERBOSE, NOTICE, MESSAGE, IMPORTANT, CRITICAL, FATAL",
141              kw::default_value=local::VERBOSE)
142         .arg("action", "routing action, one of: ACCEPT, REJECT",
143              kw::default_value=ACCEPT)
144         .overloadDoc("Remove the routing entry matching the specified arguments.");
145     consoleDir_().add("flush", senf::membind(&Target::flush, this))
146         .doc("Remove all routing entries clearing the routing table. This will disable all\n"
147              "logging output on this target.");
148 }
149
150 prefix_ senf::log::Target::~Target()
151 {
152     while( ! rib_.empty()) {
153         // This is slow but simplifies the area cache handling and removing a target should be
154         // relatively seldom
155         RIB::reverse_iterator i (rib_.rbegin());
156         unroute(i->stream_, i->area_, i->level_, i->action_);
157     }
158     detail::TargetRegistry::instance().unregisterTarget(this);
159 }
160
161 prefix_ void senf::log::Target::route(std::string const & stream, std::string const & area,
162                                       unsigned level, action_t action, int index)
163 {
164     detail::StreamBase const * s (0);
165     if (! stream.empty()) {
166         s = StreamRegistry::instance().lookup(stream);
167         if (!s)
168             throw InvalidStreamException();
169     }
170     detail::AreaBase const * a (0);
171     if (! area.empty()) {
172         a = AreaRegistry::instance().lookup(area);
173         if (!a)
174             throw InvalidAreaException();
175     }
176     route(s, a, level, action, index);
177 }
178
179 prefix_ void senf::log::Target::unroute(std::string const & stream, std::string const & area,
180                                         unsigned level, action_t action)
181 {
182     detail::StreamBase const * s (0);
183     if (! stream.empty()) {
184         s = StreamRegistry::instance().lookup(stream);
185         if (!s)
186             throw InvalidStreamException();
187     }
188     detail::AreaBase const * a (0);
189     if (! area.empty()) {
190         a = AreaRegistry::instance().lookup(area);
191         if (!a)
192             throw InvalidAreaException();
193     }
194     unroute(s, a, level, action);
195 }
196
197 prefix_ void senf::log::Target::unroute(int index)
198 {
199     if (rib_.empty())
200         return;
201     RIB::iterator i;
202     if (index < 0) {
203         if (RIB::size_type(-index) >= rib_.size())
204             i = rib_.begin();
205         else {
206             i = rib_.end();
207             std::advance(i, index);
208         }
209     } else {
210         if (RIB::size_type(index+1) >= rib_.size()) {
211             i = rib_.end();
212             --i;
213         } else {
214             i = rib_.begin();
215             std::advance(i, index);
216         }
217     }
218     if (i == rib_.end())
219         return;
220     RoutingEntry entry (*i);
221     rib_.erase(i);
222     if (entry.action_ == ACCEPT)
223         updateRoutingCache(entry.stream_, entry.area_);
224 }
225
226 prefix_ void senf::log::Target::flush()
227 {
228     RIB old;
229     rib_.swap(old);
230     RIB::const_iterator i (old.begin());
231     RIB::const_iterator const i_end (old.end());
232     for (; i != i_end; ++i)
233         if (i->action_ == ACCEPT)
234             updateRoutingCache(i->stream_, i->area_);
235 }
236
237 ////////////////////////////////////////
238 // protected members
239
240 prefix_ senf::log::detail::TargetRegistry::TargetRegistry()
241     : fallbackRouting_(true)
242 {
243     console::sysdir().add("log", consoleDir_());
244     consoleDir_().add("areas", senf::membind(&TargetRegistry::consoleAreas, this))
245         .doc("List all areas");
246     consoleDir_().add("streams", senf::membind(&TargetRegistry::consoleStreams, this))
247         .doc("List all streams");
248 }
249
250 prefix_ void senf::log::detail::TargetRegistry::consoleAreas(std::ostream & os)
251 {
252     AreaRegistry::iterator i (AreaRegistry::instance().begin());
253     AreaRegistry::iterator const i_end (AreaRegistry::instance().end());
254     for (; i != i_end; ++i)
255         os << *i << "\n";
256 }
257
258 prefix_ void senf::log::detail::TargetRegistry::consoleStreams(std::ostream & os)
259 {
260     StreamRegistry::iterator i (StreamRegistry::instance().begin());
261     StreamRegistry::iterator const i_end (StreamRegistry::instance().end());
262     for (; i != i_end; ++i)
263         os << *i << "\n";
264 }
265
266 ////////////////////////////////////////
267 // private members
268
269 prefix_ void senf::log::Target::route(detail::StreamBase const * stream,
270                                       detail::AreaBase const * area, unsigned level,
271                                       action_t action, int index)
272 {
273     RIB::iterator i;
274     if (index < 0) {
275         if (RIB::size_type(-index-1) >= rib_.size())
276             i = rib_.begin();
277         else {
278             i = rib_.end();
279             std::advance(i, index + 1 );
280         }
281     } else {
282         if (RIB::size_type(index) >= rib_.size())
283             i = rib_.end();
284         else {
285             i = rib_.begin();
286             std::advance(i, index);
287         }
288     }
289     rib_.insert(i, RoutingEntry(stream, area, level, action));
290     if (action == ACCEPT)
291         updateRoutingCache(stream, area);
292     // This disables the initial fallback routing
293     detail::TargetRegistry::instance().routed();
294 }
295
296 prefix_ void senf::log::Target::unroute(detail::StreamBase const * stream,
297                                         detail::AreaBase const * area, unsigned level,
298                                         action_t action)
299 {
300     RIB::iterator i = std::find(rib_.begin(), rib_.end(),
301                                 RoutingEntry(stream, area, level, action));
302     if (i != rib_.end())
303         unroute(std::distance(rib_.begin(), i));
304 }
305
306 prefix_ void senf::log::Target::updateRoutingCache(detail::StreamBase const * stream,
307                                                    detail::AreaBase const * area)
308 {
309     if (! stream) {
310         StreamRegistry::Registry::iterator i (StreamRegistry::instance().registry_.begin());
311         StreamRegistry::Registry::iterator const i_end (StreamRegistry::instance().registry_.end());
312         for (; i != i_end ; ++i)
313             updateRoutingCache(i->second, area);
314         return;
315     }
316     if (! area) {
317         AreaRegistry::Registry::iterator i (AreaRegistry::instance().registry_.begin());
318         AreaRegistry::Registry::iterator const i_end (AreaRegistry::instance().registry_.end());
319         for (; i != i_end ; ++i)
320             updateRoutingCache(stream, i->second);
321         return;
322     }
323     unsigned limit (DISABLED::value);
324     RIB::iterator i (rib_.begin());
325     RIB::iterator const i_end (rib_.end());
326     for(; i != i_end; ++i)
327         if ( (! i->stream_ || i->stream_ == stream) &&
328              (! i->area_ || i->area_ == area) &&
329              i->action_ == ACCEPT ) {
330             unsigned l (i->level_ == NONE::value ? stream->defaultRuntimeLimit() : i->level_);
331             if (l < limit)
332                 limit = l;
333         }
334     if (limit == DISABLED::value)
335         area->removeRoutingCache(*this, *stream);
336     else
337         area->updateRoutingCache(*this, *stream, limit);
338 }
339
340 prefix_ void senf::log::Target::write(time_type timestamp,
341                                       detail::StreamBase const & stream,
342                                       detail::AreaBase const & area, unsigned level,
343                                       std::string const & message)
344 {
345     RIB::iterator i (rib_.begin());
346     RIB::iterator const i_end (rib_.end());
347     for (; i != i_end; ++i)
348         if ( (! i->stream_ || i->stream_ == &stream) &&
349              (! i->area_ || i->area_ == &area) &&
350              (i->level_ == NONE::value ? stream.defaultRuntimeLimit() : i->level_) <= level ) {
351             if (i->action_ == ACCEPT)
352                 v_write(timestamp, stream.v_name(), area.v_name(), level, message);
353             return;
354         }
355 }
356
357 namespace {
358     std::string formatLabel(std::string const & l)
359     {
360         if (l.empty())
361             return "-";
362         if (l.size() > 29)
363             return l.substr(l.size()-29);
364         return l;
365     }
366 }
367
368 prefix_ void senf::log::Target::consoleList(std::ostream & os)
369 {
370     static char const * levels[] = { 
371         "none", "verbose", "notice", "message", "important", "critical", "fatal", "disabled" };
372
373     boost::format fmt ("%2d %-29s %-29s %-9s %-6s\n");
374     os << fmt % "#" % "STREAM" % "AREA" % "LEVEL" % "ACTION";
375     unsigned n (0);
376     for (iterator i (begin()); i != end(); ++i, ++n)
377         os << fmt 
378             % n 
379             % formatLabel(i->stream())
380             % formatLabel(i->area())
381             % levels[i->level()]
382             % (i->action() == ACCEPT ? "accept" : "reject");
383 }
384
385 ///////////////////////////////////////////////////////////////////////////
386 // senf::log::detail::TargetRegistry
387
388 prefix_ void senf::log::detail::TargetRegistry::registerTarget(Target * target,
389                                                                std::string const & name)
390 {
391     targets_.insert(target);
392     consoleDir_().add(name, target->consoleDir_());
393 }
394
395 prefix_ void senf::log::detail::TargetRegistry::write(StreamBase const & stream,
396                                                       AreaBase const & area, unsigned level,
397                                                       std::string const & msg)
398 {
399     if (fallbackRouting_) {
400         if (level >= stream.defaultRuntimeLimit())
401             static_cast<Target &>(ConsoleTarget::instance()).v_write(
402                 TimeSource::now(), stream.v_name(), area.v_name(), level, msg );
403     }
404     else
405         area.write( TimeSource::now(), stream, level, msg );
406 }
407 ///////////////////////////////////////////////////////////////////////////
408 // namespace members
409
410 prefix_ std::ostream & senf::log::operator<<(std::ostream & os, senf::log::Target::action_t const & action)
411 {
412     if( action == Target::ACCEPT) os << "ACCEPT";
413     else if( action == Target::REJECT) os << "REJECT";
414     else os << "unknown action";
415     return os;
416 }
417
418
419 ///////////////////////////////cc.e////////////////////////////////////////
420 #undef prefix_
421 //#include "Target.mpp"
422
423
424 // Local Variables:
425 // mode: c++
426 // fill-column: 100
427 // comment-column: 40
428 // c-file-style: "senf"
429 // indent-tabs-mode: nil
430 // ispell-local-dictionary: "american"
431 // compile-command: "scons -u test"
432 // End: