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, <); 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, <); 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