From 3a28ee48a5f5b0d8d111605a8a2c3047ea4d97c8 Mon Sep 17 00:00:00 2001 From: Ondrej Martine Date: Fri, 30 Apr 2010 14:04:23 +0000 Subject: [PATCH] * change the format of messages for stderr and syslog (eg, debugging info goes after the message) * add thread ID to the logging facility API * on linux, use gettid() syscall instead of pthread_self() for thread ID * convert syslog logging to a logging facility * add possibility to disable syslog at compile-time (see DISABLE_SYSLOG_LOG macro) git-svn-id: http://svn.berlios.de/svnroot/repos/sems/trunk@1870 8eb893ce-cfd4-0310-b710-fb5ebe64c474 --- core/AmApi.h | 14 +++- core/log.cpp | 215 +++++++++++++++++++++++++++++---------------------- core/log.h | 160 +++++++++++++++++++++----------------- 3 files changed, 223 insertions(+), 166 deletions(-) diff --git a/core/AmApi.h b/core/AmApi.h index 9e4379ed..f9e76e98 100644 --- a/core/AmApi.h +++ b/core/AmApi.h @@ -223,9 +223,19 @@ class AmLoggingFacility : public AmPluginFactory AmLoggingFacility(const string& name); virtual ~AmLoggingFacility() { } - /** will be called on logging messages + /** + * This method is called when logging a message if the instance + * is registered as a logging hook. + * + * @param level log level + * @param pid process ID + * @param tid thread ID + * @param func function name + * @param file file name + * @param line line number + * @param msg message */ - virtual void log(int level, const char* msg) = 0; + virtual void log(int level, pid_t pid, pthread_t tid, const char* func, const char* file, int line, char* msg) = 0; }; #if __GNUC__ < 3 diff --git a/core/log.cpp b/core/log.cpp index 84d6d510..7405eed2 100644 --- a/core/log.cpp +++ b/core/log.cpp @@ -20,135 +20,164 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "log.h" - -#include -#include +#include #include -#include +#include + +#ifndef DISABLE_SYSLOG_LOG +# include +#endif #include -#include "AmApi.h" +#include -int log_level=L_INFO; -int log_stderr=0; +#include "AmApi.h" /* AmLoggingFacility */ +#include "AmThread.h" /* AmMutex */ +#include "log.h" -/* log facility (see syslog(3)) */ -int log_facility=LOG_DAEMON; -vector log_hooks; +int log_level = AmConfig::LogLevel; /**< log level */ +int log_stderr = AmConfig::LogStderr; /**< non-zero if logging to stderr */ +/** Map log levels to text labels */ +const char* log_level2str[] = { "ERROR", "WARNING", "INFO", "DEBUG" }; -inline const char* level2txt(int level) -{ - switch(level){ - case L_ERR: return "ERROR"; - case L_WARN: return "WARNING"; - case L_INFO: return "INFO"; - case L_DBG: return "DEBUG"; +/** Registered logging hooks */ +static vector log_hooks; +static AmMutex log_hooks_mutex; + +#ifndef DISABLE_SYSLOG_LOG + +/** + * Syslog Logging Facility (built-in plug-in) + */ +class SyslogLogFac : public AmLoggingFacility { + int facility; /**< syslog facility */ + + void init() { + openlog("sems", LOG_PID | LOG_CONS, facility); + setlogmask(-1); } - return ""; -} -void init_log() -{ - openlog(LOG_NAME, LOG_PID|LOG_CONS,log_facility); - setlogmask( -1 ); -} + public: + SyslogLogFac() : AmLoggingFacility("syslog"), facility(LOG_DAEMON) { + init(); + } + + ~SyslogLogFac() { + closelog(); + } + + int onLoad() { + /* unused (because it is a built-in plug-in */ + return 0; + } + + bool setFacility(const char* str); + void log(int, pid_t, pthread_t, const char*, const char*, int, char*); +}; + +static SyslogLogFac syslog_log; + +/** Set syslog facility */ +bool SyslogLogFac::setFacility(const char* str) { + static int local_fac[] = { + LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, + LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, + }; -void set_log_facility(const char* facility) { int new_facility = -1; - if (!strcmp(facility, "DAEMON")){ + + if (!strcmp(str, "DAEMON")){ new_facility = LOG_DAEMON; - } else if (!strcmp(facility, "USER")) { + } + else if (!strcmp(str, "USER")) { new_facility = LOG_USER; - } else if (strlen(facility)==6 && - !strncmp(facility, "LOCAL", 5) && - facility[5]-'0' < 8) { - switch (facility[5]) { - case '0': new_facility = LOG_LOCAL0; break; - case '1': new_facility = LOG_LOCAL1; break; - case '2': new_facility = LOG_LOCAL2; break; - case '3': new_facility = LOG_LOCAL3; break; - case '4': new_facility = LOG_LOCAL4; break; - case '5': new_facility = LOG_LOCAL5; break; - case '6': new_facility = LOG_LOCAL6; break; - case '7': new_facility = LOG_LOCAL7; break; - } - } else { - ERROR("unknown syslog facility '%s'\n", - facility); - return; + } + else if (strlen(str) == 6 && !strncmp(str, "LOCAL", 5) && + isdigit(str[5]) && str[5] - '0' < 8) { + new_facility = local_fac[str[5] - '0']; + } + else { + ERROR("unknown syslog facility '%s'\n", str); + return false; } - if (new_facility != log_facility) { - log_facility = new_facility; + if (new_facility != facility) { + facility = new_facility; closelog(); - init_log(); + init(); } + + return true; } -AmMutex dprint_mut; +void SyslogLogFac::log(int level, pid_t pid, pthread_t tid, const char* func, const char* file, int line, char* msg) +{ + static const int log2syslog_level[] = { LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG }; +#ifdef _DEBUG +# ifndef NO_THREADID_LOG + syslog(log2syslog_level[level], "%s: %s [%s, #%lx] [%s:%d]", + log_level2str[level], msg, func, tid, file, line); +# else /* NO_THREADID_LOG */ + syslog(log2syslog_level[level], "%s: %s [%s] [%s:%d]", + log_level2str[level], msg, func, file, line); +# endif /* NO_THREADID_LOG */ +#else /* !_DEBUG */ + syslog(log2syslog_level[level], "%s: %s [%s:%d]", + log_level2str[level], msg, file, line); +#endif /* !_DEBUG */ +} -void dprint(int level, const char* fct, const char* file, int line, const char* fmt, ...) +int set_syslog_facility(const char* str) { - dprint_mut.lock(); + return (syslog_log.setFacility(str) == true); +} - va_list ap; +#endif /* !DISABLE_SYSLOG_LOG */ -#ifndef _DEBUG - fprintf(stderr, "(%i) %s: %s (%s:%i): ",(int)getpid(), level2txt(level), fct, file, line); -#else - fprintf(stderr, "(%i) %s: " THREAD_FMT "%s (%s:%i): ",(int)getpid(), level2txt(level), THREAD_ID fct, file, line); -#endif - va_start(ap, fmt); - vfprintf(stderr,fmt,ap); - fflush(stderr); - va_end(ap); - dprint_mut.unlock(); -} - -void log_print (int level, const char* fmt, ...) +/** + * Initialize logging + */ +void init_logging() { - va_list ap; + log_hooks.clear(); + +#ifndef DISABLE_SYSLOG_LOG + register_log_hook(&syslog_log); +#endif - fprintf(stderr, "(%i) %s: ",(int)getpid(),level2txt(level)); - va_start(ap, fmt); - vfprintf(stderr,fmt,ap); - fflush(stderr); - va_end(ap); + INFO("Logging initialized\n"); } -void log_fac_print(int level, const char* fct, const char* file, int line, const char* fmt, ...) +/** + * Run log hooks + */ +void run_log_hooks(int level, pid_t pid, pthread_t tid, const char* func, const char* file, int line, char* msg) { - va_list ap; - char logline[512]; - int len; - - if(log_hooks.empty()) return; + log_hooks_mutex.lock(); - // file, line etc - len = snprintf(logline, sizeof(logline), "(%i) %s: %s (%s:%i): ", - (int)getpid(), level2txt(level), fct, file, line); - // dbg msg - va_start(ap, fmt); - vsnprintf(logline+len, sizeof(logline)-len, fmt, ap); - va_end(ap); + if (!log_hooks.empty()) { + for (vector::iterator it = log_hooks.begin(); + it != log_hooks.end(); ++it) { + (*it)->log(level, pid, tid, func, file, line, msg); + } + } - for(unsigned i=0; ilog(level, logline); + log_hooks_mutex.unlock(); } -void register_logging_fac(void* vp) +/** + * Register the log hook + */ +void register_log_hook(AmLoggingFacility* fac) { - AmLoggingFacility* lf = static_cast(vp); -#ifdef _DEBUG - assert(lf != NULL); -#endif - log_hooks.push_back(lf); + AmLock lock(log_hooks_mutex); + log_hooks.push_back(fac); } diff --git a/core/log.h b/core/log.h index f75b7f3a..11495c5b 100644 --- a/core/log.h +++ b/core/log.h @@ -20,102 +20,120 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/** @file log.h */ #ifndef _log_h_ #define _log_h_ -#include -#ifdef _DEBUG -#include -#endif +#include /* pid_t */ +#include +#include /* getpid() */ +#include /* pthread_self() */ + #ifdef __cplusplus extern "C" { +# if 0 +} +# endif #endif -#define L_ERR 0 -#define L_WARN 1 -#define L_INFO 2 -#define L_DBG 3 - - /* priority at which we log */ -#define DPRINT_PRIO LOG_DEBUG - -#define LOG_NAME "sems" - - extern int log_level; - extern int log_stderr; - - extern int log_facility; - - void init_log(); - void set_log_facility(const char* facility); +/** + * @{ Log levels + */ +enum Log_Level { + L_ERR = 0, + L_WARN, + L_INFO, + L_DBG +}; +/** @} */ - void dprint (int level, const char* fct, const char* file, int line, const char* fmt, ...); - void log_print (int level, const char* fmt, ...); - void register_logging_fac(void*); - void log_fac_print(int level, const char* fct, const char* file, int line, const char* fmt, ...); +#define FIX_LOG_LEVEL(level) \ + ((level) < L_ERR ? L_ERR : ((level) > L_DBG ? L_DBG : (level))) -#ifdef _DEBUG -#define LOG_PRINT(level, args... ) dprint( level, __FUNCTION__, __FILE__, __LINE__, ##args ) +#ifdef __cplusplus +# define FUNC_NAME __PRETTY_FUNCTION__ #else -#define LOG_PRINT(level, args... ) log_print( level , ##args ) +# define FUNC_NAME __FUNCTION__ #endif -#define LOG_FAC_PRINT(level, args... ) log_fac_print( level, __FUNCTION__, __FILE__, __LINE__, ##args ) -#ifdef _DEBUG -#ifndef NO_THREADID_LOG -#define THREAD_FMT "[%lx] " -#define THREAD_ID (unsigned long)pthread_self(), -#endif -#endif - -#ifndef THREAD_FMT -#define THREAD_FMT "" -#define THREAD_ID +#ifdef __linux +# include +# define GET_PID() syscall(__NR_gettid) +#else +# define GET_PID() getpid() #endif -#if __GNUC__ < 3 -#define _LOG(level,fmt...) LOG_PRINT(level,##fmt) +#ifdef _DEBUG +# ifndef NO_THREADID_LOG +# define GET_TID() pthread_self() +# define LOC_FMT " [%s, #%lx] [%u/%s:%d]" +# define LOC_DATA FUNC_NAME, tid_, pid_, __FILE__, __LINE__ +# else +# define GET_TID() 0 +# define LOC_FMT " [%s] [%u/%s:%d]" +# define LOC_DATA FUNC_NAME, pid_, __FILE__, __LINE__ +# endif #else -#define _LOG(level, fmt, args...) \ - do{ \ - if((level)<=log_level) { \ - if(log_stderr) \ - LOG_PRINT( level, fmt, ##args ); \ - else { \ - switch(level){ \ - case L_ERR: \ - syslog(LOG_ERR, (char*)("ERROR: " THREAD_FMT "%s (%s:%i): " fmt), THREAD_ID __FUNCTION__, __FILE__, __LINE__, ##args); \ - break; \ - case L_WARN: \ - syslog(LOG_WARNING, (char*)("WARNING: " THREAD_FMT "%s (%s:%i): " fmt), THREAD_ID __FUNCTION__, __FILE__, __LINE__, ##args); \ - break; \ - case L_INFO: \ - syslog(LOG_INFO, (char*)("INFO: " THREAD_FMT "%s (%s:%i): " fmt), THREAD_ID __FUNCTION__, __FILE__, __LINE__, ##args); \ - break; \ - case L_DBG: \ - syslog(LOG_DEBUG, (char*)("DEBUG: " THREAD_FMT "%s (%s:%i): " fmt), THREAD_ID __FUNCTION__, __FILE__, __LINE__, ##args); \ - break; \ - } \ - } \ - } \ - LOG_FAC_PRINT( level, fmt, ##args ); \ - }while(0) +# define GET_TID() 0 +# define LOC_FMT " [%u/%s:%d]" +# define LOC_DATA pid_, __FILE__, __LINE__ #endif +/* The underscores in parameter and local variable names are there to + avoid collisions. */ +#define _LOG(level__, fmt, args...) \ + do { \ + int level_ = FIX_LOG_LEVEL(level__); \ + \ + if ((level_) <= log_level) { \ + pid_t pid_ = GET_PID(); \ + pthread_t tid_ = GET_TID(); \ + char msg_[512]; \ + int n_ = snprintf(msg_, sizeof(msg_), fmt, ##args); \ + if (msg_[n_ - 1] == '\n') msg_[n_ - 1] = '\0'; \ + \ + if (log_stderr) { \ + fprintf(stderr, "%s: %s" LOC_FMT "\n", log_level2str[level_], msg_, LOC_DATA); \ + fflush(stderr); \ + } \ + run_log_hooks(level_, pid_, tid_, FUNC_NAME, __FILE__, __LINE__, msg_); \ + } \ + } while(0) + +/** + * @{ Logging macros + */ +#define ERROR(fmt, args...) _LOG(L_ERR, fmt, ##args) +#define WARN(fmt, args...) _LOG(L_WARN, fmt, ##args) +#define INFO(fmt, args...) _LOG(L_INFO, fmt, ##args) +#define DBG(fmt, args...) _LOG(L_DBG, fmt, ##args) +/** @} */ + +extern int log_level; +extern int log_stderr; +extern const char* log_level2str[]; + +void init_logging(void); +void run_log_hooks(int, pid_t, pthread_t, const char*, const char*, int, char*); -#define DBG(args...) _LOG(L_DBG, ##args) -#define ERROR(args...) _LOG(L_ERR, ##args) -#define WARN(args...) _LOG(L_WARN, ##args) -#define INFO(args...) _LOG(L_INFO, ##args) +#ifndef DISABLE_SYSLOG_LOG +int set_syslog_facility(const char*); +#endif #ifdef __cplusplus } #endif +#ifdef __cplusplus +/* ...only for C++ */ +class AmLoggingFacility; +void register_log_hook(AmLoggingFacility*); #endif + +#endif /* !_log_h_ */