diff options
Diffstat (limited to 'libglusterfs/src/logging.c')
| -rw-r--r-- | libglusterfs/src/logging.c | 1760 |
1 files changed, 1436 insertions, 324 deletions
diff --git a/libglusterfs/src/logging.c b/libglusterfs/src/logging.c index eaa735d5f..2afa2f354 100644 --- a/libglusterfs/src/logging.c +++ b/libglusterfs/src/logging.c @@ -1,20 +1,11 @@ /* - Copyright (c) 2006-2010 Gluster, Inc. <http://www.gluster.com> + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> This file is part of GlusterFS. - GlusterFS is free software; you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - GlusterFS is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see - <http://www.gnu.org/licenses/>. + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. */ #ifndef _CONFIG_H @@ -30,64 +21,207 @@ #include <locale.h> #include <string.h> #include <stdlib.h> - -#include "xlator.h" -#include "logging.h" -#include "defaults.h" - -#ifdef GF_LINUX_HOST_OS #include <syslog.h> + +#ifdef HAVE_LIBINTL_H +#include <libintl.h> #endif #ifdef HAVE_BACKTRACE #include <execinfo.h> #endif +#include <sys/stat.h> +#include "gf-error-codes.h" -static pthread_mutex_t logfile_mutex; -static char *filename = NULL; -static uint8_t logrotate = 0; -static FILE *logfile = NULL; -static gf_loglevel_t loglevel = GF_LOG_INFO; -static int gf_log_syslog = 1; +#define GF_JSON_MSG_LENGTH 8192 +#define GF_SYSLOG_CEE_FORMAT \ + "@cee: {\"msg\": \"%s\", \"gf_code\": \"%u\", \"gf_message\": \"%s\"}" +#define GF_LOG_CONTROL_FILE "/etc/glusterfs/logger.conf" +#define GF_LOG_BACKTRACE_DEPTH 5 +#define GF_LOG_BACKTRACE_SIZE 4096 +#define GF_LOG_TIMESTR_SIZE 256 -char gf_log_xl_log_set; -gf_loglevel_t gf_log_loglevel; /* extern'd */ -FILE *gf_log_logfile; +#include "xlator.h" +#include "logging.h" +#include "defaults.h" +#include "glusterfs.h" + +static char *gf_level_strings[] = {"", /* NONE */ + "M", /* EMERGENCY */ + "A", /* ALERT */ + "C", /* CRITICAL */ + "E", /* ERROR */ + "W", /* WARNING */ + "N", /* NOTICE */ + "I", /* INFO */ + "D", /* DEBUG */ + "T", /* TRACE */ + "" +}; -static char *cmd_log_filename = NULL; -static FILE *cmdlogfile = NULL; +/* Ideally this should get moved to logging.h */ +struct _msg_queue { + struct list_head msgs; +}; + +struct _log_msg { + const char *msg; + struct list_head queue; +}; void gf_log_logrotate (int signum) { - logrotate = 1; + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + ctx->log.logrotate = 1; } void gf_log_enable_syslog (void) { - gf_log_syslog = 1; + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + ctx->log.gf_log_syslog = 1; } void gf_log_disable_syslog (void) { - gf_log_syslog = 0; + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + ctx->log.gf_log_syslog = 0; } gf_loglevel_t gf_log_get_loglevel (void) { - return loglevel; + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + return ctx->log.loglevel; + else + /* return global defaluts (see gf_log_globals_init) */ + return GF_LOG_INFO; } void gf_log_set_loglevel (gf_loglevel_t level) { - gf_log_loglevel = loglevel = level; + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + ctx->log.loglevel = level; } +void +gf_log_flush (void) +{ + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; + + if (ctx && ctx->log.logger == gf_logger_glusterlog) { + pthread_mutex_lock (&ctx->log.logfile_mutex); + fflush (ctx->log.gf_log_logfile); + pthread_mutex_unlock (&ctx->log.logfile_mutex); + } + + return; +} + +void +gf_log_set_xl_loglevel (void *this, gf_loglevel_t level) +{ + xlator_t *xl = this; + if (!xl) + return; + xl->ctx->log.gf_log_xl_log_set = 1; + xl->loglevel = level; +} + +/* TODO: The following get/set functions are yet not invoked from anywhere + * in the code. The _intention_ is to pass CLI arguments to various daemons + * that are started, which would then invoke these set APIs as required. + * + * glusterd would read the defaults from its .vol file configuration shipped + * as a part of the packages distributed. + * + * For any gluster* daemon that is started the shipped configuration becomes the + * default, if a volume has to change its logging format or logger, then a + * gluster CLI is invoked to set this property for the volume in question. + * + * The property is maintained by glusterd, and passed to the daemon as a CLI + * option, IOW persistence of the option is maintained by glusterd persistent + * storage (i.e .vol file) only + * + * care needs to be taken to configure and start daemons based on the versions + * that supports these features */ +gf_log_format_t +gf_log_get_logformat (void) +{ + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + return ctx->log.logformat; + else + /* return global defaluts (see gf_log_globals_init) */ + return gf_logformat_withmsgid; +} + +void +gf_log_set_logformat (gf_log_format_t format) +{ + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + ctx->log.logformat = format; +} + +gf_log_logger_t +gf_log_get_logger (void) +{ + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + return ctx->log.logger; + else + /* return global defaluts (see gf_log_globals_init) */ + return gf_logger_glusterlog; +} + +void +gf_log_set_logger (gf_log_logger_t logger) +{ + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + ctx->log.logger = logger; +} gf_loglevel_t gf_log_get_xl_loglevel (void *this) @@ -98,116 +232,433 @@ gf_log_get_xl_loglevel (void *this) return xl->loglevel; } +static void +gf_log_rotate(glusterfs_ctx_t *ctx) +{ + int fd = -1; + FILE *new_logfile = NULL; + FILE *old_logfile = NULL; + + /* not involving locks on initial check to speed it up */ + if (ctx->log.logrotate) { + /* let only one winner through on races */ + pthread_mutex_lock (&ctx->log.logfile_mutex); + + if (!ctx->log.logrotate) { + pthread_mutex_unlock (&ctx->log.logfile_mutex); + return; + } else { + ctx->log.logrotate = 0; + pthread_mutex_unlock (&ctx->log.logfile_mutex); + } + + fd = open (ctx->log.filename, + O_CREAT | O_RDONLY, S_IRUSR | S_IWUSR); + if (fd < 0) { + gf_log ("logrotate", GF_LOG_ERROR, + "%s", strerror (errno)); + return; + } + close (fd); + + new_logfile = fopen (ctx->log.filename, "a"); + if (!new_logfile) { + gf_log ("logrotate", GF_LOG_CRITICAL, + "failed to open logfile %s (%s)", + ctx->log.filename, strerror (errno)); + return; + } + + pthread_mutex_lock (&ctx->log.logfile_mutex); + { + if (ctx->log.logfile) + old_logfile = ctx->log.logfile; + + ctx->log.gf_log_logfile = ctx->log.logfile = + new_logfile; + } + pthread_mutex_unlock (&ctx->log.logfile_mutex); + + if (old_logfile != NULL) + fclose (old_logfile); + } + + return; +} + void -gf_log_set_xl_loglevel (void *this, gf_loglevel_t level) +gf_log_globals_fini (void) { - xlator_t *xl = this; - if (!xl) - return; - gf_log_xl_log_set = 1; - xl->loglevel = level; + /* TODO: Nobody is invoking the fini, but cleanup needs to happen here, + * needs cleanup for, log.ident, log.filename, closelog, log file close + * rotate state, possibly under a lock */ + pthread_mutex_destroy (&THIS->ctx->log.logfile_mutex); +} + +/** gf_log_fini - function to perform the cleanup of the log information + * @data - glusterfs context + * @return: success: 0 + * failure: -1 + */ +int +gf_log_fini (void *data) +{ + glusterfs_ctx_t *ctx = data; + int ret = 0; + FILE *old_logfile = NULL; + + if (ctx == NULL) { + ret = -1; + goto out; + } + + pthread_mutex_lock (&ctx->log.logfile_mutex); + { + if (ctx->log.logfile) { + old_logfile = ctx->log.logfile; + + /* Logfile needs to be set to NULL, so that any + call to gf_log after calling gf_log_fini, will + log the message to stderr. + */ + ctx->log.loglevel = GF_LOG_NONE; + ctx->log.logfile = NULL; + } + } + pthread_mutex_unlock (&ctx->log.logfile_mutex); + + if (old_logfile && (fclose (old_logfile) != 0)) + ret = -1; + + out: + return ret; +} + +/** + * gf_get_error_message -function to get error message for given error code + * @error_code: error code defined by log book + * + * @return: success: string + * failure: NULL + */ +const char * +gf_get_error_message (int error_code) { + return _gf_get_message (error_code); } + +/** + * gf_openlog -function to open syslog specific to gluster based on + * existence of file /etc/glusterfs/logger.conf + * @ident: optional identification string similar to openlog() + * @option: optional value to option to openlog(). Passing -1 uses + * 'LOG_PID | LOG_NDELAY' as default + * @facility: optional facility code similar to openlog(). Passing -1 + * uses LOG_DAEMON as default + * + * @return: void + */ void -gf_log_fini (void) +gf_openlog (const char *ident, int option, int facility) { - pthread_mutex_destroy (&logfile_mutex); + int _option = option; + int _facility = facility; + + if (-1 == _option) { + _option = LOG_PID | LOG_NDELAY; + } + if (-1 == _facility) { + _facility = LOG_LOCAL1; + } + + /* TODO: Should check for errors here and return appropriately */ + setlocale(LC_ALL, ""); +#ifdef HAVE_LIBINTL_H + bindtextdomain("gluster", "/usr/share/locale"); + textdomain("gluster"); +#endif + /* close the previous syslog if open as we are changing settings */ + closelog (); + openlog(ident, _option, _facility); +} + + +/** + * _json_escape -function to convert string to json encoded string + * @str: input string + * @buf: buffer to store encoded string + * @len: length of @buf + * + * @return: success: last unprocessed character position by pointer in @str + * failure: NULL + * + * Internal function. Heavily inspired by _ul_str_escape() function in + * libumberlog + * + * Sample output: + * [1] str = "devel error" + * buf = "devel error" + * [2] str = "devel error" + * buf = "devel\terror" + * [3] str = "I/O error on "/tmp/foo" file" + * buf = "I/O error on \"/tmp/foo\" file" + * [4] str = "I/O erroron /tmp/bar file" + * buf = "I/O error\u001bon /tmp/bar file" + * + */ +char * +_json_escape(const char *str, char *buf, size_t len) +{ + static const unsigned char json_exceptions[UCHAR_MAX + 1] = + { + [0x01] = 1, [0x02] = 1, [0x03] = 1, [0x04] = 1, + [0x05] = 1, [0x06] = 1, [0x07] = 1, [0x08] = 1, + [0x09] = 1, [0x0a] = 1, [0x0b] = 1, [0x0c] = 1, + [0x0d] = 1, [0x0e] = 1, [0x0f] = 1, [0x10] = 1, + [0x11] = 1, [0x12] = 1, [0x13] = 1, [0x14] = 1, + [0x15] = 1, [0x16] = 1, [0x17] = 1, [0x18] = 1, + [0x19] = 1, [0x1a] = 1, [0x1b] = 1, [0x1c] = 1, + [0x1d] = 1, [0x1e] = 1, [0x1f] = 1, + ['\\'] = 1, ['"'] = 1 + }; + static const char json_hex_chars[16] = "0123456789abcdef"; + unsigned char *p = NULL; + size_t pos = 0; + + if (!str || !buf || len <= 0) { + return NULL; + } + + for (p = (unsigned char *)str; + *p && (pos + 1) < len; + p++) + { + if (json_exceptions[*p] == 0) { + buf[pos++] = *p; + continue; + } + + if ((pos + 2) >= len) { + break; + } + + switch (*p) + { + case '\b': + buf[pos++] = '\\'; + buf[pos++] = 'b'; + break; + case '\n': + buf[pos++] = '\\'; + buf[pos++] = 'n'; + break; + case '\r': + buf[pos++] = '\\'; + buf[pos++] = 'r'; + break; + case '\t': + buf[pos++] = '\\'; + buf[pos++] = 't'; + break; + case '\\': + buf[pos++] = '\\'; + buf[pos++] = '\\'; + break; + case '"': + buf[pos++] = '\\'; + buf[pos++] = '"'; + break; + default: + if ((pos + 6) >= len) { + buf[pos] = '\0'; + return (char *)p; + } + buf[pos++] = '\\'; + buf[pos++] = 'u'; + buf[pos++] = '0'; + buf[pos++] = '0'; + buf[pos++] = json_hex_chars[(*p) >> 4]; + buf[pos++] = json_hex_chars[(*p) & 0xf]; + break; + } + } + + buf[pos] = '\0'; + return (char *)p; } +/** + * gf_syslog -function to submit message to syslog specific to gluster + * @error_code: error code defined by log book + * @facility_priority: facility_priority of syslog() + * @format: optional format string to syslog() + * + * @return: void + */ +void +gf_syslog (int error_code, int facility_priority, char *format, ...) +{ + char *msg = NULL; + char json_msg[GF_JSON_MSG_LENGTH]; + GF_UNUSED char *p = NULL; + const char *error_message = NULL; + char json_error_message[GF_JSON_MSG_LENGTH]; + va_list ap; + + error_message = gf_get_error_message (error_code); + + va_start (ap, format); + if (format) { + vasprintf (&msg, format, ap); + p = _json_escape (msg, json_msg, GF_JSON_MSG_LENGTH); + if (error_message) { + p = _json_escape (error_message, json_error_message, + GF_JSON_MSG_LENGTH); + syslog (facility_priority, GF_SYSLOG_CEE_FORMAT, + json_msg, error_code, json_error_message); + } else { + /* ignore the error code because no error message for it + and use normal syslog */ + syslog (facility_priority, "%s", msg); + } + free (msg); + } else { + if (error_message) { + /* no user message: treat error_message as msg */ + syslog (facility_priority, GF_SYSLOG_CEE_FORMAT, + json_error_message, error_code, + json_error_message); + } else { + /* cannot produce log as neither error_message nor + msg available */ + } + } + va_end (ap); +} + void -gf_log_globals_init (void) +gf_log_globals_init (void *data) { - pthread_mutex_init (&logfile_mutex, NULL); + glusterfs_ctx_t *ctx = data; + + pthread_mutex_init (&ctx->log.logfile_mutex, NULL); + + ctx->log.loglevel = GF_LOG_INFO; + ctx->log.gf_log_syslog = 1; + ctx->log.sys_log_level = GF_LOG_CRITICAL; + ctx->log.logger = gf_logger_glusterlog; + ctx->log.logformat = gf_logformat_withmsgid; #ifdef GF_LINUX_HOST_OS /* For the 'syslog' output. one can grep 'GlusterFS' in syslog for serious logs */ openlog ("GlusterFS", LOG_PID, LOG_DAEMON); #endif + } int -gf_log_init (const char *file) +gf_log_init (void *data, const char *file, const char *ident) { + glusterfs_ctx_t *ctx = NULL; + int fd = -1; + struct stat buf; + + ctx = data; + + if (ident) { + ctx->log.ident = gf_strdup (ident); + } + + /* we keep the files and the syslog open, so that on logger change, we + * are ready to log anywhere, that the new value specifies */ + if (ctx->log.ident) { + gf_openlog (ctx->log.ident, -1, LOG_DAEMON); + } else { + gf_openlog (NULL, -1, LOG_DAEMON); + } + /* TODO: make FACILITY configurable than LOG_DAEMON */ + if (stat (GF_LOG_CONTROL_FILE, &buf) == 0) { + /* use syslog logging */ + ctx->log.log_control_file_found = 1; + } else { + /* use old style logging */ + ctx->log.log_control_file_found = 0; + } + if (!file){ fprintf (stderr, "ERROR: no filename specified\n"); return -1; } - filename = gf_strdup (file); - if (!filename) { + if (strcmp (file, "-") == 0) { + file = "/dev/stderr"; + } + + ctx->log.filename = gf_strdup (file); + if (!ctx->log.filename) { fprintf (stderr, "ERROR: updating log-filename failed: %s\n", strerror (errno)); return -1; } - logfile = fopen (file, "a"); - if (!logfile){ + fd = open (file, O_CREAT | O_RDONLY, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf (stderr, "ERROR: failed to create logfile" + " \"%s\" (%s)\n", file, strerror (errno)); + return -1; + } + close (fd); + + ctx->log.logfile = fopen (file, "a"); + if (!ctx->log.logfile){ fprintf (stderr, "ERROR: failed to open logfile \"%s\" (%s)\n", file, strerror (errno)); return -1; } - gf_log_logfile = logfile; + ctx->log.gf_log_logfile = ctx->log.logfile; return 0; } - - -struct _msg_queue { - struct list_head msgs; -}; - -struct _log_msg { - const char *msg; - struct list_head queue; -}; - - void -gf_log_lock (void) +set_sys_log_level (gf_loglevel_t level) { - pthread_mutex_lock (&logfile_mutex); -} + glusterfs_ctx_t *ctx = NULL; + ctx = THIS->ctx; -void -gf_log_unlock (void) -{ - pthread_mutex_unlock (&logfile_mutex); -} - - -void -gf_log_cleanup (void) -{ - pthread_mutex_destroy (&logfile_mutex); + if (ctx) + ctx->log.sys_log_level = level; } int -_gf_log_nomem (const char *domain, const char *file, - const char *function, int line, gf_loglevel_t level, - size_t size) +_gf_log_callingfn (const char *domain, const char *file, const char *function, + int line, gf_loglevel_t level, const char *fmt, ...) { const char *basename = NULL; - struct tm *tm = NULL; xlator_t *this = NULL; + char *str1 = NULL; + char *str2 = NULL; + char *msg = NULL; + char timestr[256] = {0,}; + char callstr[4096] = {0,}; struct timeval tv = {0,}; + size_t len = 0; int ret = 0; - gf_loglevel_t xlator_loglevel = 0; - char msg[8092]; - char timestr[256]; - char callstr[4096]; + va_list ap; + glusterfs_ctx_t *ctx = NULL; this = THIS; + ctx = this->ctx; - xlator_loglevel = this->loglevel; - if (xlator_loglevel == 0) - xlator_loglevel = loglevel; - - if (level > xlator_loglevel) + if (ctx->log.gf_log_xl_log_set) { + if (this->loglevel && (level > this->loglevel)) + goto out; + } + if (level > ctx->log.loglevel) goto out; static char *level_strings[] = {"", /* NONE */ @@ -222,258 +673,824 @@ _gf_log_nomem (const char *domain, const char *file, "T", /* TRACE */ ""}; - if (!domain || !file || !function) { + if (!domain || !file || !function || !fmt) { fprintf (stderr, "logging: %s:%s():%d: invalid argument\n", __FILE__, __PRETTY_FUNCTION__, __LINE__); return -1; } + basename = strrchr (file, '/'); + if (basename) + basename++; + else + basename = file; + #if HAVE_BACKTRACE /* Print 'calling function' */ do { void *array[5]; char **callingfn = NULL; - size_t bt_size = 0; + size_t size = 0; - bt_size = backtrace (array, 5); - if (bt_size) - callingfn = backtrace_symbols (&array[2], bt_size-2); + size = backtrace (array, 5); + if (size) + callingfn = backtrace_symbols (&array[2], size-2); if (!callingfn) break; - if (bt_size == 5) + if (size == 5) snprintf (callstr, 4096, "(-->%s (-->%s (-->%s)))", callingfn[2], callingfn[1], callingfn[0]); - if (bt_size == 4) + if (size == 4) snprintf (callstr, 4096, "(-->%s (-->%s))", callingfn[1], callingfn[0]); - if (bt_size == 3) + if (size == 3) snprintf (callstr, 4096, "(-->%s)", callingfn[0]); free (callingfn); } while (0); #endif /* HAVE_BACKTRACE */ + if (ctx->log.log_control_file_found) + { + int priority; + /* treat GF_LOG_TRACE and GF_LOG_NONE as LOG_DEBUG and + other level as is */ + if (GF_LOG_TRACE == level || GF_LOG_NONE == level) { + priority = LOG_DEBUG; + } else { + priority = level - 1; + } + + va_start (ap, fmt); + vasprintf (&str2, fmt, ap); + va_end (ap); + + gf_syslog (GF_ERR_DEV, priority, + "[%s:%d:%s] %s %d-%s: %s", + basename, line, function, + callstr, + ((this->graph) ? this->graph->id:0), domain, + str2); + + goto out; + } + ret = gettimeofday (&tv, NULL); if (-1 == ret) goto out; + va_start (ap, fmt); + gf_time_fmt (timestr, sizeof timestr, tv.tv_sec, gf_timefmt_FT); + snprintf (timestr + strlen (timestr), sizeof timestr - strlen (timestr), + ".%"GF_PRI_SUSECONDS, tv.tv_usec); + + ret = gf_asprintf (&str1, "[%s] %s [%s:%d:%s] %s %d-%s: ", + timestr, level_strings[level], + basename, line, function, callstr, + ((this->graph) ? this->graph->id:0), domain); + if (-1 == ret) { + goto out; + } - tm = localtime (&tv.tv_sec); + ret = vasprintf (&str2, fmt, ap); + if (-1 == ret) { + goto out; + } - pthread_mutex_lock (&logfile_mutex); - { - strftime (timestr, 256, "%Y-%m-%d %H:%M:%S", tm); - snprintf (timestr + strlen (timestr), 256 - strlen (timestr), - ".%"GF_PRI_SUSECONDS, tv.tv_usec); + va_end (ap); - basename = strrchr (file, '/'); - if (basename) - basename++; - else - basename = file; + len = strlen (str1); + msg = GF_MALLOC (len + strlen (str2) + 1, gf_common_mt_char); - ret = sprintf (msg, "[%s] %s [%s:%d:%s] %s %s: no memory " - "available for size (%"GF_PRI_SIZET")", - timestr, level_strings[level], - basename, line, function, callstr, - domain, size); - if (-1 == ret) { - goto unlock; - } + strcpy (msg, str1); + strcpy (msg + len, str2); - if (logfile) { - fprintf (logfile, "%s\n", msg); - fflush (logfile); - } else { + pthread_mutex_lock (&ctx->log.logfile_mutex); + { + if (ctx->log.logfile) { + fprintf (ctx->log.logfile, "%s\n", msg); + } else if (ctx->log.loglevel >= level) { fprintf (stderr, "%s\n", msg); } #ifdef GF_LINUX_HOST_OS /* We want only serious log in 'syslog', not our debug and trace logs */ - if (gf_log_syslog && level && (level <= GF_LOG_ERROR)) + if (ctx->log.gf_log_syslog && level && + (level <= ctx->log.sys_log_level)) syslog ((level-1), "%s\n", msg); #endif } -unlock: - pthread_mutex_unlock (&logfile_mutex); + pthread_mutex_unlock (&ctx->log.logfile_mutex); + out: + GF_FREE (msg); + + GF_FREE (str1); + + FREE (str2); + return ret; - } +} int -_gf_log_callingfn (const char *domain, const char *file, const char *function, - int line, gf_loglevel_t level, const char *fmt, ...) +_gf_msg_plain_internal (gf_loglevel_t level, const char *msg) { - const char *basename = NULL; - struct tm *tm = NULL; - xlator_t *this = NULL; - char *str1 = NULL; - char *str2 = NULL; - char *msg = NULL; - char timestr[256] = {0,}; - char callstr[4096] = {0,}; - struct timeval tv = {0,}; - size_t len = 0; - int ret = 0; - gf_loglevel_t xlator_loglevel = 0; - va_list ap; + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + int priority; this = THIS; + ctx = this->ctx; + + /* log to the configured logging service */ + switch (ctx->log.logger) { + case gf_logger_syslog: + if (ctx->log.log_control_file_found && ctx->log.gf_log_syslog) { + SET_LOG_PRIO (level, priority); - xlator_loglevel = this->loglevel; - if (xlator_loglevel == 0) - xlator_loglevel = loglevel; + syslog (priority, "%s", msg); + break; + } + /* NOTE: If syslog control file is absent, which is another + * way to control logging to syslog, then we will fall through + * to the gluster log. The ideal way to do things would be to + * not have the extra control file check */ + case gf_logger_glusterlog: + pthread_mutex_lock (&ctx->log.logfile_mutex); + { + if (ctx->log.logfile) { + fprintf (ctx->log.logfile, "%s\n", msg); + fflush (ctx->log.logfile); + } else { + fprintf (stderr, "%s\n", msg); + fflush (stderr); + } - if (level > xlator_loglevel) +#ifdef GF_LINUX_HOST_OS + /* We want only serious logs in 'syslog', not our debug + * and trace logs */ + if (ctx->log.gf_log_syslog && level && + (level <= ctx->log.sys_log_level)) + syslog ((level-1), "%s\n", msg); +#endif + } + pthread_mutex_unlock (&ctx->log.logfile_mutex); + + break; + } + + return 0; +} + +int +_gf_msg_plain (gf_loglevel_t level, const char *fmt, ...) +{ + xlator_t *this = NULL; + int ret = 0; + va_list ap; + char *msg = NULL; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; + + if (!ctx) goto out; - static char *level_strings[] = {"", /* NONE */ - "M", /* EMERGENCY */ - "A", /* ALERT */ - "C", /* CRITICAL */ - "E", /* ERROR */ - "W", /* WARNING */ - "N", /* NOTICE */ - "I", /* INFO */ - "D", /* DEBUG */ - "T", /* TRACE */ - ""}; + if (ctx->log.gf_log_xl_log_set) { + if (this->loglevel && (level > this->loglevel)) + goto out; + } + if (level > ctx->log.loglevel) + goto out; - if (!domain || !file || !function || !fmt) { - fprintf (stderr, - "logging: %s:%s():%d: invalid argument\n", - __FILE__, __PRETTY_FUNCTION__, __LINE__); - return -1; + va_start (ap, fmt); + ret = vasprintf (&msg, fmt, ap); + va_end (ap); + if (-1 == ret) { + goto out; } + ret = _gf_msg_plain_internal (level, msg); + + FREE (msg); + +out: + return ret; +} + +int +_gf_msg_vplain (gf_loglevel_t level, const char *fmt, va_list ap) +{ + xlator_t *this = NULL; + int ret = 0; + char *msg = NULL; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; + + if (!ctx) + goto out; + + if (ctx->log.gf_log_xl_log_set) { + if (this->loglevel && (level > this->loglevel)) + goto out; + } + if (level > ctx->log.loglevel) + goto out; + + ret = vasprintf (&msg, fmt, ap); + if (-1 == ret) { + goto out; + } + + ret = _gf_msg_plain_internal (level, msg); + + FREE (msg); +out: + return ret; +} + +int +_gf_msg_plain_nomem (gf_loglevel_t level, const char *msg) +{ + xlator_t *this = NULL; + int ret = 0; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; + + if (!ctx) + goto out; + + if (ctx->log.gf_log_xl_log_set) { + if (this->loglevel && (level > this->loglevel)) + goto out; + } + if (level > ctx->log.loglevel) + goto out; + + ret = _gf_msg_plain_internal (level, msg); + +out: + return ret; +} + #if HAVE_BACKTRACE - /* Print 'calling function' */ - do { - void *array[5]; - char **callingfn = NULL; - size_t size = 0; +void +_gf_msg_backtrace_nomem (gf_loglevel_t level, int stacksize) +{ + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + void *array[200]; + size_t bt_size = 0; + int fd = -1; - size = backtrace (array, 5); - if (size) - callingfn = backtrace_symbols (&array[2], size-2); - if (!callingfn) - break; + this = THIS; + ctx = this->ctx; - if (size == 5) - snprintf (callstr, 4096, "(-->%s (-->%s (-->%s)))", - callingfn[2], callingfn[1], callingfn[0]); - if (size == 4) - snprintf (callstr, 4096, "(-->%s (-->%s))", - callingfn[1], callingfn[0]); - if (size == 3) - snprintf (callstr, 4096, "(-->%s)", callingfn[0]); + if (!ctx) + goto out; - free (callingfn); - } while (0); + /* syslog does not have fd support, hence no no-mem variant */ + if (ctx->log.logger != gf_logger_glusterlog) + goto out; + + if (ctx->log.gf_log_xl_log_set) { + if (this->loglevel && (level > this->loglevel)) + goto out; + } + if (level > ctx->log.loglevel) + goto out; + + bt_size = backtrace (array, ((stacksize <= 200)? stacksize : 200)); + pthread_mutex_lock (&ctx->log.logfile_mutex); + { + fd = ctx->log.logfile? + fileno (ctx->log.logfile) : + fileno (stderr); + if (bt_size && (fd != -1)) { + /* print to the file fd, to prevent any + * allocations from backtrace_symbols */ + backtrace_symbols_fd (&array[0], bt_size, fd); + } + } + pthread_mutex_unlock (&ctx->log.logfile_mutex); + +out: + return; +} + +int +_gf_msg_backtrace (int stacksize, char *callstr, size_t strsize) +{ + int ret = -1; + int i = 0; + int size = 0; + int savstrsize = strsize; + void *array[200]; + char **callingfn = NULL; + + /* We chop off last 2 anyway, so if request is less than tolerance + * nothing to do */ + if (stacksize < 3) + goto out; + + size = backtrace (array, ((stacksize <= 200)? stacksize : 200)); + if ((size - 3) < 0) + goto out; + if (size) + callingfn = backtrace_symbols (&array[2], size - 2); + if (!callingfn) + goto out; + + ret = snprintf (callstr, strsize, "("); + PRINT_SIZE_CHECK (ret, out, strsize); + + for ((i = size - 3); i >= 0; i--) { + ret = snprintf (callstr + savstrsize - strsize, strsize, + "-->%s ", callingfn[i]); + PRINT_SIZE_CHECK (ret, out, strsize); + } + + ret = snprintf (callstr + savstrsize - strsize, strsize, ")"); + PRINT_SIZE_CHECK (ret, out, strsize); +out: + FREE (callingfn); + return ret; +} #endif /* HAVE_BACKTRACE */ +int +_gf_msg_nomem (const char *domain, const char *file, + const char *function, int line, gf_loglevel_t level, + size_t size) +{ + const char *basename = NULL; + xlator_t *this = NULL; + struct timeval tv = {0,}; + int ret = 0; + int fd = -1; + char msg[2048] = {0,}; + char timestr[GF_LOG_TIMESTR_SIZE] = {0,}; + glusterfs_ctx_t *ctx = NULL; + int wlen = 0; + int priority; + + this = THIS; + ctx = this->ctx; + + if (!ctx) + goto out; + + if (ctx->log.gf_log_xl_log_set) { + if (this->loglevel && (level > this->loglevel)) + goto out; + } + if (level > ctx->log.loglevel) + goto out; + + if (!domain || !file || !function) { + fprintf (stderr, + "logging: %s:%s():%d: invalid argument\n", + __FILE__, __PRETTY_FUNCTION__, __LINE__); + return -1; + } + + GET_FILE_NAME_TO_LOG (file, basename); + ret = gettimeofday (&tv, NULL); if (-1 == ret) goto out; + gf_time_fmt (timestr, sizeof timestr, tv.tv_sec, gf_timefmt_FT); + ret = snprintf (timestr + strlen (timestr), + sizeof timestr - strlen (timestr), + ".%"GF_PRI_SUSECONDS, tv.tv_usec); + if (-1 == ret) { + goto out; + } - tm = localtime (&tv.tv_sec); + /* TODO: Currently we print in the enhanced format, with a message ID + * of 0. Need to enhance this to support format as configured */ + ret = snprintf (msg, sizeof msg, "[%s] %s [MSGID: %"PRIu64"]" + " [%s:%d:%s] %s: no memory " + "available for size (%"GF_PRI_SIZET")" + " [call stack follows]\n", + timestr, gf_level_strings[level], (uint64_t) 0, + basename, line, function, domain, size); + if (-1 == ret) { + goto out; + } - pthread_mutex_lock (&logfile_mutex); - { - va_start (ap, fmt); + /* log to the configured logging service */ + switch (ctx->log.logger) { + case gf_logger_syslog: + if (ctx->log.log_control_file_found && ctx->log.gf_log_syslog) { + SET_LOG_PRIO (level, priority); - strftime (timestr, 256, "%Y-%m-%d %H:%M:%S", tm); - snprintf (timestr + strlen (timestr), 256 - strlen (timestr), - ".%"GF_PRI_SUSECONDS, tv.tv_usec); + /* if syslog allocates, then this may fail, but we + * cannot do much about it at the moment */ + /* There is no fd for syslog, hence no stack printed */ + syslog (priority, "%s", msg); + break; + } + /* NOTE: If syslog control file is absent, which is another + * way to control logging to syslog, then we will fall through + * to the gluster log. The ideal way to do things would be to + * not have the extra control file check */ + case gf_logger_glusterlog: + pthread_mutex_lock (&ctx->log.logfile_mutex); + { + fd = ctx->log.logfile? fileno (ctx->log.logfile) : + fileno (stderr); + if (fd == -1) { + pthread_mutex_unlock (&ctx->log.logfile_mutex); + goto out; + } + + wlen = strlen (msg); + + /* write directly to the fd to prevent out of order + * message and stack */ + ret = write (fd, msg, wlen); + if (ret == -1) { + pthread_mutex_unlock (&ctx->log.logfile_mutex); + goto out; + } +#ifdef GF_LINUX_HOST_OS + /* We want only serious log in 'syslog', not our debug + * and trace logs */ + if (ctx->log.gf_log_syslog && level && + (level <= ctx->log.sys_log_level)) + syslog ((level-1), "%s\n", msg); +#endif + } + pthread_mutex_unlock (&ctx->log.logfile_mutex); - basename = strrchr (file, '/'); - if (basename) - basename++; - else - basename = file; +#ifdef HAVE_BACKTRACE + _gf_msg_backtrace_nomem (level, GF_LOG_BACKTRACE_DEPTH); +#endif - ret = gf_asprintf (&str1, "[%s] %s [%s:%d:%s] %s %d-%s: ", - timestr, level_strings[level], - basename, line, function, callstr, - ((this->graph) ? this->graph->id:0), domain); - if (-1 == ret) { - goto unlock; + break; + } + +out: + return ret; +} + +static int +gf_log_syslog (const char *domain, const char *file, const char *function, + int32_t line, gf_loglevel_t level, int errnum, + uint64_t msgid, char **appmsgstr, char *callstr) +{ + int priority; + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; + + SET_LOG_PRIO (level, priority); + + /* log with appropriate format */ + if (ctx->log.logformat == gf_logformat_traditional) { + if (!callstr) { + if (errnum) { + syslog (priority, "[%s:%d:%s] %d-%s: %s [%s]", + file, line, function, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr, strerror(errnum)); + } else { + syslog (priority, "[%s:%d:%s] %d-%s: %s", + file, line, function, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr); + } + } else { + if (errnum) { + syslog (priority, "[%s:%d:%s] %s %d-%s:" + " %s [%s]", + file, line, function, callstr, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr, strerror(errnum)); + } else { + syslog (priority, "[%s:%d:%s] %s %d-%s: %s", + file, line, function, callstr, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr); + } } + } else if (ctx->log.logformat == gf_logformat_withmsgid) { + if (!callstr) { + if (errnum) { + syslog (priority, "[MSGID: %"PRIu64"]" + " [%s:%d:%s] %d-%s: %s [%s]", + msgid, file, line, function, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr, strerror(errnum)); + } else { + syslog (priority, "[MSGID: %"PRIu64"]" + " [%s:%d:%s] %d-%s: %s", + msgid, file, line, function, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr); + } + } else { + if (errnum) { + syslog (priority, "[MSGID: %"PRIu64"]" + " [%s:%d:%s] %s %d-%s: %s [%s]", + msgid, file, line, function, callstr, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr, strerror(errnum)); + } else { + syslog (priority, "[MSGID: %"PRIu64"]" + " [%s:%d:%s] %s %d-%s: %s", + msgid, file, line, function, callstr, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr); + } + } + } else if (ctx->log.logformat == gf_logformat_cee) { + /* TODO: Enhance CEE with additional parameters */ + gf_syslog (GF_ERR_DEV, priority, + "[%s:%d:%s] %d-%s: %s", + file, line, function, + ((this->graph) ? this->graph->id:0), + domain, *appmsgstr); + } else { + /* NOTE: should not get here without logging */ + } + + /* TODO: There can be no errors from gf_syslog? */ + return 0; +} + +static int +gf_log_glusterlog (const char *domain, const char *file, const char *function, + int32_t line, gf_loglevel_t level, int errnum, + uint64_t msgid, char **appmsgstr, char *callstr) +{ + char timestr[GF_LOG_TIMESTR_SIZE] = {0,}; + struct timeval tv = {0,}; + char *header = NULL; + char *footer = NULL; + char *msg = NULL; + size_t hlen = 0, flen = 0, mlen = 0; + int ret = 0; + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; - ret = vasprintf (&str2, fmt, ap); + /* rotate if required */ + gf_log_rotate(ctx); + + /* format the time stanp */ + ret = gettimeofday (&tv, NULL); + if (-1 == ret) + goto out; + gf_time_fmt (timestr, sizeof timestr, tv.tv_sec, gf_timefmt_FT); + snprintf (timestr + strlen (timestr), sizeof timestr - strlen (timestr), + ".%"GF_PRI_SUSECONDS, tv.tv_usec); + + /* generate header and footer */ + if (ctx->log.logformat == gf_logformat_traditional) { + if (!callstr) { + ret = gf_asprintf (&header, "[%s] %s [%s:%d:%s]" + " %d-%s: ", + timestr, gf_level_strings[level], + file, line, function, + ((this->graph)?this->graph->id:0), + domain); + } else { + ret = gf_asprintf (&header, "[%s] %s [%s:%d:%s] %s" + " %d-%s: ", + timestr, gf_level_strings[level], + file, line, function, callstr, + ((this->graph)?this->graph->id:0), + domain); + } + if (-1 == ret) { + goto err; + } + } else { /* gf_logformat_withmsgid */ + /* CEE log format unsupported in logger_glusterlog, so just + * print enhanced log format */ + if (!callstr) { + ret = gf_asprintf (&header, "[%s] %s [MSGID: %"PRIu64"]" + " [%s:%d:%s] %d-%s: ", + timestr, gf_level_strings[level], + msgid, file, line, function, + ((this->graph)?this->graph->id:0), + domain); + } else { + ret = gf_asprintf (&header, "[%s] %s [MSGID: %"PRIu64"]" + " [%s:%d:%s] %s %d-%s: ", + timestr, gf_level_strings[level], + msgid, file, line, function, callstr, + ((this->graph)?this->graph->id:0), + domain); + } if (-1 == ret) { - goto unlock; + goto err; } + } - va_end (ap); + if (errnum) { + ret = gf_asprintf (&footer, " [%s]",strerror(errnum)); + if (-1 == ret) { + goto err; + } + } - len = strlen (str1); - msg = GF_MALLOC (len + strlen (str2) + 1, gf_common_mt_char); + /* generate the full message to log */ + hlen = strlen (header); + flen = footer? strlen (footer) : 0; + mlen = strlen (*appmsgstr); + msg = GF_MALLOC (hlen + flen + mlen + 1, gf_common_mt_char); - strcpy (msg, str1); - strcpy (msg + len, str2); + strcpy (msg, header); + strcpy (msg + hlen, *appmsgstr); + if (footer) + strcpy (msg + hlen + mlen, footer); - if (logfile) { - fprintf (logfile, "%s\n", msg); - fflush (logfile); - } else { + pthread_mutex_lock (&ctx->log.logfile_mutex); + { + if (ctx->log.logfile) { + fprintf (ctx->log.logfile, "%s\n", msg); + fflush (ctx->log.logfile); + } else if (ctx->log.loglevel >= level) { fprintf (stderr, "%s\n", msg); + fflush (stderr); } #ifdef GF_LINUX_HOST_OS - /* We want only serious log in 'syslog', not our debug - and trace logs */ - if (gf_log_syslog && level && (level <= GF_LOG_CRITICAL)) + /* We want only serious logs in 'syslog', not our debug + * and trace logs */ + if (ctx->log.gf_log_syslog && level && + (level <= ctx->log.sys_log_level)) syslog ((level-1), "%s\n", msg); #endif } -unlock: - pthread_mutex_unlock (&logfile_mutex); + /* TODO: Plugin in memory log buffer retention here. For logs not + * flushed during cores, it would be useful to retain some of the last + * few messages in memory */ + pthread_mutex_unlock (&ctx->log.logfile_mutex); - if (msg) { - GF_FREE (msg); +err: + GF_FREE (msg); + GF_FREE (header); + GF_FREE (footer); + +out: + return ret; +} + +static int +_gf_msg_internal (const char *domain, const char *file, const char *function, + int32_t line, gf_loglevel_t level, int errnum, uint64_t msgid, + char **appmsgstr, char *callstr) +{ + const char *basename = NULL; + int ret = -1; + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; + + GET_FILE_NAME_TO_LOG (file, basename); + + /* TODO: Plug in repeated message suppression for gluster logs here. + * Comparison of last few messages stored based on, appmsgstr, errnum + * msgid. */ + + /* log to the configured logging service */ + switch (ctx->log.logger) { + case gf_logger_syslog: + if (ctx->log.log_control_file_found && ctx->log.gf_log_syslog) { + ret = gf_log_syslog (domain, basename, function, line, + level, errnum, msgid, appmsgstr, + callstr); + break; + } + /* NOTE: If syslog control file is absent, which is another + * way to control logging to syslog, then we will fall through + * to the gluster log. The ideal way to do things would be to + * not have the extra control file check */ + case gf_logger_glusterlog: + ret = gf_log_glusterlog (domain, basename, function, line, + level, errnum, msgid, appmsgstr, + callstr); + break; } - if (str1) - GF_FREE (str1); + return ret; +} - if (str2) - FREE (str2); +int +_gf_msg (const char *domain, const char *file, const char *function, + int32_t line, gf_loglevel_t level, int errnum, int trace, + uint64_t msgid, const char *fmt, ...) +{ + int ret = 0; + char *msgstr = NULL; + va_list ap; + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + char callstr[GF_LOG_BACKTRACE_SIZE] = {0,}; + int passcallstr = 0; + + /* in args check */ + if (!domain || !file || !function || !fmt) { + fprintf (stderr, + "logging: %s:%s():%d: invalid argument\n", + __FILE__, __PRETTY_FUNCTION__, __LINE__); + return -1; + } + + this = THIS; + ctx = this->ctx; + if (ctx == NULL) { + /* messages before context initialization are ignored */ + return -1; + } + + /* check if we should be logging */ + if (ctx->log.gf_log_xl_log_set) { + if (this->loglevel && (level > this->loglevel)) + goto out; + } + if (level > ctx->log.loglevel) + goto out; + +#if HAVE_BACKTRACE + if (trace) { + ret = _gf_msg_backtrace (GF_LOG_BACKTRACE_DEPTH, callstr, + GF_LOG_BACKTRACE_DEPTH); + if (ret >= 0) + passcallstr = 1; + else + ret = 0; + } +#endif /* HAVE_BACKTRACE */ + + /* form the message */ + va_start (ap, fmt); + ret = vasprintf (&msgstr, fmt, ap); + va_end (ap); + + /* log */ + if (ret != -1) + ret = _gf_msg_internal(domain, file, function, line, level, + errnum, msgid, &msgstr, + (passcallstr? callstr : NULL)); + else + /* man (3) vasprintf states on error strp contents + * are undefined, be safe */ + msgstr = NULL; + + FREE (msgstr); out: return ret; } +/* TODO: Deprecate (delete) _gf_log, _gf_log_callingfn, + * once messages are changed to use _gf_msgXXX APIs for logging */ int _gf_log (const char *domain, const char *file, const char *function, int line, gf_loglevel_t level, const char *fmt, ...) { - const char *basename = NULL; - FILE *new_logfile = NULL; - va_list ap; - struct tm *tm = NULL; - char timestr[256]; + const char *basename = NULL; + FILE *new_logfile = NULL; + va_list ap; + char timestr[GF_LOG_TIMESTR_SIZE] = {0,}; struct timeval tv = {0,}; - - char *str1 = NULL; - char *str2 = NULL; - char *msg = NULL; - size_t len = 0; - int ret = 0; - xlator_t *this = NULL; - gf_loglevel_t xlator_loglevel = 0; + char *str1 = NULL; + char *str2 = NULL; + char *msg = NULL; + size_t len = 0; + int ret = 0; + int fd = -1; + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; this = THIS; + ctx = this->ctx; - xlator_loglevel = this->loglevel; - if (xlator_loglevel == 0) - xlator_loglevel = loglevel; - - if (level > xlator_loglevel) + if (ctx->log.gf_log_xl_log_set) { + if (this->loglevel && (level > this->loglevel)) + goto out; + } + if (level > ctx->log.loglevel) goto out; static char *level_strings[] = {"", /* NONE */ @@ -495,120 +1512,220 @@ _gf_log (const char *domain, const char *file, const char *function, int line, return -1; } + basename = strrchr (file, '/'); + if (basename) + basename++; + else + basename = file; + + if (ctx->log.log_control_file_found) + { + int priority; + /* treat GF_LOG_TRACE and GF_LOG_NONE as LOG_DEBUG and + other level as is */ + if (GF_LOG_TRACE == level || GF_LOG_NONE == level) { + priority = LOG_DEBUG; + } else { + priority = level - 1; + } - if (logrotate) { - logrotate = 0; + va_start (ap, fmt); + vasprintf (&str2, fmt, ap); + va_end (ap); - new_logfile = fopen (filename, "a"); + gf_syslog (GF_ERR_DEV, priority, + "[%s:%d:%s] %d-%s: %s", + basename, line, function, + ((this->graph) ? this->graph->id:0), domain, str2); + goto err; + } + + if (ctx->log.logrotate) { + ctx->log.logrotate = 0; + + fd = open (ctx->log.filename, + O_CREAT | O_RDONLY, S_IRUSR | S_IWUSR); + if (fd < 0) { + gf_log ("logrotate", GF_LOG_ERROR, + "%s", strerror (errno)); + return -1; + } + close (fd); + + new_logfile = fopen (ctx->log.filename, "a"); if (!new_logfile) { gf_log ("logrotate", GF_LOG_CRITICAL, "failed to open logfile %s (%s)", - filename, strerror (errno)); + ctx->log.filename, strerror (errno)); goto log; } - if (logfile) - fclose (logfile); + pthread_mutex_lock (&ctx->log.logfile_mutex); + { + if (ctx->log.logfile) + fclose (ctx->log.logfile); + + ctx->log.gf_log_logfile = + ctx->log.logfile = new_logfile; + } + pthread_mutex_unlock (&ctx->log.logfile_mutex); - gf_log_logfile = logfile = new_logfile; } log: ret = gettimeofday (&tv, NULL); if (-1 == ret) goto out; + va_start (ap, fmt); + gf_time_fmt (timestr, sizeof timestr, tv.tv_sec, gf_timefmt_FT); + snprintf (timestr + strlen (timestr), sizeof timestr - strlen (timestr), + ".%"GF_PRI_SUSECONDS, tv.tv_usec); - tm = localtime (&tv.tv_sec); - - pthread_mutex_lock (&logfile_mutex); - { - va_start (ap, fmt); - - strftime (timestr, 256, "%Y-%m-%d %H:%M:%S", tm); - snprintf (timestr + strlen (timestr), 256 - strlen (timestr), - ".%"GF_PRI_SUSECONDS, tv.tv_usec); - - basename = strrchr (file, '/'); - if (basename) - basename++; - else - basename = file; + ret = gf_asprintf (&str1, "[%s] %s [%s:%d:%s] %d-%s: ", + timestr, level_strings[level], + basename, line, function, + ((this->graph)?this->graph->id:0), domain); + if (-1 == ret) { + goto err; + } - ret = gf_asprintf (&str1, "[%s] %s [%s:%d:%s] %d-%s: ", - timestr, level_strings[level], - basename, line, function, - ((this->graph)?this->graph->id:0), domain); - if (-1 == ret) { - goto unlock; - } + ret = vasprintf (&str2, fmt, ap); + if (-1 == ret) { + goto err; + } - ret = vasprintf (&str2, fmt, ap); - if (-1 == ret) { - goto unlock; - } + va_end (ap); - va_end (ap); + len = strlen (str1); + msg = GF_MALLOC (len + strlen (str2) + 1, gf_common_mt_char); - len = strlen (str1); - msg = GF_MALLOC (len + strlen (str2) + 1, gf_common_mt_char); + strcpy (msg, str1); + strcpy (msg + len, str2); - strcpy (msg, str1); - strcpy (msg + len, str2); + pthread_mutex_lock (&ctx->log.logfile_mutex); + { - if (logfile) { - fprintf (logfile, "%s\n", msg); - fflush (logfile); - } else { + if (ctx->log.logfile) { + fprintf (ctx->log.logfile, "%s\n", msg); + fflush (ctx->log.logfile); + } else if (ctx->log.loglevel >= level) { fprintf (stderr, "%s\n", msg); + fflush (stderr); } #ifdef GF_LINUX_HOST_OS /* We want only serious log in 'syslog', not our debug and trace logs */ - if (gf_log_syslog && level && (level <= GF_LOG_CRITICAL)) + if (ctx->log.gf_log_syslog && level && + (level <= ctx->log.sys_log_level)) syslog ((level-1), "%s\n", msg); #endif } -unlock: - pthread_mutex_unlock (&logfile_mutex); + pthread_mutex_unlock (&ctx->log.logfile_mutex); - if (msg) { - GF_FREE (msg); - } +err: + GF_FREE (msg); - if (str1) - GF_FREE (str1); + GF_FREE (str1); - if (str2) - FREE (str2); + FREE (str2); out: return (0); } +int +_gf_log_eh (const char *function, const char *fmt, ...) +{ + int ret = -1; + va_list ap; + char *str1 = NULL; + char *str2 = NULL; + char *msg = NULL; + xlator_t *this = NULL; + + this = THIS; + + ret = gf_asprintf (&str1, "[%d] %s: ", + ((this->graph)?this->graph->id:0), + function); + if (-1 == ret) { + goto out; + } + + va_start (ap, fmt); + + ret = vasprintf (&str2, fmt, ap); + if (-1 == ret) { + goto out; + } + + va_end (ap); + + msg = GF_MALLOC (strlen (str1) + strlen (str2) + 1, gf_common_mt_char); + if (!msg) { + ret = -1; + goto out; + } + + strcpy (msg, str1); + strcat (msg, str2); + + ret = eh_save_history (this->history, msg); + +out: + GF_FREE (str1); + + /* Use FREE instead of GF_FREE since str2 was allocated by vasprintf */ + if (str2) + FREE (str2); + + return ret; +} int gf_cmd_log_init (const char *filename) { + int fd = -1; + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; + if (!filename){ - gf_log ("glusterd", GF_LOG_CRITICAL, "gf_cmd_log_init: no " + gf_log (this->name, GF_LOG_CRITICAL, "gf_cmd_log_init: no " "filename specified\n"); return -1; } - cmd_log_filename = gf_strdup (filename); - if (!cmd_log_filename) { - gf_log ("glusterd", GF_LOG_CRITICAL, "gf_cmd_log_init: strdup" - " error\n"); + ctx->log.cmd_log_filename = gf_strdup (filename); + if (!ctx->log.cmd_log_filename) { + gf_log (this->name, GF_LOG_CRITICAL, + "gf_cmd_log_init: strdup error\n"); + return -1; + } + /* close and reopen cmdlogfile for log rotate*/ + if (ctx->log.cmdlogfile) { + fclose (ctx->log.cmdlogfile); + ctx->log.cmdlogfile = NULL; + } + + fd = open (ctx->log.cmd_log_filename, + O_CREAT | O_RDONLY, S_IRUSR | S_IWUSR); + if (fd < 0) { + gf_log (this->name, GF_LOG_CRITICAL, + "%s", strerror (errno)); return -1; } + close (fd); - cmdlogfile = fopen (cmd_log_filename, "a"); - if (!cmdlogfile){ - gf_log ("glusterd", GF_LOG_CRITICAL, + ctx->log.cmdlogfile = fopen (ctx->log.cmd_log_filename, "a"); + if (!ctx->log.cmdlogfile){ + gf_log (this->name, GF_LOG_CRITICAL, "gf_cmd_log_init: failed to open logfile \"%s\" " - "(%s)\n", cmd_log_filename, strerror (errno)); + "(%s)\n", ctx->log.cmd_log_filename, strerror (errno)); return -1; } return 0; @@ -617,17 +1734,18 @@ gf_cmd_log_init (const char *filename) int gf_cmd_log (const char *domain, const char *fmt, ...) { - va_list ap; - struct tm *tm = NULL; - char timestr[256]; + va_list ap; + char timestr[64]; struct timeval tv = {0,}; - char *str1 = NULL; - char *str2 = NULL; - char *msg = NULL; - size_t len = 0; - int ret = 0; - - if (!cmdlogfile) + char *str1 = NULL; + char *str2 = NULL; + char *msg = NULL; + size_t len = 0; + int ret = 0; + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + if (!ctx->log.cmdlogfile) return -1; @@ -640,12 +1758,10 @@ gf_cmd_log (const char *domain, const char *fmt, ...) ret = gettimeofday (&tv, NULL); if (ret == -1) goto out; - - tm = localtime (&tv.tv_sec); - va_start (ap, fmt); - strftime (timestr, 256, "%Y-%m-%d %H:%M:%S", tm); - snprintf (timestr + strlen (timestr), 256 - strlen (timestr), + gf_time_fmt (timestr, sizeof timestr, tv.tv_sec, gf_timefmt_FT); + snprintf (timestr + strlen (timestr), + GF_LOG_TIMESTR_SIZE - strlen (timestr), ".%"GF_PRI_SUSECONDS, tv.tv_usec); ret = gf_asprintf (&str1, "[%s] %s : ", @@ -667,19 +1783,15 @@ gf_cmd_log (const char *domain, const char *fmt, ...) strcpy (msg, str1); strcpy (msg + len, str2); - fprintf (cmdlogfile, "%s\n", msg); - fflush (cmdlogfile); + fprintf (ctx->log.cmdlogfile, "%s\n", msg); + fflush (ctx->log.cmdlogfile); out: - if (msg) { - GF_FREE (msg); - } + GF_FREE (msg); - if (str1) - GF_FREE (str1); + GF_FREE (str1); - if (str2) - FREE (str2); + FREE (str2); return (0); } |
