Utils/Logger: Add console directory to target
[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", "stream to match or empty to match any stream",
92              kw::default_value="")
93         .arg("area", "area to match or empty to match any area",
94              kw::default_value="")
95         .arg("level", "log level, one of: VERBOSE, NOTICE, MESSAGE, IMPORTANT, CRITICAL, FATAL",
96              kw::default_value=local::VERBOSE)
97         .arg("action", "routing action, one of: ACCEPT, REJECT",
98              kw::default_value=ACCEPT)
99         .arg("index", "index at which to insert new rule",
100              kw::default_value=-1)
101         .doc("Add routing entry. Log messages are matched against the routing table beginning\n"
102              "with the first entry. The action of the first matching entry determines the\n"
103              "handling of the message.\n"
104              "\n"
105              "Examples:\n"
106              "\n"
107              "    route\n"
108              "        route all messages to this target.\n"
109              "\n"
110              "    route \"\" my::Class\n"
111              "        route all messages which are in the my::Class area.\n"
112              "\n"
113              "    route senf::log::Debug \"\" VERBOSE REJECT\n"
114              "    route \"\" \"\" VERBOSE\n"
115              "        route all messages not in the senf::log::Debug stream to the current area.\n"
116              "\n"
117              "The additional optional index argument identifies the position in the routing table\n"
118              "where the new routing entry will be added. Positive numbers count from the\n"
119              "beginning, 0 being the first routing entry. Negative values count from the end.\n");
120     consoleDir_().add("unroute",
121                       senf::membind(static_cast<void (Target::*)(int)>(&Target::unroute), this))
122         .arg("index", "index of routing entry to remove")
123         .overloadDoc("Remove routing entry with the given index");
124     consoleDir_().add("unroute", 
125                       boost::function<void (std::string const &, std::string const &,
126                                             local::Level, action_t)>(
127                                                 senf::membind(
128                                                     static_cast<void (Target::*)(
129                                                         std::string const &, std::string const &,
130                                                         unsigned, action_t)>(&Target::unroute),
131                                                     this)))
132         .arg("stream", "stream to match or empty to match any stream",
133              kw::default_value="")
134         .arg("area", "area to match or empty to match any area",
135              kw::default_value="")
136         .arg("level", "log level, one of: VERBOSE, NOTICE, MESSAGE, IMPORTANT, CRITICAL, FATAL",
137              kw::default_value=local::VERBOSE)
138         .arg("action", "routing action, one of: ACCEPT, REJECT",
139              kw::default_value=ACCEPT)
140         .overloadDoc("Remove the routing entry matching the specified arguments.");
141     consoleDir_().add("flush", senf::membind(&Target::flush, this))
142         .doc("Remove all routing entries clearing the routing table. This will disable all\n"
143              "logging output on this target.");
144 }
145
146 prefix_ senf::log::Target::~Target()
147 {
148     while( ! rib_.empty()) {
149         // This is slow but simplifies the area cache handling and removing a target should be
150         // relatively seldom
151         RIB::reverse_iterator i (rib_.rbegin());
152         unroute(i->stream_, i->area_, i->level_, i->action_);
153     }
154     detail::TargetRegistry::instance().unregisterTarget(this);
155 }
156
157 prefix_ void senf::log::Target::route(std::string const & stream, std::string const & area,
158                                       unsigned level, action_t action, int index)
159 {
160     detail::StreamBase const * s (0);
161     if (! stream.empty()) {
162         s = StreamRegistry::instance().lookup(stream);
163         if (!s)
164             throw InvalidStreamException();
165     }
166     detail::AreaBase const * a (0);
167     if (! area.empty()) {
168         a = AreaRegistry::instance().lookup(area);
169         if (!a)
170             throw InvalidAreaException();
171     }
172     route(s, a, level, action, index);
173 }
174
175 prefix_ void senf::log::Target::unroute(std::string const & stream, std::string const & area,
176                                         unsigned level, action_t action)
177 {
178     detail::StreamBase const * s (0);
179     if (! stream.empty()) {
180         s = StreamRegistry::instance().lookup(stream);
181         if (!s)
182             throw InvalidStreamException();
183     }
184     detail::AreaBase const * a (0);
185     if (! area.empty()) {
186         a = AreaRegistry::instance().lookup(area);
187         if (!a)
188             throw InvalidAreaException();
189     }
190     unroute(s, a, level, action);
191 }
192
193 prefix_ void senf::log::Target::unroute(int index)
194 {
195     if (rib_.empty())
196         return;
197     RIB::iterator i;
198     if (index < 0) {
199         if (RIB::size_type(-index) >= rib_.size())
200             i = rib_.begin();
201         else {
202             i = rib_.end();
203             std::advance(i, index);
204         }
205     } else {
206         if (RIB::size_type(index+1) >= rib_.size()) {
207             i = rib_.end();
208             --i;
209         } else {
210             i = rib_.begin();
211             std::advance(i, index);
212         }
213     }
214     if (i == rib_.end())
215         return;
216     RoutingEntry entry (*i);
217     rib_.erase(i);
218     if (entry.action_ == ACCEPT)
219         updateRoutingCache(entry.stream_, entry.area_);
220 }
221
222 prefix_ void senf::log::Target::flush()
223 {
224     RIB old;
225     rib_.swap(old);
226     RIB::const_iterator i (old.begin());
227     RIB::const_iterator const i_end (old.end());
228     for (; i != i_end; ++i)
229         if (i->action_ == ACCEPT)
230             updateRoutingCache(i->stream_, i->area_);
231 }
232
233 ////////////////////////////////////////
234 // protected members
235
236 prefix_ senf::log::detail::TargetRegistry::TargetRegistry()
237     : fallbackRouting_(true)
238 {
239     console::sysdir().add("log", consoleDir_());
240 }
241
242 ////////////////////////////////////////
243 // private members
244
245 prefix_ void senf::log::Target::route(detail::StreamBase const * stream,
246                                       detail::AreaBase const * area, unsigned level,
247                                       action_t action, int index)
248 {
249     RIB::iterator i;
250     if (index < 0) {
251         if (RIB::size_type(-index-1) >= rib_.size())
252             i = rib_.begin();
253         else {
254             i = rib_.end();
255             std::advance(i, index + 1 );
256         }
257     } else {
258         if (RIB::size_type(index) >= rib_.size())
259             i = rib_.end();
260         else {
261             i = rib_.begin();
262             std::advance(i, index);
263         }
264     }
265     rib_.insert(i, RoutingEntry(stream, area, level, action));
266     if (action == ACCEPT)
267         updateRoutingCache(stream, area);
268     // This disables the initial fallback routing
269     detail::TargetRegistry::instance().routed();
270 }
271
272 prefix_ void senf::log::Target::unroute(detail::StreamBase const * stream,
273                                         detail::AreaBase const * area, unsigned level,
274                                         action_t action)
275 {
276     RIB::iterator i = std::find(rib_.begin(), rib_.end(),
277                                 RoutingEntry(stream, area, level, action));
278     if (i != rib_.end())
279         unroute(std::distance(rib_.begin(), i));
280 }
281
282 prefix_ void senf::log::Target::updateRoutingCache(detail::StreamBase const * stream,
283                                                    detail::AreaBase const * area)
284 {
285     if (! stream) {
286         StreamRegistry::Registry::iterator i (StreamRegistry::instance().registry_.begin());
287         StreamRegistry::Registry::iterator const i_end (StreamRegistry::instance().registry_.end());
288         for (; i != i_end ; ++i)
289             updateRoutingCache(i->second, area);
290         return;
291     }
292     if (! area) {
293         AreaRegistry::Registry::iterator i (AreaRegistry::instance().registry_.begin());
294         AreaRegistry::Registry::iterator const i_end (AreaRegistry::instance().registry_.end());
295         for (; i != i_end ; ++i)
296             updateRoutingCache(stream, i->second);
297         return;
298     }
299     unsigned limit (DISABLED::value);
300     RIB::iterator i (rib_.begin());
301     RIB::iterator const i_end (rib_.end());
302     for(; i != i_end; ++i)
303         if ( (! i->stream_ || i->stream_ == stream) &&
304              (! i->area_ || i->area_ == area) &&
305              i->action_ == ACCEPT ) {
306             unsigned l (i->level_ == NONE::value ? stream->defaultRuntimeLimit() : i->level_);
307             if (l < limit)
308                 limit = l;
309         }
310     if (limit == DISABLED::value)
311         area->removeRoutingCache(*this, *stream);
312     else
313         area->updateRoutingCache(*this, *stream, limit);
314 }
315
316 prefix_ void senf::log::Target::write(time_type timestamp,
317                                       detail::StreamBase const & stream,
318                                       detail::AreaBase const & area, unsigned level,
319                                       std::string const & message)
320 {
321     RIB::iterator i (rib_.begin());
322     RIB::iterator const i_end (rib_.end());
323     for (; i != i_end; ++i)
324         if ( (! i->stream_ || i->stream_ == &stream) &&
325              (! i->area_ || i->area_ == &area) &&
326              (i->level_ == NONE::value ? stream.defaultRuntimeLimit() : i->level_) <= level ) {
327             if (i->action_ == ACCEPT)
328                 v_write(timestamp, stream.v_name(), area.v_name(), level, message);
329             return;
330         }
331 }
332
333 namespace {
334     std::string formatLabel(std::string const & l)
335     {
336         if (l.empty())
337             return "-";
338         if (l.size() > 29)
339             return l.substr(l.size()-29);
340         return l;
341     }
342 }
343
344 prefix_ void senf::log::Target::consoleList(std::ostream & os)
345 {
346     static char const * levels[] = { 
347         "none", "verbose", "notice", "message", "important", "critical", "fatal", "disabled" };
348
349     boost::format fmt ("%2d %-29s %-29s %-9s %-6s\n");
350     os << fmt % "#" % "STREAM" % "AREA" % "LEVEL" % "ACTION";
351     unsigned n (0);
352     for (iterator i (begin()); i != end(); ++i, ++n)
353         os << fmt 
354             % n 
355             % formatLabel(i->stream())
356             % formatLabel(i->area())
357             % levels[i->level()]
358             % (i->action() == ACCEPT ? "accept" : "reject");
359 }
360
361 ///////////////////////////////////////////////////////////////////////////
362 // senf::log::detail::TargetRegistry
363
364 prefix_ void senf::log::detail::TargetRegistry::registerTarget(Target * target,
365                                                                std::string const & name)
366 {
367     targets_.insert(target);
368     consoleDir_().add(name, target->consoleDir_());
369 }
370
371 prefix_ void senf::log::detail::TargetRegistry::write(StreamBase const & stream,
372                                                       AreaBase const & area, unsigned level,
373                                                       std::string const & msg)
374 {
375     if (fallbackRouting_) {
376         if (level >= stream.defaultRuntimeLimit())
377             static_cast<Target &>(ConsoleTarget::instance()).v_write(
378                 TimeSource::now(), stream.v_name(), area.v_name(), level, msg );
379     }
380     else
381         area.write( TimeSource::now(), stream, level, msg );
382 }
383 ///////////////////////////////////////////////////////////////////////////
384 // namespace members
385
386 prefix_ std::ostream & senf::log::operator<<(std::ostream & os, senf::log::Target::action_t const & action)
387 {
388     if( action == Target::ACCEPT) os << "ACCEPT";
389     else if( action == Target::REJECT) os << "REJECT";
390     else os << "unknown action";
391     return os;
392 }
393
394
395 ///////////////////////////////cc.e////////////////////////////////////////
396 #undef prefix_
397 //#include "Target.mpp"
398
399
400 // Local Variables:
401 // mode: c++
402 // fill-column: 100
403 // comment-column: 40
404 // c-file-style: "senf"
405 // indent-tabs-mode: nil
406 // ispell-local-dictionary: "american"
407 // compile-command: "scons -u test"
408 // End: