1 /*
2     KHOMP generic endpoint/channel library.
3     Copyright (C) 2007-2009 Khomp Ind. & Com.
4 
5   The contents of this file are subject to the Mozilla Public License Version 1.1
6   (the "License"); you may not use this file except in compliance with the
7   License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
8 
9   Software distributed under the License is distributed on an "AS IS" basis,
10   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
11   the specific language governing rights and limitations under the License.
12 
13   Alternatively, the contents of this file may be used under the terms of the
14   "GNU Lesser General Public License 2.1" license (the “LGPL" License), in which
15   case the provisions of "LGPL License" are applicable instead of those above.
16 
17   If you wish to allow use of your version of this file only under the terms of
18   the LGPL License and not to allow others to use your version of this file under
19   the MPL, indicate your decision by deleting the provisions above and replace them
20   with the notice and other provisions required by the LGPL License. If you do not
21   delete the provisions above, a recipient may use your version of this file under
22   either the MPL or the LGPL License.
23 
24   The LGPL header follows below:
25 
26     This library is free software; you can redistribute it and/or
27     modify it under the terms of the GNU Lesser General Public
28     License as published by the Free Software Foundation; either
29     version 2.1 of the License, or (at your option) any later version.
30 
31     This library is distributed in the hope that it will be useful,
32     but WITHOUT ANY WARRANTY; without even the implied warranty of
33     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
34     Lesser General Public License for more details.
35 
36     You should have received a copy of the GNU Lesser General Public License
37     along with this library; if not, write to the Free Software Foundation, Inc.,
38     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
39 
40 */
41 
42 #include <stdio.h>
43 #include <sys/time.h>
44 #include <time.h>
45 
46 #include <map>
47 #include <string>
48 #include <iostream>
49 
50 #include <tagged_union.hpp>
51 #include <format.hpp>
52 #include <refcounter.hpp>
53 #include <flagger.hpp>
54 
55 #if defined(COMMONS_LIBRARY_USING_ASTERISK) || defined(COMMONS_LIBRARY_USING_FREESWITCH)
56 extern "C"
57 {
58     #include <time.h>
59 }
60 #elif defined(COMMONS_LIBRARY_USING_CALLWEAVER)
61 extern "C"
62 {
63     #include <callweaver/localtime.h>
64 }
65 #endif
66 /*
67 
68 ********************************************************************************
69 ***************************** 'Logger' user manual *****************************
70 ********************************************************************************
71 
72 * Description:
73 
74 This class does the management of log messages for applications. It works with
75 the following axioms:
76 
77 <*> There are several class of messages.
78 <*> There are some outputs, which may be files, sockets, or a console device.
79 <*> There are options for classes, for outputs and for the association of both.
80 
81 The last rule also shows the order in which options are processed: first the
82 'classes' options are processed, then 'output' options are processed, and then
83 the options for the tuple '(class, output)' are processed.
84 
85 The options are mapped like this:
86 
87   <class-of-message>                  -> options [prefix, flags]
88   <output-sink>                       -> options [prefix]
89 ( <class-of-message>, <output-sink> ) -> options [prefix, flags]
90 
91  - "prefix" means a fixed string prefix before the real message.
92  - "flags" means auxiliary flags (DATETIME, THREADID) which are
93    used to add information based on OS or process context info.
94 
95 * Example of use:
96 
97 typedef enum
98 {
99     C_DBG_FUNC,
100     C_DBG_LOCK,
101     C_WARNING,
102     C_ERROR,
103     C_CLI,
104 }
105 AstClassId;
106 
107 typedef enum
108 {
109     F_CONSOLE,
110     F_GENERIC,
111     F_TRACE,
112 }
113 AstOutputId;
114 
115 // used to indicate the console log //
116 struct AstConsoleLog {};
117 
118 struct AstPrinter: public Logger::DefaultPrinter
119 {
120     typedef Logger::DefaultPrinter Super;
121 
122     using namespace Tagged;
123 
124     using Super::operator()(int);
125 
126     // just 2 type of descriptors //
127     typedef Union < int, Union < AstConsoleLog > > Container;
128 
129     ast_printer(std::string & msg): Super(msg) {};
130 
131     bool operator()(const AstConsoleLog & log)
132     {
133         ast_console_puts(_msg.c_str());
134         return true;
135     };
136 
137 #if 0
138     bool operator()(int log)
139     {
140         return Super::operator()(log);
141     };
142 #endif
143 };
144 
145 bool start_log()
146 {
147     typedef Logger::Manager<AstClassId, AstOutputId, AstPrinter, SimpleLock> LogManager;
148 
149     LogManager logger;
150 
151     // shortcut definition //
152     typedef LogManager::Option LogOption;
153 
154     FILE * log_file = fopen( "output.log", "a");
155 
156     if (!log_file)
157         return false;
158 
159     logger.add( F_CONSOLE, AstConsoleLog(), "chan_khomp: ");
160     logger.add( F_GENERIC, log_file);
161 
162     logger.classe( C_WARNING )
163         & LogOption(F_CONSOLE, "WARNING: ", LogOption::Flags(LogOption::Flag(LogOption::DATETIME)))
164         & LogOption(F_GENERIC, "W: ", LogOption::Flags(LogOption::Flag(LogOption::DATETIME)))
165 
166     logger.classe( C_DBG_LOCK ).enabled(false);
167 
168     logger.classe( C_DBG_LOCK )
169         & LogOption(F_GENERIC, "L: ", LogOption::Flags
170             (LogOption::flag_type(LogOption::ENABLED) &
171              LogOption::flag_type(LogOption::DATETIME))
172 
173     logger(C_WARNING, "eu sou uma mensagem de warning");
174 
175     logger.classe(C_WARNING).set(F_GENERIC, LogOption::ENABLED, true);
176     logger.classe(C_WARNING).set(F_CONSOLE, LogOption::ENABLED, false);
177 
178     logger.classe(C_CLI).prefix("<K>");
179 
180     return true;
181 }
182 
183 void message_the_user(int fd)
184 {
185     logger(C_CLI, fd, "eu sou uma mensagem de cli!");
186     logger(C_WARNING, "eu sou um warning");
187 }
188 
189 ********************************************************************************
190 ********************************************************************************
191 
192 Now, the code..!
193 
194 */
195 
196 #ifndef _LOGGER_HPP_
197 #define _LOGGER_HPP_
198 
199 #include <tagged_union.hpp>
200 
201 struct Logger
202 {
203     /*** a base struct for printing messages in many ways ***/
204 
205     struct DefaultPrinter
206     {
207         typedef Tagged::Union < int, Tagged::Union < FILE *, Tagged::Union < std::ostream * > > >  BaseType;
208 
209         typedef bool ReturnType;
210 
DefaultPrinterLogger::DefaultPrinter211         DefaultPrinter(std::string & msg): _msg(msg) {};
212 
operator ()Logger::DefaultPrinter213         bool operator()(std::ostream * out)
214         {
215             (*out) << _msg;
216             out->flush();
217 
218             return out->good();
219         }
220 
operator ()Logger::DefaultPrinter221         bool operator()(FILE * out)
222         {
223             if (fputs(_msg.c_str(), out) < 0)
224                 return false;
225 
226             if (fflush(out) < 0)
227                 return false;
228 
229             return true;
230         }
231 
operator ()Logger::DefaultPrinter232         bool operator()(int out)
233         {
234 #ifndef KWIN32
235             return (write(out, _msg.c_str(), _msg.size()) == (int)_msg.size());
236 #else
237             // no need for file descriptors on windows
238             return false;
239 #endif
240         }
241 
msgLogger::DefaultPrinter242         std::string & msg() { return _msg; }
243 
244      protected:
245         std::string & _msg;
246     };
247 
248     /*** manage the printing of messages ***/
249 
250     template <class ClassId, class OutputId, class Printer, class LockType>
251     struct Manager
252     {
253         typedef  typename Printer::BaseType       BaseType;
254 
255      protected:
256          /* holds a stream, and an optimal message prefix */
257         struct OutputOptions
258         {
OutputOptionsLogger::Manager::OutputOptions259             OutputOptions(BaseType & stream, std::string & prefix)
260             : _stream(stream), _prefix(prefix) {};
261 
262             BaseType     _stream;
263             std::string  _prefix;
264             LockType     _lock;
265         };
266 
267         typedef  std::map < OutputId, OutputOptions > OutputMap;
268 
269      public:
270 
271         /* print in a specific 'message class' */
272         struct ClassType
273         {
ClassTypeLogger::Manager::ClassType274             ClassType(void)
275             : _enabled(true)
276             {};
277 
278 //            ClassType(ClassType & o)
279 //            : _stream_map(o._stream_map), _prefix(o.prefix),
280 //              _lock(o._lock),_enabled(o._enabled)
281 //            {};
282 
283             /* initializes the options of the (class, stream) pair */
284             struct Option
285             {
286                 typedef enum { ENABLED, DATETIME, THREADID, DATETIMEMS } EnumType;
287 
288                 typedef Flagger< EnumType >             Flags;
289                 typedef typename Flags::InitFlags   InitFlags;
290 
OptionLogger::Manager::ClassType::Option291                 Option(OutputId output, const char * prefix,
292                        Flags flags = InitFlags(ENABLED))
293                 : _output(output), _prefix(prefix), _flags(flags) {};
294 
OptionLogger::Manager::ClassType::Option295                 Option(OutputId output, std::string prefix,
296                        Flags flags = InitFlags(ENABLED))
297                 : _output(output), _prefix(prefix), _flags(flags) {};
298 
OptionLogger::Manager::ClassType::Option299                 Option(OutputId output,
300                     Flags flags = InitFlags(ENABLED))
301                 : _output(output), _flags(flags) {};
302 
303                 OutputId     _output;
304                 std::string  _prefix;
305                 Flags        _flags;
306             };
307 
308          protected:
309 
310              /* holds a prefix and a activation status */
311             struct OptionContainer
312             {
OptionContainerLogger::Manager::ClassType::OptionContainer313                 OptionContainer(std::string prefix, typename Option::Flags flags)
314                 : _prefix(prefix), _flags(flags) {};
315 
316                 std::string            _prefix;
317                 typename Option::Flags _flags;
318             };
319 
320             typedef std::multimap < OutputId, OptionContainer > OptionMap;
321 
322             /* utility function for printing */
printLogger::Manager::ClassType323             bool print(std::string & msg, BaseType & stream, LockType & lock)
324             {
325                 lock.lock();
326 
327                 Printer p(msg);
328                 bool ret = stream.visit(p);
329 
330                 lock.unlock();
331 
332                 return ret;
333             };
334 
335 /*
336             bool print(std::string & msg, BaseType & stream, LockType & lock)
337             {
338                 lock.lock();
339 
340                 Printer p(msg);
341                 bool ret = stream.visit(p);
342 
343                 lock.unlock();
344 
345                 return ret;
346             };
347 */
348 
349          public:
operator &Logger::Manager::ClassType350             ClassType & operator&(const Option & value)
351             {
352                 add(value._output, value._prefix, value._flags);
353                 return *this;
354             }
355 
addLogger::Manager::ClassType356             void add(OutputId output_id, std::string prefix,
357                 typename Option::Flags      flags)
358             {
359                 typedef std::pair < OutputId, OptionContainer > pair_type;
360                 _stream_map.insert(pair_type(output_id, OptionContainer(prefix, flags)));
361             }
362 
363             /* get and set methods for active mode */
setLogger::Manager::ClassType364             void set(OutputId id, typename Option::EnumType flag, bool value = true)
365             {
366                 typename OptionMap::iterator iter = _stream_map.find(id);
367 
368                 if (iter == _stream_map.end())
369                     return;
370 
371                 (*iter).second._flags.set(flag, value);
372             }
373 
getLogger::Manager::ClassType374             bool get(OutputId idx, typename Option::EnumType flag)
375             {
376                 typename OptionMap::iterator iter = _stream_map.find(idx);
377 
378                 if (iter == _stream_map.end())
379                     return false;
380 
381                 return (*iter).second._flags.is_set(flag);
382             }
383 
384             /* get/adjust the enable/disable value for the class */
enabledLogger::Manager::ClassType385             void  enabled(bool enabled) { _enabled = enabled; };
enabledLogger::Manager::ClassType386             bool  enabled()             { return _enabled;    };
387 
388             /* get/adjust the classe prefix */
prefixLogger::Manager::ClassType389             void          prefix(const char  * prefix) { _prefix = prefix; }
prefixLogger::Manager::ClassType390             void          prefix(std::string & prefix) { _prefix = prefix; }
prefixLogger::Manager::ClassType391             std::string & prefix()                     { return _prefix;   }
392 
393             /* printing function (operator, actually) */
operator ()Logger::Manager::ClassType394             bool operator()(OutputMap & out_map, std::string & msg)
395             {
396                 if (!_enabled)
397                     return true;
398 
399                 typedef typename OptionMap::iterator Iter;
400 
401                 bool ret = true;
402 
403                 for (Iter iter = _stream_map.begin(); iter != _stream_map.end(); iter++)
404                 {
405                     OptionContainer & opt = (*iter).second;
406 
407                     if (!opt._flags[Option::ENABLED])
408                         continue;
409 
410                     typename OutputMap::iterator out_iter = out_map.find((*iter).first);
411 
412                     /* this stream have been added already? if not, skip! */
413                     if (out_iter == out_map.end())
414                         continue;
415 
416                     /* final message */
417                     std::string out_msg;
418 
419                     if (opt._flags[Option::DATETIME])
420                     {
421 #if defined(COMMONS_LIBRARY_USING_ASTERISK) || defined(COMMONS_LIBRARY_USING_CALLWEAVER) || defined(COMMONS_LIBRARY_USING_FREESWITCH)
422                         time_t      tv;
423                         struct tm   lt;
424 
425                         time (&tv);
426                         localtime_r (&tv, &lt);
427 
428                         out_msg += STG(FMT("[%02d-%02d-%02d %02d:%02d:%02d] ")
429                             % (lt.tm_year % 100) % (lt.tm_mon + 1) % lt.tm_mday % lt.tm_hour
430                             % lt.tm_min % lt.tm_sec);
431 #endif
432                     }
433 
434                     if (opt._flags[Option::DATETIMEMS])
435                     {
436 #if defined(COMMONS_LIBRARY_USING_ASTERISK) || defined(COMMONS_LIBRARY_USING_CALLWEAVER) || defined(COMMONS_LIBRARY_USING_FREESWITCH)
437                         time_t      tv;
438                         struct tm   lt;
439 
440                         time (&tv);
441                         localtime_r (&tv, &lt);
442 
443                         out_msg += STG(FMT("[%02d-%02d-%02d %02d:%02d:%02d:%04d] ")
444                             % (lt.tm_year % 100) % (lt.tm_mon + 1) % lt.tm_mday % lt.tm_hour % lt.tm_min
445                             % lt.tm_sec % (tv * 1000));
446 #endif
447                     }
448 
449                     OutputOptions & out_opt = (*out_iter).second;
450 
451                     if (opt._flags[Option::THREADID])
452                     {
453 #if defined (COMMONS_LIBRARY_USING_ASTERISK) || defined(COMMONS_LIBRARY_USING_CALLWEAVER) || defined(COMMONS_LIBRARY_USING_FREESWITCH)
454                         out_msg += STG(FMT("%08x ") % ((unsigned long)pthread_self()));
455 #endif
456                     }
457 
458                     out_msg += _prefix;
459                     out_msg += out_opt._prefix;
460                     out_msg += opt._prefix;
461                     out_msg += msg;
462                     out_msg += "\n";
463 
464                     ret |= print(out_msg, out_opt._stream, out_opt._lock);
465                 }
466 
467                 return ret;
468             }
469 
operator ()Logger::Manager::ClassType470             bool operator()(BaseType & stream, std::string & msg)
471             {
472                 std::string final_msg;
473 
474                 final_msg += _prefix;
475                 final_msg += msg;
476                 final_msg += "\n";
477 
478                 return print(final_msg, stream, _lock);
479             }
480 
481          protected:
482             OptionMap    _stream_map;
483             std::string  _prefix;
484             LockType     _lock;
485             bool         _enabled;
486         };
487 
488         /* util declaration */
489         typedef typename ClassType::Option  Option;
490 
491         /* class_id_type -> ClassType mapper */
492         typedef std::map < ClassId, ClassType >  ClassMap;
493 
494         /* local option pair */
495         typedef std::pair < OutputId, OutputOptions >  OutputOptionPair;
496 
addLogger::Manager497         void add(OutputId output, BaseType stream, const char * prefix = "")
498         {
499             std::string str_prefix(prefix);
500 
501             _output_map.insert(OutputOptionPair(output, OutputOptions(stream, str_prefix)));
502         }
503 
addLogger::Manager504         void add(OutputId output, BaseType stream, std::string prefix)
505         {
506             _output_map.insert(OutputOptionPair(output, OutputOptions(stream, prefix)));
507         }
508 
classeLogger::Manager509         ClassType & classe(ClassId classeid)
510         {
511             return _classe_map[classeid];
512         }
513 
operator ()Logger::Manager514         bool operator()(ClassId classeid, const char * msg)
515         {
516             std::string str_msg(msg);
517             return _classe_map[classeid](_output_map, str_msg);
518         }
519 
operator ()Logger::Manager520         bool operator()(ClassId classeid, std::string & msg)
521         {
522             return _classe_map[classeid](_output_map, msg);
523         }
524 
operator ()Logger::Manager525         bool operator()(ClassId classeid, Format fmt)
526         {
527             std::string str_fmt = STG(fmt);
528             return _classe_map[classeid](_output_map, str_fmt);
529         }
530 
operator ()Logger::Manager531         bool operator()(ClassId classeid, BaseType stream, const char * msg)
532         {
533             std::string str_msg(msg);
534             return _classe_map[classeid](stream, str_msg);
535         }
536 
operator ()Logger::Manager537         bool operator()(ClassId classeid, BaseType stream, std::string & msg)
538         {
539             return _classe_map[classeid](stream, msg);
540         }
541 
operator ()Logger::Manager542         bool operator()(ClassId classeid, BaseType stream, Format fmt)
543         {
544             std::string str_fmt = STG(fmt);
545             return _classe_map[classeid](stream, str_fmt);
546         }
547 
548      protected:
549         ClassMap   _classe_map;
550         OutputMap  _output_map;
551     };
552 
553  private:
554     Logger();
555 };
556 
557 #endif /* _LOGGER_HPP_ */
558