00001 /*****************************************************************************/ 00002 /******************************** Documentation ******************************/ 00003 /*****************************************************************************/ 00004 00005 /** 00006 * \file 00007 * Implementation of logging routines. 00008 */ 00009 00010 /*****************************************************************************/ 00011 /******************************* Include Files *******************************/ 00012 /*****************************************************************************/ 00013 00014 #include <stdio.h> 00015 #include <stdlib.h> 00016 #include <stdarg.h> 00017 #include <stdint.h> 00018 #include <string.h> 00019 #include <fcntl.h> 00020 #include <unistd.h> 00021 #include <errno.h> 00022 #include <time.h> 00023 #include <ctype.h> 00024 00025 #include <sys/param.h> 00026 00027 #include "config.h" 00028 #include "util.h" 00029 #include "logging.h" 00030 #include "path_utils.h" 00031 00032 /*****************************************************************************/ 00033 /****************************** Internal Defines *****************************/ 00034 /*****************************************************************************/ 00035 00036 /** 00037 * The default path name for our log file. 00038 * 00039 * On the assumption the package name is "lwatch" this typically expands to: 00040 * /var/log/lwatch/lwatch.log 00041 */ 00042 #define DEFAULT_LOG_FILEPATH PKGLOGDIR "/" PACKAGE_NAME ".log" 00043 00044 /*****************************************************************************/ 00045 /************************** Internal Type Definitions ************************/ 00046 /*****************************************************************************/ 00047 00048 /*****************************************************************************/ 00049 /********************** External Function Declarations *********************/ 00050 /*****************************************************************************/ 00051 00052 /*****************************************************************************/ 00053 /********************** Internal Function Declarations *********************/ 00054 /*****************************************************************************/ 00055 00056 /*****************************************************************************/ 00057 /************************* External Global Variables ***********************/ 00058 /*****************************************************************************/ 00059 00060 /*****************************************************************************/ 00061 /************************* Internal Global Variables ***********************/ 00062 /*****************************************************************************/ 00063 00064 00065 static char log_filepath[PATH_MAX] = DEFAULT_LOG_FILEPATH; /**< path name of log file */ 00066 static int log_level = LOG_WARN; /**< current logging level */ 00067 00068 static bool log_to_console_flag = false; /**< boolean flag if log messages should be written to the console */ 00069 static int log_console_fd = 1; /**< the file descriptor for console logging */ 00070 00071 static bool log_to_file_flag = true; /**< boolean flag if log messages should be written to the log file */ 00072 static int log_file_fd = -1; /**< the file descriptor for log file logging */ 00073 00074 static char log_time_format[128] = ""; /**< strftime style format for timestamps */ 00075 00076 /** 00077 * \name Flags for optional components in a log message 00078 * @{ 00079 */ 00080 static bool show_level = false; /**< true if the log level appears in messages */ 00081 static bool show_file = false; /**< true if the file name appears in messages */ 00082 static bool show_line = false; /**< true if the line number appears in messages */ 00083 static bool show_function = false; /**< true if the function name appears in messages */ 00084 static bool show_time = false; /**< true if a timestamp appears in messages */ 00085 /* @} */ 00086 00087 /*****************************************************************************/ 00088 /**************************** Inline Functions *****************************/ 00089 /*****************************************************************************/ 00090 00091 /*****************************************************************************/ 00092 /*************************** Internal Functions ****************************/ 00093 /*****************************************************************************/ 00094 00095 /*****************************************************************************/ 00096 /**************************** Exported Functions ***************************/ 00097 /*****************************************************************************/ 00098 00099 /** 00100 * Initialize the logging subsystem. 00101 * 00102 * \return SUCCESS (0) or non-zero error code otherwise 00103 * 00104 * Initialization consists of: 00105 * - set the default log file path and open that file 00106 */ 00107 int log_init() 00108 { 00109 int error = SUCCESS; 00110 00111 error = log_set_filepath(NULL); 00112 return error; 00113 } 00114 00115 /** 00116 * Close the logging subsystem, release resources. 00117 * 00118 * \return SUCCESS (0) or non-zero error code otherwise 00119 * 00120 * Finalization consists of: 00121 * - closing the log file 00122 * - closing console output (if enabled) 00123 * 00124 * \see log_close() 00125 */ 00126 int log_fini() 00127 { 00128 return log_close(); 00129 } 00130 00131 /** 00132 * Close the logging subsystem, release resources. 00133 * 00134 * \return SUCCESS (0) or non-zero error code otherwise 00135 * 00136 * Finalization consists of: 00137 * - closing the log file 00138 * - closing console output (if enabled) 00139 */ 00140 int log_close() 00141 { 00142 int error = SUCCESS; 00143 00144 error = log_close_file(); 00145 log_to_console(false); 00146 return error; 00147 } 00148 00149 /** 00150 * Close the log file, further log file write will be disabled. 00151 * 00152 * \return SUCCESS (0) or non-zero error code otherwise 00153 */ 00154 int log_close_file() 00155 { 00156 int error = SUCCESS; 00157 00158 if (log_file_fd >= 0) { 00159 if (close(log_file_fd) < 0) { 00160 error = errno; 00161 } 00162 log_file_fd = -1; 00163 } 00164 return error; 00165 } 00166 00167 /** 00168 * Close existing log file, set the current path of the log file, potentially reopen 00169 * 00170 * \param[in] path The new path name of the log file, if NULL use default log file 00171 * \return SUCCESS (0) or non-zero error code otherwise 00172 * 00173 * The sequence of operations is: 00174 * -# Close current log file if open 00175 * -# Set new log file path (if path param is NULL use default) 00176 * -# If logging to a file is enabled open newly set file 00177 * 00178 * \see DEFAULT_LOG_FILEPATH 00179 */ 00180 int log_set_filepath(const char *path) 00181 { 00182 int error = SUCCESS; 00183 00184 if (log_file_fd >= 0) { 00185 close(log_file_fd); 00186 log_file_fd = -1; 00187 } 00188 00189 if (path) { 00190 if ((error = copy_path(log_filepath, path, sizeof(log_filepath))) != SUCCESS) { 00191 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), path, error_string(error)); 00192 return error; 00193 } 00194 } else { 00195 if ((error = copy_path(log_filepath, DEFAULT_LOG_FILEPATH, sizeof(log_filepath))) != SUCCESS) { 00196 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), DEFAULT_LOG_FILEPATH, error_string(error)); 00197 return error; 00198 } 00199 } 00200 00201 if (log_to_file_flag) { 00202 log_file_fd = open(log_filepath, O_WRONLY | O_CREAT | O_TRUNC, 0664); 00203 error = errno; 00204 } 00205 00206 return error; 00207 } 00208 00209 /** 00210 * Set the current logging level 00211 * 00212 * \param[in] level The new logging level 00213 * \return SUCCESS (0) or non-zero error code otherwise 00214 * 00215 * \see LOG_ERROR 00216 * \see LOG_WARN 00217 * \see LOG_INFO 00218 * \see LOG_DEBUG 00219 * \see LOG_VERBOSE_DEBUG 00220 */ 00221 int log_set_level(int level) 00222 { 00223 switch(level) { 00224 case LOG_ERROR: 00225 case LOG_WARN: 00226 case LOG_INFO: 00227 log_level = level; 00228 return SUCCESS; 00229 case LOG_DEBUG: 00230 case LOG_VERBOSE_DEBUG: 00231 log_level = level; 00232 debug = 1; 00233 return SUCCESS; 00234 default: 00235 return EINVAL; 00236 } 00237 } 00238 00239 /** 00240 * Set boolean flag indicating log messages should appear on the console 00241 * 00242 * \param[in] flag True if log messages should appear on the console, false otherwise. 00243 * \return SUCCESS (0) or non-zero error code otherwise 00244 */ 00245 int log_to_console(bool flag) 00246 { 00247 log_to_console_flag = flag; 00248 return SUCCESS; 00249 } 00250 00251 /** 00252 * Set boolean flag indicating log messages should be written to log file 00253 * 00254 * \param[in] flag True if log messages should be written to log file, false otherwise. 00255 * \return SUCCESS (0) or non-zero error code otherwise 00256 */ 00257 int log_to_file(bool flag) 00258 { 00259 log_to_file_flag = flag; 00260 return SUCCESS; 00261 } 00262 00263 /** 00264 * Set boolean flag indicating if the logging level should appear in log messages. 00265 * 00266 * \param[in] show True if the logging level should appear in log messages, false otherwise 00267 * \return SUCCESS (0) or non-zero error code otherwise 00268 */ 00269 int log_set_show_level(bool show) 00270 { 00271 show_level = show; 00272 return SUCCESS; 00273 } 00274 00275 /** 00276 * Set boolean flag indicating if the file name should appear in log messages 00277 * 00278 * \param[in] show True if the the file name should appear in log messages, false otherwise 00279 * \return SUCCESS (0) or non-zero error code otherwise 00280 */ 00281 int log_set_show_file(bool show) 00282 { 00283 show_file = show; 00284 return SUCCESS; 00285 } 00286 00287 /** 00288 * Set boolean flag indicating if the line number should appear in log messages 00289 * 00290 * \param[in] show True if the line number should appear in log messages, false otherwise 00291 * \return SUCCESS (0) or non-zero error code otherwise 00292 */ 00293 int log_set_show_line(bool show) 00294 { 00295 show_line = show; 00296 return SUCCESS; 00297 } 00298 00299 /** 00300 * Set boolean flag indicating if the function name should appear in log messages 00301 * 00302 * \param[in] show True if the function name should appear in log messages, false otherwise 00303 * \return SUCCESS (0) or non-zero error code otherwise 00304 */ 00305 int log_set_show_function(bool show) 00306 { 00307 show_function = show; 00308 return SUCCESS; 00309 } 00310 00311 /** 00312 * Set boolean flag indicating if a timestamp should appear in log messages 00313 * 00314 * \param[in] show True if a timestamp should appear in log messages, false otherwise 00315 * \return SUCCESS (0) or non-zero error code otherwise 00316 */ 00317 int log_set_show_time(bool show) 00318 { 00319 show_time = show; 00320 return SUCCESS; 00321 } 00322 00323 /** 00324 * Set the time format (as per strftime()) for log message timestamps 00325 * 00326 * \param[in] format The strftime() style time format string 00327 * \return SUCCESS (0) or non-zero error code otherwise 00328 */ 00329 int log_set_time_format(const char *format) 00330 { 00331 int error; 00332 00333 error = SUCCESS; 00334 strncpy(log_time_format, format, sizeof(log_time_format)); 00335 if (format[sizeof(log_time_format) - 1] != 0) { 00336 error = ENOBUFS; 00337 log_msg(LOG_ERROR, _("failed to fully copy time format \"%s\" (%s)\n"), format, error_string(error)); 00338 } 00339 return error; 00340 } 00341 00342 /** 00343 * If not filtered by the log level format a log message and emit it on each 00344 * enabled output stream. 00345 * 00346 * \param[in] level The log level associated with this message 00347 * \param[in] file The source code file name 00348 * \param[in] line The line number in the source code file 00349 * \param[in] function The name of the containing function 00350 * \param[in] fmt The printf style format string 00351 * \param[in] ... Optional printf style arguments 00352 * \return void 00353 * 00354 * The sequence of operations is: 00355 * -# Verify if the message would be emitted according the current logging level 00356 * -# If any optional information will be emitted insert an opening bracket ("[") for it 00357 * -# If displaying the log level is enabled insert the log level 00358 * -# If displaying the file name is enabled insert the file name 00359 * -# If displaying the line number is enabled insert the line number 00360 * -# If displaying the function name is enabled insert the function name 00361 * -# If displaying a timestamp is enabled insert the timestamp 00362 * -# If any optional information was emitted insert a closing bracket ("]") for it 00363 * -# Format the printf style arguments 00364 * -# For each enabled output stream write the message 00365 * 00366 * \see log_msg() 00367 */ 00368 void log_msg1(int level, const char *file, int line, const char *function, const char *fmt, ...) 00369 { 00370 static __thread char buf[4096]; /* thread local static buffer */ 00371 bool do_prefix; 00372 char *p, *buf_end; 00373 int msg_len; 00374 va_list ap; 00375 00376 if (log_level < level) return; 00377 00378 p = buf; /* current position in buffer */ 00379 buf_end = &buf[sizeof(buf)]; /* non-inclusive end of buffer */ 00380 *p = 0; 00381 00382 do_prefix = show_level ||show_file || show_line || show_function || show_time; 00383 if (do_prefix) { 00384 p += snprintf(p, buf_end - p, "["); 00385 if (p >= buf_end) goto fail; 00386 } 00387 00388 if (show_level) { 00389 switch(level) { 00390 case LOG_ERROR: p += snprintf(p, buf_end - p, "ERROR "); if (p >= buf_end) goto fail; break; 00391 case LOG_WARN: p += snprintf(p, buf_end - p, "WARNING "); if (p >= buf_end) goto fail; break; 00392 case LOG_INFO: p += snprintf(p, buf_end - p, "INFO "); if (p >= buf_end) goto fail; break; 00393 case LOG_DEBUG: p += snprintf(p, buf_end - p, "DEBUG "); if (p >= buf_end) goto fail; break; 00394 case LOG_VERBOSE_DEBUG: p += snprintf(p, buf_end - p, "VERBOSE_DEBUG "); if (p >= buf_end) goto fail; break; 00395 } 00396 } 00397 00398 if (show_file) { 00399 p += snprintf(p, buf_end - p, "%s ", file); 00400 if (p >= buf_end) goto fail; 00401 } 00402 00403 if (show_line) { 00404 if (show_file) p[-1] = ':'; 00405 p += snprintf(p, buf_end - p, "%d ", line); 00406 if (p >= buf_end) goto fail; 00407 } 00408 00409 if (show_function) { 00410 p += snprintf(p, buf_end - p, "%s() ", function); 00411 if (p >= buf_end) goto fail; 00412 } 00413 00414 if (show_time) { 00415 p += snprintf(p, buf_end - p, "%s ", time_string(time(NULL), true, log_time_format[0] ? log_time_format : NULL)); 00416 if (p >= buf_end) goto fail; 00417 } 00418 00419 if (do_prefix) { 00420 p--; /* remove trailing space in prefix */ 00421 p += snprintf(p, buf_end - p, "] "); 00422 if (p >= buf_end) goto fail; 00423 } 00424 00425 va_start(ap, fmt); 00426 if (fmt) { 00427 p += vsnprintf(p, buf_end - p, fmt, ap); 00428 if (p >= buf_end) goto fail; 00429 } 00430 00431 msg_len = p - buf; 00432 output: 00433 if (log_to_console_flag && log_console_fd >= 0) write(log_console_fd, buf, msg_len); 00434 if (log_to_file_flag && log_file_fd >= 0) write(log_file_fd, buf, msg_len); 00435 return; 00436 00437 fail: 00438 /* exhausted the buffer; NULL terminate then output truncated string */ 00439 buf_end[-1] = 0; /* should already be NULL terminated, but be safe */ 00440 msg_len = buf_end - buf; 00441 goto output; 00442 } 00443 00444 /** 00445 * Parse a log level in case insensitive manner, set variable to log level enumeration 00446 * 00447 * \param[in] level_str Pointer to string containing log level, leading white space ignored 00448 * \param[out] level Pointer to variable to receive log level enumeration 00449 * \return SUCCESS (0) or non-zero error code otherwise 00450 */ 00451 int log_level_from_string(const char *level_str, int *level) 00452 { 00453 const char *p; 00454 00455 for (p = level_str; *p && isspace(*p); p++); /* skip white space */ 00456 if (!*p) return EINVAL; 00457 00458 if (strcasecmp("error", p) == 0) { 00459 *level = LOG_ERROR; 00460 return SUCCESS; 00461 } 00462 00463 if ((strcasecmp("warn", p) == 0) || 00464 (strcasecmp("warning", p) == 0)) { 00465 *level = LOG_WARN; 00466 return SUCCESS; 00467 } 00468 00469 if (strcasecmp("info", p) == 0) { 00470 *level = LOG_INFO; 00471 return SUCCESS; 00472 } 00473 00474 if (strcasecmp("debug", p) == 0) { 00475 *level = LOG_DEBUG; 00476 return SUCCESS; 00477 } 00478 00479 if (strcasecmp("verbose_debug", p) == 0) { 00480 *level = LOG_VERBOSE_DEBUG; 00481 return SUCCESS; 00482 } 00483 00484 return EINVAL; 00485 }