8b1ce4ffce4fb07ddff65c5c8339df98faf22e36
[libi2ncommon] / src / logfunc.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20 /** @file
21  * @brief implementaton of logging functions.
22  *
23  * @copyright © Copyright 2007-2008 by Intra2net AG
24  * 
25  * @note Don't call loggers from global constructed objects
26  * as this depends on the global object construction sequence.
27  */
28
29 #include "logfunc.hpp"
30
31 #include <syslog.h>
32 #include <cstdlib>
33 #include <ctime>
34 #include <algorithm>
35 #include <iostream>
36 #include <fstream>
37 #include <unistd.h>
38 #include <string.h>
39 #include <boost/shared_ptr.hpp>
40 #include <boost/thread/recursive_mutex.hpp>
41 #include <boost/thread/thread.hpp>
42
43 #include <stringfunc.hxx>
44 #include <filefunc.hxx>
45
46
47 namespace I2n
48 {
49 namespace Logger
50 {
51
52 /**
53  * @brief the global logger instance.
54  *
55  * This may be used in all cases wheer a specialized logger is not available
56  * or shouldn't be used for some reason.
57  */
58 PartLogger GlobalLogger("");
59
60
61 namespace
62 {
63
64 /// Global lock for the logger. Used during all syslog operations
65 /// and modification of our shared local variables.
66 static boost::recursive_mutex LoggerLock;
67
68 /*
69 ** local globals:
70 */
71
72 static std::string g_ident;
73 static Facility    g_facility;
74 static bool        g_syslog_opened = false;
75 static int         g_max_level= LogLevel::Warning;
76
77 static bool g_stderr_log = false;
78
79 static std::string g_log_file_name;
80 static boost::shared_ptr< std::ofstream > g_log_stream_ptr;
81
82 /**
83  * @brief lookup array for translating our log levels to syslog level.
84  */
85 static int loglevel_2_syslog_level[ LogLevel::_LogLevel_END ] = {
86     LOG_EMERG,
87     LOG_ALERT,
88     LOG_CRIT,
89     LOG_ERR,
90     LOG_WARNING,
91     LOG_NOTICE,
92     LOG_INFO,
93     LOG_DEBUG
94 };
95
96
97 /**
98  * @brief lookup array for translating our log levels to a short (4 character) tag.
99  *
100  * These tags are used when logs are written to stderr or into a log file.
101  */
102 static std::string loglevel_2_short_tag[ LogLevel::_LogLevel_END ] = {
103     "EMRG",
104     "ALRT",
105     "CRIT",
106     "ERR ",
107     "WARN",
108     "NOTE",
109     "INFO",
110     "DBUG"
111 };
112
113
114 /**
115  * @brief a copy of the current identifier used for syslog.
116  *
117  * Keeping a copy of this ident is necessary since openlog doen't copy it's first
118  * argument but copies only the pointer! (what a **censored**!)
119  */
120 static char* syslog_ident= NULL;
121
122
123 /*
124 ** functions:
125 */
126
127 /**
128  * @brief close syslog.
129  */
130 void close_syslog()
131 {
132     boost::recursive_mutex::scoped_lock lock(LoggerLock);
133
134     if (g_syslog_opened)
135     {
136         closelog();
137         if (syslog_ident)
138         {
139             free(syslog_ident);
140             syslog_ident= NULL;
141         }
142         g_syslog_opened= false;
143     }
144 } // eo close_syslog()
145
146
147 /**
148  * @brief open syslog.
149  */
150 void open_syslog()
151 {
152     boost::recursive_mutex::scoped_lock lock(LoggerLock);
153
154     close_syslog();
155     syslog_ident= strdup(g_ident.c_str());
156     openlog( syslog_ident, LOG_CONS|LOG_PID, g_facility);
157     g_syslog_opened= true;
158 } // eo open_syslog()
159
160
161 /**
162  * @brief get syslog level from internal log level.
163  * @param level log level.
164  * @return syslog level
165  */
166 int get_syslog_level( int level )
167 {
168     // Note: Thread safe
169
170     if (level >=0 &&  level < LogLevel::_LogLevel_END)
171     {
172         return loglevel_2_syslog_level[level];
173     }
174     return (level<0) ? (LOG_EMERG) : (LOG_DEBUG);
175 } // eo get_syslog_level(int)
176
177
178 /**
179  * @brief get a short tag for the log level (/ message type)
180  * @param level the log level
181  * @return a short tag for the level.
182  */
183 std::string get_level_tag( int level )
184 {
185     // Note: Thread safe
186
187     if (level >=0 &&  level < LogLevel::_LogLevel_END)
188     {
189         return loglevel_2_short_tag[level];
190     }
191     return (level<0) ? loglevel_2_short_tag[0] : loglevel_2_short_tag[ LogLevel::_LogLevel_END -1 ];
192 } // eo get_level_tag(int)
193
194
195 /**
196  * @brief the "real" log function which logs a message at a given level.
197  * @param level the log level to log the message at.
198  * @param msg the message.
199  *
200  * Write the message to every enabled log channel.
201  *
202  * If syslog is enabled the message is passed unmodified to syslog.
203  *
204  * If a stream log is enabled (stderr or file) then the message is prepended with date, time
205  * and process information (like syslog does). The message is splitted at line ends and
206  * consecutive lines are indented.
207  */
208 void log_msg( int level, const std::string& msg)
209 {
210     boost::recursive_mutex::scoped_lock lock(LoggerLock);
211
212     if (not g_syslog_opened and not g_stderr_log and not g_log_stream_ptr)
213     {
214         // if nothing is opened for logging: we activate syslog!
215         enable_syslog(true);
216     }
217
218     if (g_syslog_opened)
219     {
220         ::syslog( get_syslog_level(level), "%s", msg.c_str());
221     }
222     // file(/stream) logging:
223     if (g_stderr_log or g_log_stream_ptr) // add more log "enabled" expressions here...
224     {
225         // here we need must do something more with the msg...
226         std::string new_msg;
227         std::string prefix;
228         {
229             std::ostringstream ostr;
230             // add time stamp (syslog like: "Mon DD HH:MM:SS") :
231             {
232                 time_t t = time(NULL);
233                 char buffer[32];
234                 std::strftime(buffer, sizeof(buffer),"%b %d %H:%M:%S ", std::localtime(&t));
235                 ostr << buffer;
236             }
237             ostr << get_level_tag(level) << " ";
238             ostr << g_ident << "[" << getpid() << "]: ";
239             prefix= ostr.str();
240         }
241         {
242             {
243                 std::string indent_string(prefix.size(), ' ');
244                 std::list< std::string > parts;
245                 split_string( chomp(msg,"\n"), parts, "\n");
246                 std::ostringstream ostr;
247                 ostr << prefix;
248                 for(std::list< std::string >::const_iterator it= parts.begin();
249                     it != parts.end();
250                     ++it)
251                 {
252                     if (it != parts.begin())
253                     {
254                         ostr << indent_string;
255                     }
256                     ostr << *it << std::endl;
257                 }
258                 new_msg= ostr.str();
259             }
260         }
261         if (g_stderr_log)
262         {
263             std::cerr << new_msg;
264         }
265         if (g_log_stream_ptr)
266         {
267             *g_log_stream_ptr << new_msg << std::flush;
268         }
269     }
270 } // eo log_msg
271
272
273 /**
274  * @brief "real" log function for part messages.
275  * @param level the log level.
276  * @param part the part(/module) name(/id)
277  * @param msg the log message.
278  *
279  * basically calls @a log(), but prepends the part (if not empty) in square brackets to the message.
280  */
281 void log_part_msg(
282     int level,
283     const std::string& part,
284     const std::string& msg)
285 {
286     // Note: Locking is done in log_msg()
287
288     if (!part.empty())
289     {
290         std::ostringstream ostr;
291         ostr << "[" << part << "] " << msg;
292         log_msg(level, ostr.str());
293     }
294     else
295     {
296         log_msg(level, msg);
297     }
298 } // eo log_part_msg(int,const std::string&,const std::string&)
299
300
301 /**
302  * @brief returns the name of the program (/binary)
303  * @return the program name if it could be determined; generated name else.
304  *
305  * Tries to determine the name of the binary.
306  *
307  * If no name could be determined, one is built.
308  */
309 std::string get_program_name()
310 {
311     // Note: Thread safe
312
313     std::string result;
314     // determine the program name:
315     {
316         // try to determine the name using the exe link:
317         std::string exe_path;
318         {
319             std::ostringstream ostr;
320             ostr << "/proc/" << ::getpid() << "/exe";
321             exe_path= ostr.str();
322         }
323         std::string binary_path= read_link(exe_path);
324         if (!binary_path.empty())
325         {
326             result= basename(binary_path);
327         }
328     }
329     if (result.empty())
330     {
331         // no program name found up to this point.
332         // make a name (as fallback solution):
333         std::ostringstream ostr;
334         ostr << "prg-" << ::getpid();
335         result= ostr.str();
336     }
337     return result;
338 } // eo get_program_name
339
340
341
342 void _cleanup()
343 {
344     // Note: Locking is done in close_syslog();
345     close_syslog();
346     //TODO other cleanups?
347 } // _cleanup
348
349
350 class __Initializer
351 {
352     public:
353         __Initializer()
354         {
355             std::atexit( _cleanup );
356         }
357 } __initialize;
358
359
360 } // eo namespace <anonymous>
361
362
363
364 /*
365 ** implementation of Facility
366 */
367
368
369 const int Facility::AuthPriv= LOG_AUTH;
370 const int Facility::Cron    = LOG_CRON;
371 const int Facility::Daemon  = LOG_DAEMON;
372 const int Facility::Kern    = LOG_KERN;
373 const int Facility::Mail    = LOG_MAIL;
374 const int Facility::News    = LOG_NEWS;
375 const int Facility::Syslog  = LOG_SYSLOG;
376 const int Facility::User    = LOG_USER;
377 const int Facility::UUCP    = LOG_UUCP;
378 const int Facility::Local0  = LOG_LOCAL0;
379 const int Facility::Local1  = LOG_LOCAL1;
380 const int Facility::Local2  = LOG_LOCAL2;
381 const int Facility::Local3  = LOG_LOCAL3;
382 const int Facility::Local4  = LOG_LOCAL4;
383 const int Facility::Local5  = LOG_LOCAL5;
384 const int Facility::Local6  = LOG_LOCAL6;
385 const int Facility::Local7  = LOG_LOCAL7;
386
387
388
389 /*
390 ** implementation of PartLogger::LogHelper
391 */
392
393 PartLogger::LogHelper::LogHelper(PartLogger& logger, int level, const SourceLocation& loc)
394 : Logger(logger)
395 , Level(level)
396 , Location(loc)
397 {
398     StreamPtr.reset(new std::ostringstream());
399 } // eo PartLogger::LogHelper::LogHelper(PartLogger&,int)
400
401 PartLogger::LogHelper::LogHelper(const LogHelper& helper)
402 : Logger(helper.Logger)
403 , Level(helper.Level)
404 , Location(helper.Location)
405 , StreamPtr(helper.StreamPtr)
406 {
407 } // eo PartLogger::LogHelper::LogHelper(const LogHelper&)
408
409
410 PartLogger::LogHelper::~LogHelper()
411 {
412     if (StreamPtr.get())
413     {
414         if (Location)
415         {
416             //*m_stream_ptr << "  at " << m_loc.Line << " in " << m_loc.FunctionName;
417             *StreamPtr << " @" << Location.get_location_tag();
418         }
419         std::string msg(StreamPtr->str());
420         if (!msg.empty())
421         {
422             Logger.log(Level,msg);
423         }
424     }
425 } // eo PartLogger::LogHelper::~LogHelper
426
427
428 /*
429 ** implementation of PartLogger
430 */
431
432 /**
433  * constructor for a part logger.
434  * @param part name of the part (module name) using the logger instance.
435  */
436 PartLogger::PartLogger(const std::string& part)
437 : Part(part)
438 {
439 } // eo PartLogger::PartLogger(const std.:string&)
440
441
442 /**
443  * @brief constructor for a part logger at module level.
444  * @param loc the source location where the PartLogger is constructed.
445  *
446  * The part name is derived from the filename given with the source location by
447  * using the basename and cutting off the C++ file suffix (if it is a well known one;
448  * currently known extensions: cpp, cxx, c++, cc, C).
449  */
450 PartLogger::PartLogger( const SourceLocation& loc )
451 {
452     if (loc.Line>0 && ! loc.File.empty())
453     {
454         std::string str= basename(loc.File);
455         Part= remove_suffix(str,".cpp");
456         if (Part == str) Part= remove_suffix(str,".cxx");
457         if (Part == str) Part= remove_suffix(str,".c++");
458         if (Part == str) Part= remove_suffix(str,".cc");
459         if (Part == str) Part= remove_suffix(str,".C");
460     }
461     else
462     {
463         Part="Unknown";
464     }
465 }// PartLogger::PartLogger(const SourceLocation&)
466
467
468 PartLogger::~PartLogger()
469 {
470 }
471
472
473 /**
474  * generic log function.
475  * @param level the log level.
476  * @param msg the log message.
477  */
478 void PartLogger::log(int level, const std::string msg)
479 {
480     boost::recursive_mutex::scoped_lock lock(LoggerLock);
481
482     if (level <= g_max_level)
483     {
484         log_part_msg(level, Part, msg);
485     }
486 } // eo PartLogger::log(int,const std::string);
487
488
489 void PartLogger::fatal(const std::string& msg)
490 {
491     log(LOG_EMERG,msg);
492 } // eo PartLogger::fatal(const std::string&)
493
494
495 void PartLogger::alert(const std::string& msg)
496 {
497     log(LOG_ALERT,msg);
498 } // eo PartLogger::alert(const std::string&)
499
500
501 void PartLogger::critical(const std::string& msg)
502 {
503     log(LOG_CRIT,msg);
504 } // eo PartLogger::critical(const std::string&)
505
506
507 void PartLogger::error(const std::string& msg)
508 {
509     log(LOG_ERR, msg);
510 } // eo PartLogger::error(const std::string&)
511
512
513 void PartLogger::warning(const std::string& msg)
514 {
515     log(LOG_WARNING, msg);
516 } // eo PartLogger::warning(const std::string&)
517
518
519 void PartLogger::notice(const std::string& msg)
520 {
521     log(LOG_NOTICE, msg);
522 } // eo PartLogger::notice(const std::string&)
523
524
525 void PartLogger::info(const std::string& msg)
526 {
527     log(LOG_INFO, msg);
528 } // eo PartLogger::info(const std::string&)
529
530
531 void PartLogger::debug(const std::string& msg)
532 {
533     log(LOG_DEBUG, msg);
534 } // eo PartLogger::debug(const std::string&)
535
536
537
538 PartLogger::LogHelper PartLogger::fatal(const SourceLocation& loc)
539 {
540     return PartLogger::LogHelper(*this,LOG_EMERG,loc);
541 } // eo PartLogger::fatal(const SourceLocation&)
542
543
544 PartLogger::LogHelper PartLogger::alert(const SourceLocation& loc)
545 {
546     return PartLogger::LogHelper(*this,LOG_ALERT,loc);
547 } // eo PartLogger::alert(const SourceLocation&)
548
549
550 PartLogger::LogHelper PartLogger::critical(const SourceLocation& loc)
551 {
552     return PartLogger::LogHelper(*this,LOG_CRIT,loc);
553 } // eo PartLogger::critical(const SourceLocation&)
554
555
556 PartLogger::LogHelper PartLogger::error(const SourceLocation& loc)
557 {
558     return PartLogger::LogHelper(*this,LOG_ERR,loc);
559 } // eo PartLogger::error(const SourceLocation&)
560
561
562 PartLogger::LogHelper PartLogger::warning(const SourceLocation& loc)
563 {
564     return PartLogger::LogHelper(*this,LOG_WARNING,loc);
565 } // eo PartLogger::warning(const SourceLocation&)
566
567
568 PartLogger::LogHelper PartLogger::notice(const SourceLocation& loc)
569 {
570     return PartLogger::LogHelper(*this,LOG_NOTICE,loc);
571 } // eo PartLogger::notice(const SourceLocation&)
572
573
574 PartLogger::LogHelper PartLogger::info(const SourceLocation& loc)
575 {
576     return PartLogger::LogHelper(*this,LOG_INFO,loc);
577 } // eo PartLogger::info(const SourceLocation&)
578
579
580 PartLogger::LogHelper PartLogger::debug(const SourceLocation& loc)
581 {
582     return PartLogger::LogHelper(*this,LOG_DEBUG,loc);
583 } // eo PartLogger::debug(const SourceLocation&)
584
585 /*
586 **
587 */
588
589
590 /**
591  * enable logging to syslog with a name and a facility.
592  * @param name the name used as ident.
593  * @param facility the facility which should be used.
594  */
595 void enable_syslog( const std::string& name, Facility facility )
596 {
597     boost::recursive_mutex::scoped_lock lock(LoggerLock);
598
599     close_syslog();
600     g_ident= name;
601     g_facility= facility;
602     open_syslog();
603 } // eo enable_syslog(const std::string,Facility)
604
605
606 /**
607  * enable logging to syslog with a facility.
608  * The ident is used from a previous call or (if none was set) is
609  * determined by reading the program path from /proc/\<pid\>/exe.
610  * @param facility the facility which should be used.
611  */
612 void enable_syslog( Facility facility )
613 {
614     boost::recursive_mutex::scoped_lock lock(LoggerLock);
615
616     if (g_ident.empty())
617     {
618         g_ident= get_program_name();
619     }
620     close_syslog();
621     g_facility = facility;
622     open_syslog();
623 } // eo enable_syslog(Facility)
624
625
626 /**
627  * enable or disable logging to syslog.
628  * @param enable whether the logging to syslog should be enabled or not.
629  */
630 void enable_syslog( bool enable )
631 {
632     boost::recursive_mutex::scoped_lock lock(LoggerLock);
633
634     if (enable)
635     {
636         if (!g_syslog_opened)
637         {
638             enable_syslog( g_facility );
639         }
640     }
641     else // ! enable
642     {
643         close_syslog();
644     }
645 } // eo enable_syslog(bool)
646
647
648 /**
649  * enable/ disable loggin to stderr.
650  * @param enable whether to enable or disable logging to stderr.
651  */
652 void enable_stderr_log(bool enable)
653 {
654     boost::recursive_mutex::scoped_lock lock(LoggerLock);
655
656     g_stderr_log= enable;
657 } // eo enableStderr;
658
659
660
661 /**
662  * enable logging to a file.
663  * @param name path to the file.
664  *
665  * @note only one log file can be use at a time.
666  */
667 void enable_log_file( const std::string& name )
668 {
669     boost::recursive_mutex::scoped_lock lock(LoggerLock);
670
671     g_log_file_name= name;
672     g_log_stream_ptr.reset( new std::ofstream() );
673     g_log_stream_ptr->open( name.c_str(), std::ios::out|std::ios::app );
674     //std::cerr << "### opened \"" << name << "\"" << g_log_stream_ptr->good() << std::endl;
675 } // eo enable_log_file(const std::string&)
676
677
678 /**
679  * enable or disable loggin to a file.
680  * if a logfile was already set by a previous call to enable_log_file(const std::string&)
681  * that one is used; else it logs to <tt>/var/log/</tt><em>program name</em><tt>.log</tt>.
682  * @param enable whether to enable or disable logging to a file.
683  */
684 void enable_log_file( bool enable )
685 {
686     boost::recursive_mutex::scoped_lock lock(LoggerLock);
687
688     if (enable)
689     {
690         if (g_log_file_name.empty())
691         {
692             std::ostringstream ostr;
693             ostr << "/var/log/" << get_program_name() << ".log";
694             enable_log_file( ostr.str() );
695         }
696         else
697         {
698             enable_log_file( g_log_file_name );
699         }
700     }
701     else // ! enable
702     {
703         g_log_stream_ptr.reset();
704     }
705 } // eo enable_log_file(bool)
706
707
708 /**
709  * @brief returns if loging to file is enabled and active.
710  * @return @a true if logfile is enabled and opened.
711  */
712 bool is_logging_to_file()
713 {
714     boost::recursive_mutex::scoped_lock lock(LoggerLock);
715
716     return g_log_stream_ptr and g_log_stream_ptr->good();
717 } // eo is_logging_to_file()
718
719
720
721 /**
722  * @brief returns the current name of the log file.
723  * @return the name of the log file; empty if none was given.
724  *
725  * This function returns the last used log file name; even
726  * when logging to that file is currently disabled.
727  */
728 std::string get_log_file_name()
729 {
730     boost::recursive_mutex::scoped_lock lock(LoggerLock);
731
732     return g_log_file_name;
733 } // eo get_log_file_name()
734
735
736 /**
737  * @brief re-opens the logfiles (if applicable).
738  */
739 void reopen()
740 {
741     boost::recursive_mutex::scoped_lock lock(LoggerLock);
742
743     if (g_log_stream_ptr)
744     {
745         enable_log_file(false); // closes log, but holds the name.
746         enable_log_file(true);  // opens the log file again.
747     }
748 } // eo reopen()
749
750
751 /**
752  * set a new log level.
753  * @param level the new log level.
754  * @return the previous log level.
755  */
756 int set_log_level(int level)
757 {
758     boost::recursive_mutex::scoped_lock lock(LoggerLock);
759
760     int previous = g_max_level;
761
762     // Sanity check
763     if (level < LogLevel::Emergency)
764       level = LogLevel::Emergency;
765     else if (level > LogLevel::Debug)
766       level = LogLevel::Debug;
767
768     g_max_level = level;
769
770     return previous;
771 } // eo set_log_level(int)
772
773
774 /**
775  * returns the current log level.
776  * @return the current log level.
777  */
778 int get_log_level()
779 {
780     boost::recursive_mutex::scoped_lock lock(LoggerLock);
781
782     return g_max_level;
783 } // eo get_log_level()
784
785
786 /**
787  * returns if the current log level covers the given level.
788  * This is a convenience function for optimization of log output (especially debug output).
789  * @param level the level which should be tested for.
790  * @return @a true iff a message with the level would be written out.
791  */
792 bool has_log_level(int level)
793 {
794     boost::recursive_mutex::scoped_lock lock(LoggerLock);
795
796     return (g_max_level >= level);
797 } // eo has_log_level(int)
798
799
800
801 } // eo namespace Logger
802 } // eo namespace I2n