glusterfs
2424 строки · 66.8 Кб
1/*
2Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
3This file is part of GlusterFS.
4
5This file is licensed to you under your choice of the GNU Lesser
6General Public License, version 3 or any later version (LGPLv3 or
7later), or the GNU General Public License, version 2 (GPLv2), in all
8cases as published by the Free Software Foundation.
9*/
10
11#include <errno.h>
12#include <pthread.h>
13#include <stdio.h>
14#include <stdarg.h>
15#include <time.h>
16#include <locale.h>
17#include <string.h>
18#include <stdlib.h>
19#include <syslog.h>
20#include <sys/resource.h>
21
22#ifdef HAVE_BACKTRACE
23#include <execinfo.h>
24#else
25#include "execinfo_compat.h"
26#endif
27
28#include <sys/stat.h>
29
30#include "glusterfs/syscall.h"
31
32#define GF_JSON_MSG_LENGTH 8192
33#define GF_SYSLOG_CEE_FORMAT \
34"@cee: {\"msg\": \"%s\", \"gf_code\": \"%u\", \"gf_message\": \"%s\"}"
35#define GF_LOG_CONTROL_FILE "/etc/glusterfs/logger.conf"
36#define GF_LOG_BACKTRACE_DEPTH 5
37#define GF_LOG_BACKTRACE_SIZE 4096
38#define GF_MAX_SLOG_PAIR_COUNT 100
39
40#define SET_LOG_PRIO(level, priority) \
41do { \
42if (GF_LOG_TRACE == (level) || GF_LOG_NONE == (level)) { \
43priority = LOG_DEBUG; \
44} else { \
45priority = (level)-1; \
46} \
47} while (0)
48
49/* extract just the file name from the path */
50#define GET_FILE_NAME_TO_LOG(file, basename) \
51do { \
52basename = strrchr((file), '/'); \
53if (basename) \
54basename++; \
55else \
56basename = (file); \
57} while (0)
58
59#define PRINT_SIZE_CHECK(ret, label, strsize) \
60do { \
61if (ret < 0) \
62goto label; \
63if ((strsize - ret) > 0) { \
64strsize -= ret; \
65} else { \
66ret = 0; \
67goto label; \
68} \
69} while (0)
70
71#include "glusterfs/logging.h"
72#include "glusterfs/timer.h"
73#include "glusterfs/libglusterfs-messages.h"
74
75/* Do not replace gf_log in TEST_LOG with gf_msg, as there is a slight chance
76* that it could lead to an infinite recursion.*/
77#define TEST_LOG(__msg, __args...) \
78gf_log("logging-infra", GF_LOG_DEBUG, __msg, ##__args);
79
80static void
81gf_log_flush_timeout_cbk(void *data);
82
83int
84gf_log_inject_timer_event(glusterfs_ctx_t *ctx);
85
86static void
87gf_log_flush_extra_msgs(gf_log_handle_t *log, uint32_t new);
88
89static int
90log_buf_init(log_buf_t *buf, const char *domain, const char *file,
91const char *function, int32_t line, gf_loglevel_t level,
92int errnum, uint64_t msgid, char *appmsgstr, int graph_id);
93static void
94gf_log_rotate(gf_log_handle_t *log);
95
96static char gf_level_strings[] = {
97' ', /* NONE */
98'M', /* EMERGENCY */
99'A', /* ALERT */
100'C', /* CRITICAL */
101'E', /* ERROR */
102'W', /* WARNING */
103'N', /* NOTICE */
104'I', /* INFO */
105'D', /* DEBUG */
106'T', /* TRACE */
107};
108
109void
110gf_log_logrotate(int signum)
111{
112xlator_t *this = THIS;
113if (this->ctx) {
114this->ctx->log.logrotate = 1;
115this->ctx->log.cmd_history_logrotate = 1;
116}
117}
118
119void
120gf_log_enable_syslog(glusterfs_ctx_t *ctx)
121{
122if (ctx)
123ctx->log.gf_log_syslog = 1;
124}
125
126void
127gf_log_disable_syslog(glusterfs_ctx_t *ctx)
128{
129if (ctx)
130ctx->log.gf_log_syslog = 0;
131}
132
133gf_loglevel_t
134gf_log_get_loglevel(void)
135{
136xlator_t *this = THIS;
137if (this->ctx)
138return this->ctx->log.loglevel;
139else
140/* return global defaults (see gf_log_globals_init) */
141return GF_LOG_INFO;
142}
143
144void
145gf_log_set_loglevel(glusterfs_ctx_t *ctx, gf_loglevel_t level)
146{
147if (ctx)
148ctx->log.loglevel = level;
149}
150
151int
152gf_log_get_localtime(void)
153{
154xlator_t *this = THIS;
155if (this->ctx)
156return this->ctx->log.localtime;
157else
158/* return global defaults (see gf_log_globals_init) */
159return 0;
160}
161
162void
163gf_log_set_localtime(int on_off)
164{
165xlator_t *this = THIS;
166if (this->ctx)
167this->ctx->log.localtime = on_off;
168}
169
170void
171gf_log_flush(glusterfs_ctx_t *ctx)
172{
173if (ctx && ctx->log.logger == gf_logger_glusterlog) {
174pthread_mutex_lock(&ctx->log.logfile_mutex);
175fflush(ctx->log.gf_log_logfile);
176pthread_mutex_unlock(&ctx->log.logfile_mutex);
177}
178
179return;
180}
181
182void
183gf_log_set_xl_loglevel(void *this, gf_loglevel_t level)
184{
185xlator_t *xl = this;
186if (xl)
187xl->loglevel = level;
188}
189
190/* TODO: The following get/set functions are yet not invoked from anywhere
191* in the code. The _intention_ is to pass CLI arguments to various daemons
192* that are started, which would then invoke these set APIs as required.
193*
194* glusterd would read the defaults from its .vol file configuration shipped
195* as a part of the packages distributed.
196*
197* For any gluster* daemon that is started the shipped configuration becomes the
198* default, if a volume has to change its logging format or logger, then a
199* gluster CLI is invoked to set this property for the volume in question.
200*
201* The property is maintained by glusterd, and passed to the daemon as a CLI
202* option, IOW persistence of the option is maintained by glusterd persistent
203* storage (i.e .vol file) only
204*
205* care needs to be taken to configure and start daemons based on the versions
206* that supports these features */
207
208void
209gf_log_set_logformat(gf_log_format_t format)
210{
211xlator_t *this = THIS;
212if (this->ctx)
213this->ctx->log.logformat = format;
214}
215
216void
217gf_log_set_logger(gf_log_logger_t logger)
218{
219xlator_t *this = THIS;
220if (this->ctx)
221this->ctx->log.logger = logger;
222}
223
224gf_loglevel_t
225gf_log_get_xl_loglevel(void *this)
226{
227xlator_t *xl = this;
228if (xl)
229return xl->loglevel;
230return 0;
231}
232
233void
234gf_log_set_log_buf_size(glusterfs_ctx_t *ctx, uint32_t buf_size)
235{
236uint32_t old = 0;
237
238if (ctx == NULL)
239return;
240
241pthread_mutex_lock(&ctx->log.log_buf_lock);
242{
243old = ctx->log.lru_size;
244ctx->log.lru_size = buf_size;
245}
246pthread_mutex_unlock(&ctx->log.log_buf_lock);
247
248/* If the old size is less than/equal to the new size, then do nothing.
249*
250* But if the new size is less than the old size, then
251* a. If the cur size of the buf is less than or equal the new size,
252* then do nothing.
253* b. But if the current size of the buf is greater than the new size,
254* then flush the least recently used (cur size - new_size) msgs
255* to disk.
256*/
257if (buf_size < old)
258gf_log_flush_extra_msgs(&ctx->log, buf_size);
259}
260
261void
262gf_log_set_log_flush_timeout(glusterfs_ctx_t *ctx, uint32_t timeout)
263{
264if (ctx)
265ctx->log.timeout = timeout;
266}
267
268/* If log_buf_init() fails (indicated by a return value of -1),
269* call log_buf_destroy() to clean up memory allocated in heap and to return
270* the log_buf_t object back to its memory pool.
271*/
272static int
273log_buf_init(log_buf_t *buf, const char *domain, const char *file,
274const char *function, int32_t line, gf_loglevel_t level,
275int errnum, uint64_t msgid, char *appmsgstr, int graph_id)
276{
277int ret = -1;
278
279if (!buf || !domain || !file || !function || !appmsgstr)
280goto out;
281
282buf->msg = gf_strdup(appmsgstr);
283if (!buf->msg)
284goto out;
285
286buf->msg_id = msgid;
287buf->errnum = errnum;
288buf->domain = gf_strdup(domain);
289if (!buf->domain)
290goto out;
291
292buf->file = gf_strdup(file);
293if (!buf->file)
294goto out;
295
296buf->function = gf_strdup(function);
297if (!buf->function)
298goto out;
299
300buf->line = line;
301buf->level = level;
302buf->refcount = 0;
303buf->graph_id = graph_id;
304INIT_LIST_HEAD(&buf->msg_list);
305
306ret = 0;
307out:
308return ret;
309}
310
311static int
312log_buf_destroy(log_buf_t *buf)
313{
314if (!buf)
315return -1;
316
317GF_FREE(buf->msg);
318GF_FREE(buf->domain);
319GF_FREE(buf->file);
320GF_FREE(buf->function);
321
322mem_put(buf);
323return 0;
324}
325
326static void
327gf_log_rotate(gf_log_handle_t *log)
328{
329int fd = -1;
330FILE *new_logfile = NULL;
331FILE *old_logfile = NULL;
332
333/* not involving locks on initial check to speed it up */
334if (log->logrotate) {
335/* let only one winner through on races */
336pthread_mutex_lock(&log->logfile_mutex);
337
338if (!log->logrotate) {
339pthread_mutex_unlock(&log->logfile_mutex);
340return;
341} else {
342log->logrotate = 0;
343pthread_mutex_unlock(&log->logfile_mutex);
344}
345
346fd = sys_open(log->filename, O_CREAT | O_WRONLY | O_APPEND,
347S_IRUSR | S_IWUSR);
348if (fd < 0) {
349gf_smsg("logrotate", GF_LOG_ERROR, errno,
350LG_MSG_OPEN_LOGFILE_FAILED, NULL);
351return;
352}
353
354new_logfile = fdopen(fd, "a");
355if (!new_logfile) {
356gf_smsg("logrotate", GF_LOG_CRITICAL, errno,
357LG_MSG_OPEN_LOGFILE_FAILED, "filename=%s", log->filename,
358NULL);
359sys_close(fd);
360return;
361}
362
363pthread_mutex_lock(&log->logfile_mutex);
364{
365if (log->logfile)
366old_logfile = log->logfile;
367
368log->gf_log_logfile = log->logfile = new_logfile;
369}
370pthread_mutex_unlock(&log->logfile_mutex);
371
372if (old_logfile != NULL)
373fclose(old_logfile);
374}
375
376return;
377}
378
379void
380gf_log_globals_fini(void)
381{
382xlator_t *this = THIS;
383/* TODO: Nobody is invoking the fini, but cleanup needs to happen here,
384* needs cleanup for, log.ident, log.filename, closelog, log file close
385* rotate state, possibly under a lock */
386pthread_mutex_destroy(&this->ctx->log.logfile_mutex);
387pthread_mutex_destroy(&this->ctx->log.log_buf_lock);
388}
389
390void
391gf_log_disable_suppression_before_exit(glusterfs_ctx_t *ctx)
392{
393/*
394* First set log buf size to 0. This would ensure two things:
395* i. that all outstanding log messages are flushed to disk, and
396* ii. all subsequent calls to gf_msg will result in the logs getting
397* directly flushed to disk without being buffered.
398*
399* Then, cancel the current log timer event.
400*/
401
402gf_log_set_log_buf_size(ctx, 0);
403pthread_mutex_lock(&ctx->log.log_buf_lock);
404{
405if (ctx->log.log_flush_timer) {
406gf_timer_call_cancel(ctx, ctx->log.log_flush_timer);
407ctx->log.log_flush_timer = NULL;
408}
409}
410pthread_mutex_unlock(&ctx->log.log_buf_lock);
411}
412
413/** gf_log_fini - function to perform the cleanup of the log information
414* @data - glusterfs context
415* @return: success: 0
416* failure: -1
417*/
418int
419gf_log_fini(void *data)
420{
421glusterfs_ctx_t *ctx = data;
422int ret = 0;
423FILE *old_logfile = NULL;
424
425if (ctx == NULL) {
426ret = -1;
427goto out;
428}
429
430gf_log_disable_suppression_before_exit(ctx);
431
432pthread_mutex_lock(&ctx->log.logfile_mutex);
433{
434if (ctx->log.logfile) {
435old_logfile = ctx->log.logfile;
436
437/* Logfile needs to be set to NULL, so that any
438call to gf_log after calling gf_log_fini, will
439log the message to stderr.
440*/
441ctx->log.loglevel = GF_LOG_NONE;
442ctx->log.logfile = NULL;
443}
444}
445pthread_mutex_unlock(&ctx->log.logfile_mutex);
446
447if (old_logfile && (fclose(old_logfile) != 0))
448ret = -1;
449
450GF_FREE(ctx->log.ident);
451GF_FREE(ctx->log.filename);
452
453out:
454return ret;
455}
456
457/**
458* gf_openlog -function to open syslog specific to gluster based on
459* existence of file /etc/glusterfs/logger.conf
460* @ident: optional identification string similar to openlog()
461* @option: optional value to option to openlog(). Passing -1 uses
462* 'LOG_PID | LOG_NDELAY' as default
463* @facility: optional facility code similar to openlog(). Passing -1
464* uses LOG_DAEMON as default
465*
466* @return: void
467*/
468static void
469gf_openlog(const char *ident, int option, int facility)
470{
471int _option = option;
472int _facility = facility;
473char *language = NULL;
474
475if (-1 == _option) {
476_option = LOG_PID | LOG_NDELAY;
477}
478if (-1 == _facility) {
479_facility = LOG_LOCAL1;
480}
481
482/* TODO: Should check for errors here and return appropriately */
483language = setlocale(LC_ALL, "en_US.UTF-8");
484if (!language)
485setlocale(LC_ALL, "");
486
487setlocale(LC_NUMERIC, "C"); /* C-locale for strtod, ... */
488/* close the previous syslog if open as we are changing settings */
489closelog();
490openlog(ident, _option, _facility);
491}
492
493/**
494* _json_escape -function to convert string to json encoded string
495* @str: input string
496* @buf: buffer to store encoded string
497* @len: length of @buf
498*
499* @return: success: last unprocessed character position by pointer in @str
500* failure: NULL
501*
502* Internal function. Heavily inspired by _ul_str_escape() function in
503* libumberlog
504*
505* Sample output:
506* [1] str = "devel error"
507* buf = "devel error"
508* [2] str = "devel error"
509* buf = "devel\terror"
510* [3] str = "I/O error on "/tmp/foo" file"
511* buf = "I/O error on \"/tmp/foo\" file"
512* [4] str = "I/O erroron /tmp/bar file"
513* buf = "I/O error\u001bon /tmp/bar file"
514*
515*/
516static char *
517_json_escape(const char *str, char *buf, size_t len)
518{
519static const unsigned char json_exceptions[UCHAR_MAX + 1] = {
520[0x01] = 1, [0x02] = 1, [0x03] = 1, [0x04] = 1, [0x05] = 1, [0x06] = 1,
521[0x07] = 1, [0x08] = 1, [0x09] = 1, [0x0a] = 1, [0x0b] = 1, [0x0c] = 1,
522[0x0d] = 1, [0x0e] = 1, [0x0f] = 1, [0x10] = 1, [0x11] = 1, [0x12] = 1,
523[0x13] = 1, [0x14] = 1, [0x15] = 1, [0x16] = 1, [0x17] = 1, [0x18] = 1,
524[0x19] = 1, [0x1a] = 1, [0x1b] = 1, [0x1c] = 1, [0x1d] = 1, [0x1e] = 1,
525[0x1f] = 1, ['\\'] = 1, ['"'] = 1};
526static const char json_hex_chars[16] = "0123456789abcdef";
527unsigned char *p = NULL;
528size_t pos = 0;
529
530if (!str || !buf || len <= 0) {
531return NULL;
532}
533
534for (p = (unsigned char *)str; *p && (pos + 1) < len; p++) {
535if (json_exceptions[*p] == 0) {
536buf[pos++] = *p;
537continue;
538}
539
540if ((pos + 2) >= len) {
541break;
542}
543
544switch (*p) {
545case '\b':
546buf[pos++] = '\\';
547buf[pos++] = 'b';
548break;
549case '\n':
550buf[pos++] = '\\';
551buf[pos++] = 'n';
552break;
553case '\r':
554buf[pos++] = '\\';
555buf[pos++] = 'r';
556break;
557case '\t':
558buf[pos++] = '\\';
559buf[pos++] = 't';
560break;
561case '\\':
562buf[pos++] = '\\';
563buf[pos++] = '\\';
564break;
565case '"':
566buf[pos++] = '\\';
567buf[pos++] = '"';
568break;
569default:
570if ((pos + 6) >= len) {
571buf[pos] = '\0';
572return (char *)p;
573}
574buf[pos++] = '\\';
575buf[pos++] = 'u';
576buf[pos++] = '0';
577buf[pos++] = '0';
578buf[pos++] = json_hex_chars[(*p) >> 4];
579buf[pos++] = json_hex_chars[(*p) & 0xf];
580break;
581}
582}
583
584buf[pos] = '\0';
585return (char *)p;
586}
587
588/**
589* gf_syslog -function to submit message to syslog specific to gluster
590* @facility_priority: facility_priority of syslog()
591* @format: optional format string to syslog()
592*
593* @return: void
594*/
595static void
596gf_syslog(int facility_priority, char *format, ...)
597{
598char *msg = NULL;
599char json_msg[GF_JSON_MSG_LENGTH];
600GF_UNUSED char *p = NULL;
601va_list ap;
602
603GF_ASSERT(format);
604
605va_start(ap, format);
606if (vasprintf(&msg, format, ap) != -1) {
607p = _json_escape(msg, json_msg, GF_JSON_MSG_LENGTH);
608syslog(facility_priority, "%s", msg);
609free(msg);
610} else
611syslog(GF_LOG_CRITICAL, "vasprintf() failed, out of memory?");
612va_end(ap);
613}
614
615void
616gf_log_globals_init(void *data, gf_loglevel_t level)
617{
618glusterfs_ctx_t *ctx = data;
619pthread_mutexattr_t log_m_attr;
620int ret;
621
622ret = pthread_mutexattr_init(&log_m_attr);
623
624pthread_mutex_init(&ctx->log.logfile_mutex, NULL);
625
626ctx->log.loglevel = level;
627ctx->log.gf_log_syslog = 1;
628ctx->log.sys_log_level = GF_LOG_CRITICAL;
629ctx->log.logger = gf_logger_glusterlog;
630ctx->log.logformat = gf_logformat_withmsgid;
631ctx->log.lru_size = GF_LOG_LRU_BUFSIZE_DEFAULT;
632ctx->log.timeout = GF_LOG_FLUSH_TIMEOUT_DEFAULT;
633ctx->log.localtime = GF_LOG_LOCALTIME_DEFAULT;
634
635if (ret) {
636pthread_mutex_init(&ctx->log.log_buf_lock, NULL);
637} else {
638pthread_mutexattr_settype(&log_m_attr, PTHREAD_MUTEX_RECURSIVE);
639pthread_mutex_init(&ctx->log.log_buf_lock, &log_m_attr);
640}
641
642INIT_LIST_HEAD(&ctx->log.lru_queue);
643
644#ifdef GF_LINUX_HOST_OS
645/* For the 'syslog' output. one can grep 'GlusterFS' in syslog
646for serious logs */
647openlog("GlusterFS", LOG_PID, LOG_DAEMON);
648#endif
649}
650
651int
652gf_log_init(void *data, const char *file, const char *ident)
653{
654glusterfs_ctx_t *ctx = data;
655int fd = -1;
656struct stat buf;
657
658if (ctx == NULL) {
659fprintf(stderr, "ERROR: ctx is NULL\n");
660return -1;
661}
662if (ident) {
663GF_FREE(ctx->log.ident);
664ctx->log.ident = gf_strdup(ident);
665}
666
667/* we keep the files and the syslog open, so that on logger change, we
668* are ready to log anywhere, that the new value specifies */
669if (ctx->log.ident) {
670gf_openlog(ctx->log.ident, -1, LOG_DAEMON);
671} else {
672gf_openlog(NULL, -1, LOG_DAEMON);
673}
674/* TODO: make FACILITY configurable than LOG_DAEMON */
675if (sys_stat(GF_LOG_CONTROL_FILE, &buf) == 0) {
676/* use syslog logging */
677ctx->log.log_control_file_found = 1;
678} else {
679/* use old style logging */
680ctx->log.log_control_file_found = 0;
681}
682
683if (!file) {
684fprintf(stderr, "ERROR: no filename specified\n");
685return -1;
686}
687
688/* free the (possible) previous filename */
689GF_FREE(ctx->log.filename);
690ctx->log.filename = NULL;
691
692/* close and reopen logfile for log rotate */
693if (ctx->log.logfile) {
694fclose(ctx->log.logfile);
695ctx->log.logfile = NULL;
696ctx->log.gf_log_logfile = NULL;
697}
698
699if (strcmp(file, "-") == 0) {
700int dupfd = -1;
701
702ctx->log.filename = gf_strdup("/dev/stderr");
703if (!ctx->log.filename) {
704fprintf(stderr, "ERROR: strdup failed\n");
705return -1;
706}
707
708dupfd = dup(fileno(stderr));
709if (dupfd == -1) {
710fprintf(stderr, "ERROR: could not dup %d (%s)\n", fileno(stderr),
711strerror(errno));
712return -1;
713}
714
715ctx->log.logfile = fdopen(dupfd, "a");
716if (!ctx->log.logfile) {
717fprintf(stderr, "ERROR: could not fdopen on %d (%s)\n", dupfd,
718strerror(errno));
719sys_close(dupfd);
720return -1;
721}
722} else {
723/* Also create parent dir */
724char *logdir = gf_strdup(file);
725if (!logdir) {
726return -1;
727}
728char *tmp_index = rindex(logdir, '/');
729if (tmp_index) {
730tmp_index[0] = '\0';
731}
732if (mkdir_p(logdir, 0755, true)) {
733/* EEXIST is handled in mkdir_p() itself */
734gf_smsg("logging", GF_LOG_ERROR, 0, LG_MSG_STRDUP_ERROR,
735"logdir=%s", logdir, "errno=%s", strerror(errno), NULL);
736GF_FREE(logdir);
737return -1;
738}
739/* no need of this variable */
740GF_FREE(logdir);
741
742ctx->log.filename = gf_strdup(file);
743if (!ctx->log.filename) {
744fprintf(stderr,
745"ERROR: updating log-filename failed: "
746"%s\n",
747strerror(errno));
748return -1;
749}
750
751fd = sys_open(file, O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR);
752if (fd < 0) {
753fprintf(stderr,
754"ERROR: failed to create logfile"
755" \"%s\" (%s)\n",
756file, strerror(errno));
757return -1;
758}
759
760ctx->log.logfile = fdopen(fd, "a");
761if (!ctx->log.logfile) {
762fprintf(stderr,
763"ERROR: failed to open logfile \"%s\" "
764"(%s)\n",
765file, strerror(errno));
766sys_close(fd);
767return -1;
768}
769}
770
771ctx->log.gf_log_logfile = ctx->log.logfile;
772
773return 0;
774}
775
776void
777set_sys_log_level(gf_loglevel_t level)
778{
779xlator_t *xl = THIS;
780if (xl->ctx)
781xl->ctx->log.sys_log_level = level;
782}
783
784/* Check if we should be logging
785* Return value: _gf_false : Print the log
786* _gf_true : Do not Print the log
787*/
788static gf_boolean_t
789skip_logging(xlator_t *this, gf_loglevel_t level)
790{
791gf_loglevel_t existing_level = this->loglevel ? this->loglevel
792: this->ctx->log.loglevel;
793if (level > existing_level) {
794return _gf_true;
795}
796
797if (level == GF_LOG_NONE) {
798return _gf_true;
799}
800
801return _gf_false;
802}
803
804int
805_gf_log_callingfn(const char *domain, const char *file, const char *function,
806int line, gf_loglevel_t level, const char *fmt, ...)
807{
808const char *basename = NULL;
809xlator_t *this = THIS;
810char *logline = NULL;
811char *msg = NULL;
812char timestr[GF_TIMESTR_SIZE] = {
8130,
814};
815char *callstr = NULL;
816struct timeval tv = {
8170,
818};
819int ret = 0;
820va_list ap;
821glusterfs_ctx_t *ctx = this->ctx;
822
823if (!ctx)
824goto out;
825
826if (skip_logging(this, level))
827goto out;
828
829if (!domain || !file || !function || !fmt) {
830fprintf(stderr, "logging: %s:%s():%d: invalid argument\n", __FILE__,
831__PRETTY_FUNCTION__, __LINE__);
832return -1;
833}
834
835basename = strrchr(file, '/');
836if (basename)
837basename++;
838else
839basename = file;
840
841/*Saving the backtrace to pre-allocated ctx->btbuf
842* to avoid allocating memory from the heap*/
843callstr = gf_backtrace_save(NULL);
844
845va_start(ap, fmt);
846ret = vasprintf(&msg, fmt, ap);
847va_end(ap);
848if (-1 == ret) {
849goto out;
850}
851
852if (ctx->log.log_control_file_found) {
853int priority;
854/* treat GF_LOG_TRACE and GF_LOG_NONE as LOG_DEBUG and
855other level as is */
856if (GF_LOG_TRACE == level || GF_LOG_NONE == level) {
857priority = LOG_DEBUG;
858} else {
859priority = level - 1;
860}
861
862gf_syslog(priority, "[%s:%d:%s] %s %d-%s: %s", basename, line, function,
863callstr, ((this->graph) ? this->graph->id : 0), domain, msg);
864
865goto out;
866}
867
868ret = gettimeofday(&tv, NULL);
869if (-1 == ret)
870goto out;
871
872gf_time_fmt_tv_FT(timestr, sizeof timestr, &tv, &ctx->log);
873
874ret = gf_asprintf(&logline, "[%s] %c [%s:%d:%s] %s %d-%s: %s\n", timestr,
875gf_level_strings[level], basename, line, function,
876callstr, ((this->graph) ? this->graph->id : 0), domain,
877msg);
878if (-1 == ret) {
879goto out;
880}
881
882pthread_mutex_lock(&ctx->log.logfile_mutex);
883{
884if (ctx->log.logfile) {
885fputs(logline, ctx->log.logfile);
886fflush(ctx->log.logfile);
887} else if (ctx->log.loglevel >= level) {
888fputs(logline, stderr);
889fflush(stderr);
890}
891
892#ifdef GF_LINUX_HOST_OS
893/* We want only serious log in 'syslog', not our debug
894and trace logs */
895if (ctx->log.gf_log_syslog && level &&
896(level <= ctx->log.sys_log_level))
897syslog((level - 1), "%s", logline);
898#endif
899}
900
901pthread_mutex_unlock(&ctx->log.logfile_mutex);
902
903out:
904
905GF_FREE(logline);
906
907FREE(msg);
908
909return ret;
910}
911
912static int
913_gf_msg_plain_internal(gf_log_handle_t *log, gf_loglevel_t level,
914const char *msg)
915{
916int priority;
917
918if (!log)
919return -1;
920
921/* log to the configured logging service */
922switch (log->logger) {
923case gf_logger_syslog:
924if (log->log_control_file_found && log->gf_log_syslog) {
925SET_LOG_PRIO(level, priority);
926
927syslog(priority, "%s", msg);
928break;
929}
930/* NOTE: If syslog control file is absent, which is another
931* way to control logging to syslog, then we will fall through
932* to the gluster log. The ideal way to do things would be to
933* not have the extra control file check */
934case gf_logger_glusterlog:
935pthread_mutex_lock(&log->logfile_mutex);
936{
937if (log->logfile) {
938fprintf(log->logfile, "%s\n", msg);
939fflush(log->logfile);
940} else {
941fprintf(stderr, "%s\n", msg);
942fflush(stderr);
943}
944
945#ifdef GF_LINUX_HOST_OS
946/* We want only serious logs in 'syslog', not our debug
947* and trace logs */
948if (level && log->gf_log_syslog &&
949(level <= log->sys_log_level))
950syslog((level - 1), "%s\n", msg);
951#endif
952}
953pthread_mutex_unlock(&log->logfile_mutex);
954
955break;
956}
957
958return 0;
959}
960
961int
962_gf_msg_plain(gf_loglevel_t level, const char *fmt, ...)
963{
964xlator_t *this = NULL;
965int ret = 0;
966va_list ap;
967char *msg = NULL;
968glusterfs_ctx_t *ctx = NULL;
969
970this = THIS;
971ctx = this->ctx;
972
973if (!ctx)
974goto out;
975
976if (skip_logging(this, level))
977goto out;
978
979va_start(ap, fmt);
980ret = vasprintf(&msg, fmt, ap);
981va_end(ap);
982if (-1 == ret) {
983goto out;
984}
985
986ret = _gf_msg_plain_internal(&ctx->log, level, msg);
987
988FREE(msg);
989
990out:
991return ret;
992}
993
994int
995_gf_msg_plain_nomem(gf_loglevel_t level, const char *msg)
996{
997xlator_t *this = NULL;
998int ret = 0;
999glusterfs_ctx_t *ctx = NULL;
1000
1001this = THIS;
1002ctx = this->ctx;
1003
1004if (!ctx)
1005goto out;
1006
1007if (skip_logging(this, level))
1008goto out;
1009
1010ret = _gf_msg_plain_internal(&ctx->log, level, msg);
1011
1012out:
1013return ret;
1014}
1015
1016void
1017_gf_msg_backtrace_nomem(gf_loglevel_t level, int stacksize)
1018{
1019xlator_t *this = NULL;
1020glusterfs_ctx_t *ctx = NULL;
1021void *array[200];
1022size_t bt_size = 0;
1023int fd = -1;
1024
1025this = THIS;
1026ctx = this->ctx;
1027
1028if (!ctx)
1029goto out;
1030
1031/* syslog does not have fd support, hence no no-mem variant */
1032if (ctx->log.logger != gf_logger_glusterlog)
1033goto out;
1034
1035if (skip_logging(this, level))
1036goto out;
1037
1038bt_size = backtrace(array, ((stacksize <= 200) ? stacksize : 200));
1039if (!bt_size)
1040goto out;
1041pthread_mutex_lock(&ctx->log.logfile_mutex);
1042{
1043fd = ctx->log.logfile ? fileno(ctx->log.logfile) : fileno(stderr);
1044if (fd != -1) {
1045/* print to the file fd, to prevent any
1046allocations from backtrace_symbols
1047*/
1048backtrace_symbols_fd(&array[0], bt_size, fd);
1049}
1050}
1051pthread_mutex_unlock(&ctx->log.logfile_mutex);
1052
1053out:
1054return;
1055}
1056
1057static int
1058_gf_msg_backtrace(int stacksize, char *callstr, size_t strsize)
1059{
1060int ret = -1;
1061int i = 0;
1062int size = 0;
1063int savstrsize = strsize;
1064void *array[200];
1065char **callingfn = NULL;
1066
1067/* We chop off last 2 anyway, so if request is less than tolerance
1068* nothing to do */
1069if (stacksize < 3)
1070goto out;
1071
1072size = backtrace(array, ((stacksize <= 200) ? stacksize : 200));
1073if ((size - 3) < 0)
1074goto out;
1075if (size)
1076callingfn = backtrace_symbols(&array[2], size - 2);
1077if (!callingfn)
1078goto out;
1079
1080ret = snprintf(callstr, strsize, "(");
1081PRINT_SIZE_CHECK(ret, out, strsize);
1082
1083for ((i = size - 3); i >= 0; i--) {
1084ret = snprintf(callstr + savstrsize - strsize, strsize, "-->%s ",
1085callingfn[i]);
1086PRINT_SIZE_CHECK(ret, out, strsize);
1087}
1088
1089ret = snprintf(callstr + savstrsize - strsize, strsize, ")");
1090PRINT_SIZE_CHECK(ret, out, strsize);
1091out:
1092FREE(callingfn);
1093return ret;
1094}
1095
1096int
1097_gf_msg_nomem(const char *domain, const char *file, const char *function,
1098int line, gf_loglevel_t level, size_t size)
1099{
1100const char *basename = NULL;
1101xlator_t *this = NULL;
1102struct timeval tv = {
11030,
1104};
1105int ret = 0;
1106int fd = -1;
1107char msg[2048] = {
11080,
1109};
1110char timestr[GF_TIMESTR_SIZE] = {
11110,
1112};
1113glusterfs_ctx_t *ctx = NULL;
1114int wlen = 0;
1115int priority;
1116struct rusage r_usage;
1117
1118this = THIS;
1119ctx = this->ctx;
1120
1121if (!ctx)
1122goto out;
1123
1124if (skip_logging(this, level))
1125goto out;
1126
1127if (!domain || !file || !function) {
1128fprintf(stderr, "logging: %s:%s():%d: invalid argument\n", __FILE__,
1129__PRETTY_FUNCTION__, __LINE__);
1130return -1;
1131}
1132
1133GET_FILE_NAME_TO_LOG(file, basename);
1134
1135ret = gettimeofday(&tv, NULL);
1136if (-1 == ret)
1137goto out;
1138gf_time_fmt_tv_FT(timestr, sizeof timestr, &tv, &ctx->log);
1139
1140/* TODO: Currently we print in the enhanced format, with a message ID
1141* of 0. Need to enhance this to support format as configured */
1142wlen = snprintf(
1143msg, sizeof msg,
1144"[%s] %c [MSGID: %" PRIu64
1145"]"
1146" [%s:%d:%s] %s: no memory "
1147"available for size (%" GF_PRI_SIZET
1148") current memory usage in kilobytes %ld"
1149" [call stack follows]\n",
1150timestr, gf_level_strings[level], (uint64_t)0, basename, line, function,
1151domain, size,
1152(!getrusage(RUSAGE_SELF, &r_usage) ? r_usage.ru_maxrss : 0));
1153if (-1 == wlen) {
1154ret = -1;
1155goto out;
1156}
1157
1158/* log to the configured logging service */
1159switch (ctx->log.logger) {
1160case gf_logger_syslog:
1161if (ctx->log.log_control_file_found && ctx->log.gf_log_syslog) {
1162SET_LOG_PRIO(level, priority);
1163
1164/* if syslog allocates, then this may fail, but we
1165* cannot do much about it at the moment */
1166/* There is no fd for syslog, hence no stack printed */
1167syslog(priority, "%s", msg);
1168break;
1169}
1170/* NOTE: If syslog control file is absent, which is another
1171* way to control logging to syslog, then we will fall through
1172* to the gluster log. The ideal way to do things would be to
1173* not have the extra control file check */
1174case gf_logger_glusterlog:
1175pthread_mutex_lock(&ctx->log.logfile_mutex);
1176{
1177fd = ctx->log.logfile ? fileno(ctx->log.logfile)
1178: fileno(stderr);
1179if (fd == -1) {
1180pthread_mutex_unlock(&ctx->log.logfile_mutex);
1181goto out;
1182}
1183
1184/* write directly to the fd to prevent out of order
1185* message and stack */
1186ret = sys_write(fd, msg, wlen);
1187if (ret == -1) {
1188pthread_mutex_unlock(&ctx->log.logfile_mutex);
1189goto out;
1190}
1191#ifdef GF_LINUX_HOST_OS
1192/* We want only serious log in 'syslog', not our debug
1193* and trace logs */
1194if (ctx->log.gf_log_syslog && level &&
1195(level <= ctx->log.sys_log_level))
1196syslog((level - 1), "%s\n", msg);
1197#endif
1198}
1199pthread_mutex_unlock(&ctx->log.logfile_mutex);
1200
1201_gf_msg_backtrace_nomem(level, GF_LOG_BACKTRACE_DEPTH);
1202
1203break;
1204}
1205
1206out:
1207return ret;
1208}
1209
1210static int
1211gf_log_syslog(const char *domain, const char *file, const char *function,
1212int32_t line, gf_loglevel_t level, int errnum, uint64_t msgid,
1213char *appmsgstr, char *callstr, int graph_id, gf_log_format_t fmt)
1214{
1215int priority;
1216
1217SET_LOG_PRIO(level, priority);
1218
1219/* log with appropriate format */
1220switch (fmt) {
1221case gf_logformat_traditional:
1222if (!callstr) {
1223if (errnum)
1224syslog(priority, "[%s:%d:%s] %d-%s: %s [%s]", file, line,
1225function, graph_id, domain, appmsgstr,
1226strerror(errnum));
1227else
1228syslog(priority, "[%s:%d:%s] %d-%s: %s", file, line,
1229function, graph_id, domain, appmsgstr);
1230} else {
1231if (errnum)
1232syslog(priority,
1233"[%s:%d:%s] %s %d-%s:"
1234" %s [%s]",
1235file, line, function, callstr, graph_id, domain,
1236appmsgstr, strerror(errnum));
1237else
1238syslog(priority, "[%s:%d:%s] %s %d-%s: %s", file, line,
1239function, callstr, graph_id, domain, appmsgstr);
1240}
1241break;
1242case gf_logformat_withmsgid:
1243if (!callstr) {
1244if (errnum)
1245syslog(priority,
1246"[MSGID: %" PRIu64
1247"]"
1248" [%s:%d:%s] %d-%s: %s [%s]",
1249msgid, file, line, function, graph_id, domain,
1250appmsgstr, strerror(errnum));
1251else
1252syslog(priority,
1253"[MSGID: %" PRIu64
1254"]"
1255" [%s:%d:%s] %d-%s: %s",
1256msgid, file, line, function, graph_id, domain,
1257appmsgstr);
1258} else {
1259if (errnum)
1260syslog(priority,
1261"[MSGID: %" PRIu64
1262"]"
1263" [%s:%d:%s] %s %d-%s: %s [%s]",
1264msgid, file, line, function, callstr, graph_id,
1265domain, appmsgstr, strerror(errnum));
1266else
1267syslog(priority,
1268"[MSGID: %" PRIu64
1269"]"
1270" [%s:%d:%s] %s %d-%s: %s",
1271msgid, file, line, function, callstr, graph_id,
1272domain, appmsgstr);
1273}
1274break;
1275case gf_logformat_cee:
1276/* TODO: Enhance CEE with additional parameters */
1277gf_syslog(priority, "[%s:%d:%s] %d-%s: %s", file, line, function,
1278graph_id, domain, appmsgstr);
1279break;
1280
1281default:
1282/* NOTE: should not get here without logging */
1283break;
1284}
1285
1286/* TODO: There can be no errors from gf_syslog? */
1287return 0;
1288}
1289
1290static int
1291gf_log_glusterlog(gf_log_handle_t *log, const char *domain, const char *file,
1292const char *function, int32_t line, gf_loglevel_t level,
1293int errnum, uint64_t msgid, char *appmsgstr, char *callstr,
1294struct timeval tv, int graph_id, gf_log_format_t fmt)
1295{
1296char timestr[GF_TIMESTR_SIZE] = {
12970,
1298};
1299char *header = NULL;
1300char *footer = NULL;
1301int ret = 0;
1302
1303/* rotate if required */
1304gf_log_rotate(log);
1305
1306/* format the time stamp */
1307gf_time_fmt_tv_FT(timestr, sizeof timestr, &tv, log);
1308
1309/* generate footer */
1310if (errnum) {
1311ret = gf_asprintf(&footer, " [%s]\n", strerror(errnum));
1312} else {
1313ret = gf_asprintf(&footer, " \n");
1314}
1315if (-1 == ret) {
1316goto err;
1317}
1318
1319/* generate message, inc. the header */
1320if (fmt == gf_logformat_traditional) {
1321if (!callstr) {
1322ret = gf_asprintf(&header,
1323"[%s] %c [%s:%d:%s]"
1324" %d-%s: %s",
1325timestr, gf_level_strings[level], file, line,
1326function, graph_id, domain, appmsgstr);
1327} else {
1328ret = gf_asprintf(&header,
1329"[%s] %c [%s:%d:%s] %s"
1330" %d-%s: %s",
1331timestr, gf_level_strings[level], file, line,
1332function, callstr, graph_id, domain, appmsgstr);
1333}
1334} else { /* gf_logformat_withmsgid */
1335/* CEE log format unsupported in logger_glusterlog, so just
1336* print enhanced log format */
1337if (!callstr) {
1338ret = gf_asprintf(&header,
1339"[%s] %c [MSGID: %" PRIu64
1340"]"
1341" [%s:%d:%s] %d-%s: %s",
1342timestr, gf_level_strings[level], msgid, file,
1343line, function, graph_id, domain, appmsgstr);
1344} else {
1345ret = gf_asprintf(&header,
1346"[%s] %c [MSGID: %" PRIu64
1347"]"
1348" [%s:%d:%s] %s %d-%s: %s",
1349timestr, gf_level_strings[level], msgid, file,
1350line, function, callstr, graph_id, domain,
1351appmsgstr);
1352}
1353}
1354if (-1 == ret) {
1355goto err;
1356}
1357
1358/* send the full message to log */
1359
1360pthread_mutex_lock(&log->logfile_mutex);
1361{
1362if (log->logfile) {
1363fprintf(log->logfile, "%s%s", header, footer);
1364fflush(log->logfile);
1365} else if (log->loglevel >= level) {
1366fprintf(stderr, "%s%s", header, footer);
1367fflush(stderr);
1368}
1369
1370#ifdef GF_LINUX_HOST_OS
1371/* We want only serious logs in 'syslog', not our debug
1372* and trace logs */
1373if (log->gf_log_syslog && level && (level <= log->sys_log_level)) {
1374syslog((level - 1), "%s%s", header, footer);
1375}
1376#endif
1377}
1378
1379/* TODO: Plugin in memory log buffer retention here. For logs not
1380* flushed during cores, it would be useful to retain some of the last
1381* few messages in memory */
1382pthread_mutex_unlock(&log->logfile_mutex);
1383ret = 0;
1384
1385err:
1386GF_FREE(header);
1387GF_FREE(footer);
1388
1389return ret;
1390}
1391
1392static int
1393gf_syslog_log_repetitions(log_buf_t *buf, gf_log_handle_t *log)
1394{
1395int priority;
1396char timestr_latest[GF_TIMESTR_SIZE] = {
13970,
1398};
1399char timestr_oldest[GF_TIMESTR_SIZE] = {
14000,
1401};
1402
1403SET_LOG_PRIO(buf->level, priority);
1404
1405gf_time_fmt_tv_FT(timestr_latest, sizeof timestr_latest, &buf->latest, log);
1406gf_time_fmt_tv_FT(timestr_oldest, sizeof timestr_oldest, &buf->oldest, log);
1407
1408if (buf->errnum) {
1409syslog(priority,
1410"The message \"[MSGID: %" PRIu64
1411"] [%s:%d:%s] "
1412"%d-%s: %s [%s] \" repeated %d times between %s"
1413" and %s",
1414buf->msg_id, buf->file, buf->line, buf->function, buf->graph_id,
1415buf->domain, buf->msg, strerror(buf->errnum), buf->refcount,
1416timestr_oldest, timestr_latest);
1417} else {
1418syslog(priority,
1419"The message \"[MSGID: %" PRIu64
1420"] [%s:%d:%s] "
1421"%d-%s: %s \" repeated %d times between %s"
1422" and %s",
1423buf->msg_id, buf->file, buf->line, buf->function, buf->graph_id,
1424buf->domain, buf->msg, buf->refcount, timestr_oldest,
1425timestr_latest);
1426}
1427return 0;
1428}
1429
1430static int
1431gf_glusterlog_log_repetitions(log_buf_t *buf, gf_log_handle_t *log)
1432{
1433int ret = 0;
1434char timestr_latest[GF_TIMESTR_SIZE] = {
14350,
1436};
1437char timestr_oldest[GF_TIMESTR_SIZE] = {
14380,
1439};
1440char errstr[256] = {
14410,
1442};
1443char *header = NULL;
1444char *footer = NULL;
1445
1446gf_log_rotate(log);
1447
1448ret = gf_asprintf(&header,
1449"The message \"%c [MSGID: %" PRIu64
1450"]"
1451" [%s:%d:%s] %d-%s: %s",
1452gf_level_strings[buf->level], buf->msg_id, buf->file,
1453buf->line, buf->function, buf->graph_id, buf->domain,
1454buf->msg);
1455if (-1 == ret) {
1456goto err;
1457}
1458
1459gf_time_fmt_tv_FT(timestr_latest, sizeof timestr_latest, &buf->latest, log);
1460
1461gf_time_fmt_tv_FT(timestr_oldest, sizeof timestr_oldest, &buf->oldest, log);
1462
1463if (buf->errnum)
1464snprintf(errstr, sizeof(errstr) - 1, " [%s]", strerror(buf->errnum));
1465
1466ret = gf_asprintf(&footer, "%s\" repeated %d times between [%s] and [%s]",
1467errstr, buf->refcount, timestr_oldest, timestr_latest);
1468if (-1 == ret) {
1469ret = -1;
1470goto err;
1471}
1472
1473pthread_mutex_lock(&log->logfile_mutex);
1474{
1475if (log->logfile) {
1476fprintf(log->logfile, "%s%s\n", header, footer);
1477fflush(log->logfile);
1478} else if (log->loglevel >= buf->level) {
1479fprintf(stderr, "%s%s\n", header, footer);
1480fflush(stderr);
1481}
1482
1483#ifdef GF_LINUX_HOST_OS
1484/* We want only serious logs in 'syslog', not our debug
1485* and trace logs */
1486if (log->gf_log_syslog && buf->level &&
1487(buf->level <= log->sys_log_level))
1488syslog((buf->level - 1), "%s%s\n", header, footer);
1489#endif
1490}
1491
1492/* TODO: Plugin in memory log buffer retention here. For logs not
1493* flushed during cores, it would be useful to retain some of the last
1494* few messages in memory */
1495pthread_mutex_unlock(&log->logfile_mutex);
1496ret = 0;
1497
1498err:
1499GF_FREE(header);
1500GF_FREE(footer);
1501
1502return ret;
1503}
1504
1505static int
1506gf_log_print_with_repetitions(log_buf_t *buf, gf_log_handle_t *log)
1507{
1508int ret = -1;
1509gf_log_logger_t logger = log->logger;
1510
1511switch (logger) {
1512case gf_logger_syslog:
1513if (log->log_control_file_found && log->gf_log_syslog) {
1514ret = gf_syslog_log_repetitions(buf, log);
1515break;
1516}
1517/* NOTE: If syslog control file is absent, which is another
1518* way to control logging to syslog, then we will fall through
1519* to the gluster log. The ideal way to do things would be to
1520* not have the extra control file check */
1521
1522case gf_logger_glusterlog:
1523ret = gf_glusterlog_log_repetitions(buf, log);
1524break;
1525}
1526
1527return ret;
1528}
1529
1530static int
1531gf_log_print_plain_fmt(gf_log_handle_t *log, const char *domain,
1532const char *file, const char *function, int32_t line,
1533gf_loglevel_t level, int errnum, uint64_t msgid,
1534char *appmsgstr, char *callstr, struct timeval tv,
1535int graph_id, gf_log_format_t fmt)
1536{
1537int ret = -1;
1538gf_log_logger_t logger = 0;
1539
1540logger = log->logger;
1541
1542/* log to the configured logging service */
1543switch (logger) {
1544case gf_logger_syslog:
1545if (log->log_control_file_found && log->gf_log_syslog) {
1546ret = gf_log_syslog(domain, file, function, line, level, errnum,
1547msgid, appmsgstr, callstr, graph_id, fmt);
1548break;
1549}
1550/* NOTE: If syslog control file is absent, which is another
1551* way to control logging to syslog, then we will fall through
1552* to the gluster log. The ideal way to do things would be to
1553* not have the extra control file check */
1554case gf_logger_glusterlog:
1555ret = gf_log_glusterlog(log, domain, file, function, line, level,
1556errnum, msgid, appmsgstr, callstr, tv,
1557graph_id, fmt);
1558break;
1559}
1560
1561return ret;
1562}
1563
1564static void
1565gf_log_flush_message(log_buf_t *buf, gf_log_handle_t *log)
1566{
1567if (buf->refcount == 1) {
1568(void)gf_log_print_plain_fmt(log, buf->domain, buf->file, buf->function,
1569buf->line, buf->level, buf->errnum,
1570buf->msg_id, buf->msg, NULL, buf->latest,
1571buf->graph_id, gf_logformat_withmsgid);
1572}
1573
1574if (buf->refcount > 1) {
1575gf_log_print_with_repetitions(buf, log);
1576}
1577return;
1578}
1579
1580static void
1581gf_log_flush_list(struct list_head *copy, gf_log_handle_t *log)
1582{
1583log_buf_t *iter = NULL;
1584log_buf_t *tmp = NULL;
1585
1586list_for_each_entry_safe(iter, tmp, copy, msg_list)
1587{
1588gf_log_flush_message(iter, log);
1589list_del_init(&iter->msg_list);
1590log_buf_destroy(iter);
1591}
1592}
1593
1594static void
1595gf_log_flush_msgs(gf_log_handle_t *log)
1596{
1597struct list_head copy;
1598
1599INIT_LIST_HEAD(©);
1600
1601pthread_mutex_lock(&log->log_buf_lock);
1602{
1603list_splice_init(&log->lru_queue, ©);
1604log->lru_cur_size = 0;
1605}
1606pthread_mutex_unlock(&log->log_buf_lock);
1607
1608gf_log_flush_list(©, log);
1609
1610return;
1611}
1612
1613static void
1614gf_log_flush_extra_msgs(gf_log_handle_t *log, uint32_t new)
1615{
1616int count = 0;
1617int i = 0;
1618log_buf_t *iter = NULL;
1619log_buf_t *tmp = NULL;
1620struct list_head copy;
1621
1622INIT_LIST_HEAD(©);
1623
1624/* If the number of outstanding log messages does not cause list
1625* overflow even after reducing the size of the list, then do nothing.
1626* Otherwise (that is if there are more items in the list than there
1627* need to be after reducing its size), move the least recently used
1628* 'diff' elements to be flushed into a separate list...
1629*/
1630
1631pthread_mutex_lock(&log->log_buf_lock);
1632{
1633if (log->lru_cur_size <= new)
1634goto unlock;
1635count = log->lru_cur_size - new;
1636list_for_each_entry_safe(iter, tmp, &log->lru_queue, msg_list)
1637{
1638if (i == count)
1639break;
1640
1641list_del_init(&iter->msg_list);
1642list_add_tail(&iter->msg_list, ©);
1643i++;
1644}
1645log->lru_cur_size = log->lru_cur_size - count;
1646}
1647// ... quickly unlock ...
1648unlock:
1649pthread_mutex_unlock(&log->log_buf_lock);
1650if (list_empty(©))
1651return;
1652
1653TEST_LOG(
1654"Log buffer size reduced. About to flush %d extra log "
1655"messages",
1656count);
1657// ... and then flush them outside the lock.
1658gf_log_flush_list(©, log);
1659TEST_LOG("Just flushed %d extra log messages", count);
1660
1661return;
1662}
1663
1664static int
1665__gf_log_inject_timer_event(glusterfs_ctx_t *ctx)
1666{
1667int ret = -1;
1668struct timespec timeout = {
16690,
1670};
1671
1672if (ctx->log.log_flush_timer) {
1673gf_timer_call_cancel(ctx, ctx->log.log_flush_timer);
1674ctx->log.log_flush_timer = NULL;
1675}
1676
1677timeout.tv_sec = ctx->log.timeout;
1678timeout.tv_nsec = 0;
1679
1680TEST_LOG("Starting timer now. Timeout = %u, current buf size = %d",
1681ctx->log.timeout, ctx->log.lru_size);
1682ctx->log.log_flush_timer = gf_timer_call_after(
1683ctx, timeout, gf_log_flush_timeout_cbk, (void *)ctx);
1684if (!ctx->log.log_flush_timer)
1685goto out;
1686
1687ret = 0;
1688
1689out:
1690return ret;
1691}
1692
1693int
1694gf_log_inject_timer_event(glusterfs_ctx_t *ctx)
1695{
1696int ret = -1;
1697
1698if (!ctx)
1699return -1;
1700
1701pthread_mutex_lock(&ctx->log.log_buf_lock);
1702{
1703ret = __gf_log_inject_timer_event(ctx);
1704}
1705pthread_mutex_unlock(&ctx->log.log_buf_lock);
1706
1707return ret;
1708}
1709
1710static void
1711gf_log_flush_timeout_cbk(void *data)
1712{
1713glusterfs_ctx_t *ctx = NULL;
1714
1715ctx = (glusterfs_ctx_t *)data;
1716
1717TEST_LOG(
1718"Log timer timed out. About to flush outstanding messages if "
1719"present");
1720gf_log_flush_msgs(&ctx->log);
1721
1722(void)gf_log_inject_timer_event(ctx);
1723
1724return;
1725}
1726
1727static int
1728_gf_msg_internal(glusterfs_ctx_t *ctx, const char *domain, const char *file,
1729const char *function, int32_t line, gf_loglevel_t level,
1730int errnum, uint64_t msgid, char *appmsgstr, char *callstr,
1731int graph_id)
1732{
1733int ret = -1;
1734uint32_t size = 0;
1735const char *basename = NULL;
1736log_buf_t *iter = NULL;
1737log_buf_t *buf_tmp = NULL;
1738log_buf_t *buf_new = NULL;
1739log_buf_t *first = NULL;
1740struct timeval tv = {
17410,
1742};
1743gf_boolean_t flush_lru = _gf_false;
1744gf_boolean_t flush_logged_msg = _gf_false;
1745
1746GET_FILE_NAME_TO_LOG(file, basename);
1747
1748ret = gettimeofday(&tv, NULL);
1749if (ret)
1750goto out;
1751
1752/* If this function is called via _gf_msg_callingfn () (indicated by a
1753* non-NULL callstr), or if the logformat is traditional, flush the
1754* message directly to disk.
1755*/
1756
1757if ((callstr) || (ctx->log.logformat == gf_logformat_traditional)) {
1758ret = gf_log_print_plain_fmt(
1759&ctx->log, domain, basename, function, line, level, errnum, msgid,
1760appmsgstr, callstr, tv, graph_id, gf_logformat_traditional);
1761goto out;
1762}
1763
1764pthread_mutex_lock(&ctx->log.log_buf_lock);
1765{
1766/* Check if the msg being logged is already part of the list */
1767list_for_each_entry_safe_reverse(iter, buf_tmp, &ctx->log.lru_queue,
1768msg_list)
1769{
1770if (first == NULL)
1771// Remember the first (lru) element in first ptr
1772first = iter;
1773
1774/* Try to fail the search early on by doing the less
1775* expensive integer comparisons and continue to string
1776* parameter comparisons only after all int parameters
1777* are found to be matching.
1778*/
1779if (line != iter->line)
1780continue;
1781
1782if (errnum != iter->errnum)
1783continue;
1784
1785if (msgid != iter->msg_id)
1786continue;
1787
1788if (level != iter->level)
1789continue;
1790
1791if (graph_id != iter->graph_id)
1792continue;
1793
1794if (strcmp(domain, iter->domain))
1795continue;
1796
1797if (strcmp(basename, iter->file))
1798continue;
1799
1800if (strcmp(function, iter->function))
1801continue;
1802
1803if (strcmp(appmsgstr, iter->msg))
1804continue;
1805
1806// Ah! Found a match!
1807list_move_tail(&iter->msg_list, &ctx->log.lru_queue);
1808iter->refcount++;
1809// Update the 'latest' timestamp.
1810memcpy((void *)&(iter->latest), (void *)&tv,
1811sizeof(struct timeval));
1812pthread_mutex_unlock(&ctx->log.log_buf_lock);
1813ret = 0;
1814goto out;
1815}
1816// did not find a match ...
1817
1818size = ctx->log.lru_size;
1819/* If the upper limit on the log buf size is 0, flush the msg to
1820* disk directly after unlock. There's no need to buffer the
1821* msg here.
1822*/
1823if (size == 0) {
1824flush_logged_msg = _gf_true;
1825goto unlock;
1826} else if (((ctx->log.lru_cur_size + 1) > size) && (first)) {
1827/* If the list is full, flush the lru msg to disk and also
1828* release it after unlock, and ...
1829* */
1830if (first->refcount >= 1)
1831TEST_LOG(
1832"Buffer overflow of a buffer whose size limit "
1833"is %d. About to flush least recently used log"
1834" message to disk",
1835size);
1836list_del_init(&first->msg_list);
1837ctx->log.lru_cur_size--;
1838flush_lru = _gf_true;
1839}
1840/* create a new list element, initialise and enqueue it.
1841* Additionally, this being the first occurrence of the msg,
1842* log it directly to disk after unlock. */
1843buf_new = mem_get0(ctx->logbuf_pool);
1844if (!buf_new) {
1845ret = -1;
1846goto unlock;
1847}
1848ret = log_buf_init(buf_new, domain, basename, function, line, level,
1849errnum, msgid, appmsgstr, graph_id);
1850if (ret) {
1851log_buf_destroy(buf_new);
1852goto unlock;
1853}
1854
1855memcpy((void *)&(buf_new->latest), (void *)&tv, sizeof(struct timeval));
1856memcpy((void *)&(buf_new->oldest), (void *)&tv, sizeof(struct timeval));
1857
1858list_add_tail(&buf_new->msg_list, &ctx->log.lru_queue);
1859ctx->log.lru_cur_size++;
1860flush_logged_msg = _gf_true;
1861ret = 0;
1862}
1863unlock:
1864pthread_mutex_unlock(&ctx->log.log_buf_lock);
1865
1866/* Value of @ret is a don't-care below since irrespective of success or
1867* failure post setting of @flush_lru, @first must be flushed and freed.
1868*/
1869if (flush_lru) {
1870gf_log_flush_message(first, &ctx->log);
1871log_buf_destroy(first);
1872}
1873/* Similarly, irrespective of whether all operations since setting of
1874* @flush_logged_msg were successful or not, flush the message being
1875* logged to disk in the plain format.
1876*/
1877if (flush_logged_msg) {
1878ret = gf_log_print_plain_fmt(
1879&ctx->log, domain, basename, function, line, level, errnum, msgid,
1880appmsgstr, callstr, tv, graph_id, gf_logformat_withmsgid);
1881}
1882
1883out:
1884return ret;
1885}
1886
1887int
1888_gf_msg(const char *domain, const char *file, const char *function,
1889int32_t line, gf_loglevel_t level, int errnum, int trace,
1890uint64_t msgid, const char *fmt, ...)
1891{
1892int ret = 0;
1893char *msgstr = NULL;
1894va_list ap;
1895xlator_t *this = THIS;
1896glusterfs_ctx_t *ctx = NULL;
1897char *callstr = NULL;
1898int log_inited = 0;
1899
1900if (this == NULL)
1901return -1;
1902
1903ctx = this->ctx;
1904if (ctx == NULL) {
1905/* messages before context initialization are ignored */
1906return -1;
1907}
1908
1909/* check if we should be logging */
1910if (skip_logging(this, level))
1911goto out;
1912
1913/* in args check */
1914if (!domain || !file || !function || !fmt) {
1915fprintf(stderr, "logging: %s:%s():%d: invalid argument\n", __FILE__,
1916__PRETTY_FUNCTION__, __LINE__);
1917return -1;
1918}
1919
1920/* form the message */
1921va_start(ap, fmt);
1922ret = vasprintf(&msgstr, fmt, ap);
1923va_end(ap);
1924
1925/* log */
1926if (ret != -1) {
1927if (trace) {
1928callstr = GF_MALLOC(GF_LOG_BACKTRACE_SIZE, gf_common_mt_char);
1929if (callstr == NULL)
1930return -1;
1931
1932ret = _gf_msg_backtrace(GF_LOG_BACKTRACE_DEPTH, callstr,
1933GF_LOG_BACKTRACE_SIZE);
1934if (ret < 0) {
1935GF_FREE(callstr);
1936callstr = NULL;
1937}
1938}
1939
1940pthread_mutex_lock(&ctx->log.logfile_mutex);
1941{
1942if (ctx->log.logfile) {
1943log_inited = 1;
1944}
1945}
1946pthread_mutex_unlock(&ctx->log.logfile_mutex);
1947
1948if (!log_inited && ctx->log.gf_log_syslog) {
1949ret = gf_log_syslog(domain, file, function, line, level, errnum,
1950msgid, msgstr, (callstr ? callstr : NULL),
1951(this->graph) ? this->graph->id : 0,
1952gf_logformat_traditional);
1953} else {
1954ret = _gf_msg_internal(ctx, domain, file, function, line, level,
1955errnum, msgid, msgstr,
1956(callstr ? callstr : NULL),
1957(this->graph) ? this->graph->id : 0);
1958}
1959} else {
1960/* man (3) vasprintf states on error strp contents
1961* are undefined, be safe */
1962msgstr = NULL;
1963}
1964if (callstr)
1965GF_FREE(callstr);
1966FREE(msgstr);
1967
1968out:
1969return ret;
1970}
1971
1972/* TODO: Deprecate (delete) _gf_log, _gf_log_callingfn,
1973* once messages are changed to use _gf_msgXXX APIs for logging */
1974int
1975_gf_log(const char *domain, const char *file, const char *function, int line,
1976gf_loglevel_t level, const char *fmt, ...)
1977{
1978const char *basename = NULL;
1979FILE *new_logfile = NULL;
1980va_list ap;
1981char timestr[GF_TIMESTR_SIZE] = {
19820,
1983};
1984struct timeval tv = {
19850,
1986};
1987char *logline = NULL;
1988char *msg = NULL;
1989int ret = 0;
1990int fd = -1;
1991xlator_t *this = THIS;
1992glusterfs_ctx_t *ctx = this->ctx;
1993
1994if (!ctx)
1995goto out;
1996
1997if (skip_logging(this, level))
1998goto out;
1999
2000if (!domain || !file || !function || !fmt) {
2001fprintf(stderr, "logging: %s:%s():%d: invalid argument\n", __FILE__,
2002__PRETTY_FUNCTION__, __LINE__);
2003return -1;
2004}
2005
2006basename = strrchr(file, '/');
2007if (basename)
2008basename++;
2009else
2010basename = file;
2011
2012va_start(ap, fmt);
2013ret = vasprintf(&msg, fmt, ap);
2014va_end(ap);
2015if (-1 == ret) {
2016goto err;
2017}
2018
2019if (ctx->log.log_control_file_found) {
2020int priority;
2021/* treat GF_LOG_TRACE and GF_LOG_NONE as LOG_DEBUG and
2022other level as is */
2023if (GF_LOG_TRACE == level || GF_LOG_NONE == level) {
2024priority = LOG_DEBUG;
2025} else {
2026priority = level - 1;
2027}
2028
2029gf_syslog(priority, "[%s:%d:%s] %d-%s: %s", basename, line, function,
2030((this->graph) ? this->graph->id : 0), domain, msg);
2031goto err;
2032}
2033
2034if (ctx->log.logrotate) {
2035ctx->log.logrotate = 0;
2036
2037fd = sys_open(ctx->log.filename, O_CREAT | O_RDONLY, S_IRUSR | S_IWUSR);
2038if (fd < 0) {
2039gf_smsg("logrotate", GF_LOG_ERROR, errno,
2040LG_MSG_OPEN_LOGFILE_FAILED, NULL);
2041return -1;
2042}
2043sys_close(fd);
2044
2045new_logfile = fopen(ctx->log.filename, "a");
2046if (!new_logfile) {
2047gf_smsg("logrotate", GF_LOG_CRITICAL, errno,
2048LG_MSG_OPEN_LOGFILE_FAILED, "filename=%s",
2049ctx->log.filename, NULL);
2050goto log;
2051}
2052
2053pthread_mutex_lock(&ctx->log.logfile_mutex);
2054{
2055if (ctx->log.logfile)
2056fclose(ctx->log.logfile);
2057
2058ctx->log.gf_log_logfile = ctx->log.logfile = new_logfile;
2059}
2060pthread_mutex_unlock(&ctx->log.logfile_mutex);
2061}
2062
2063log:
2064ret = gettimeofday(&tv, NULL);
2065if (-1 == ret)
2066goto out;
2067
2068gf_time_fmt_tv_FT(timestr, sizeof timestr, &tv, &ctx->log);
2069
2070ret = gf_asprintf(&logline, "[%s] %c [%s:%d:%s] %d-%s: %s\n", timestr,
2071gf_level_strings[level], basename, line, function,
2072((this->graph) ? this->graph->id : 0), domain, msg);
2073if (-1 == ret) {
2074goto err;
2075}
2076
2077pthread_mutex_lock(&ctx->log.logfile_mutex);
2078{
2079if (ctx->log.logfile) {
2080fputs(logline, ctx->log.logfile);
2081fflush(ctx->log.logfile);
2082} else if (ctx->log.loglevel >= level) {
2083fputs(logline, stderr);
2084fflush(stderr);
2085}
2086
2087#ifdef GF_LINUX_HOST_OS
2088/* We want only serious log in 'syslog', not our debug
2089and trace logs */
2090if (ctx->log.gf_log_syslog && level &&
2091(level <= ctx->log.sys_log_level))
2092syslog((level - 1), "%s", logline);
2093#endif
2094}
2095
2096pthread_mutex_unlock(&ctx->log.logfile_mutex);
2097
2098err:
2099GF_FREE(logline);
2100
2101FREE(msg);
2102
2103out:
2104return (0);
2105}
2106
2107int
2108_gf_log_eh(const char *function, const char *fmt, ...)
2109{
2110int ret = -1;
2111va_list ap;
2112char *logline = NULL;
2113char *msg = NULL;
2114xlator_t *this = NULL;
2115
2116this = THIS;
2117
2118va_start(ap, fmt);
2119ret = vasprintf(&msg, fmt, ap);
2120va_end(ap);
2121if (-1 == ret) {
2122goto out;
2123}
2124
2125ret = gf_asprintf(&logline, "[%d] %s: %s",
2126((this->graph) ? this->graph->id : 0), function, msg);
2127if (-1 == ret) {
2128goto out;
2129}
2130
2131ret = eh_save_history(this->history, logline);
2132
2133out:
2134GF_FREE(logline);
2135
2136FREE(msg);
2137
2138return ret;
2139}
2140
2141int
2142gf_cmd_log_init(const char *filename)
2143{
2144int fd = -1;
2145xlator_t *this = NULL;
2146glusterfs_ctx_t *ctx = NULL;
2147
2148this = THIS;
2149ctx = this->ctx;
2150
2151if (!ctx)
2152return -1;
2153
2154if (!filename) {
2155gf_smsg(this->name, GF_LOG_CRITICAL, 0, LG_MSG_FILENAME_NOT_SPECIFIED,
2156"gf_cmd_log_init", NULL);
2157return -1;
2158}
2159
2160ctx->log.cmd_log_filename = gf_strdup(filename);
2161if (!ctx->log.cmd_log_filename) {
2162return -1;
2163}
2164/* close and reopen cmdlogfile for log rotate*/
2165if (ctx->log.cmdlogfile) {
2166fclose(ctx->log.cmdlogfile);
2167ctx->log.cmdlogfile = NULL;
2168}
2169
2170fd = sys_open(ctx->log.cmd_log_filename, O_CREAT | O_WRONLY | O_APPEND,
2171S_IRUSR | S_IWUSR);
2172if (fd < 0) {
2173gf_smsg(this->name, GF_LOG_CRITICAL, errno, LG_MSG_OPEN_LOGFILE_FAILED,
2174"cmd_log_file", NULL);
2175return -1;
2176}
2177
2178ctx->log.cmdlogfile = fdopen(fd, "a");
2179if (!ctx->log.cmdlogfile) {
2180gf_smsg(this->name, GF_LOG_CRITICAL, errno, LG_MSG_OPEN_LOGFILE_FAILED,
2181"gf_cmd_log_init: %s", ctx->log.cmd_log_filename, NULL);
2182sys_close(fd);
2183return -1;
2184}
2185return 0;
2186}
2187
2188int
2189gf_cmd_log(const char *domain, const char *fmt, ...)
2190{
2191va_list ap;
2192char timestr[GF_TIMESTR_SIZE];
2193struct timeval tv = {
21940,
2195};
2196char *logline = NULL;
2197char *msg = NULL;
2198int ret = 0;
2199int fd = -1;
2200glusterfs_ctx_t *ctx = NULL;
2201
2202ctx = THIS->ctx;
2203
2204if (!ctx)
2205return -1;
2206
2207if (!ctx->log.cmdlogfile)
2208return -1;
2209
2210if (!domain || !fmt) {
2211gf_msg_trace("glusterd", 0, "logging: invalid argument\n");
2212return -1;
2213}
2214
2215ret = gettimeofday(&tv, NULL);
2216if (ret == -1)
2217goto out;
2218va_start(ap, fmt);
2219ret = vasprintf(&msg, fmt, ap);
2220va_end(ap);
2221if (ret == -1) {
2222goto out;
2223}
2224
2225gf_time_fmt_tv_FT(timestr, sizeof timestr, &tv, &ctx->log);
2226
2227ret = gf_asprintf(&logline, "[%s] %s : %s\n", timestr, domain, msg);
2228if (ret == -1) {
2229goto out;
2230}
2231
2232/* close and reopen cmdlogfile fd for in case of log rotate*/
2233if (ctx->log.cmd_history_logrotate) {
2234ctx->log.cmd_history_logrotate = 0;
2235
2236if (ctx->log.cmdlogfile) {
2237fclose(ctx->log.cmdlogfile);
2238ctx->log.cmdlogfile = NULL;
2239}
2240
2241fd = sys_open(ctx->log.cmd_log_filename, O_CREAT | O_WRONLY | O_APPEND,
2242S_IRUSR | S_IWUSR);
2243if (fd < 0) {
2244gf_smsg(THIS->name, GF_LOG_CRITICAL, errno,
2245LG_MSG_OPEN_LOGFILE_FAILED, "name=%s",
2246ctx->log.cmd_log_filename, NULL);
2247ret = -1;
2248goto out;
2249}
2250
2251ctx->log.cmdlogfile = fdopen(fd, "a");
2252if (!ctx->log.cmdlogfile) {
2253gf_smsg(THIS->name, GF_LOG_CRITICAL, errno,
2254LG_MSG_OPEN_LOGFILE_FAILED, "name=%s",
2255ctx->log.cmd_log_filename, NULL);
2256ret = -1;
2257sys_close(fd);
2258goto out;
2259}
2260}
2261
2262fputs(logline, ctx->log.cmdlogfile);
2263fflush(ctx->log.cmdlogfile);
2264
2265out:
2266GF_FREE(logline);
2267
2268FREE(msg);
2269
2270return ret;
2271}
2272
2273static int
2274_do_slog_format(int errnum, const char *event, va_list inp, char **msg)
2275{
2276va_list valist_tmp;
2277int i = 0;
2278int j = 0;
2279int k = 0;
2280int ret = 0;
2281char *fmt = NULL;
2282char *buffer = NULL;
2283int num_format_chars = 0;
2284char format_char = '%';
2285char *tmp1 = NULL;
2286char *tmp2 = NULL;
2287char temp_sep[3] = "";
2288
2289tmp2 = gf_strdup("");
2290if (!tmp2) {
2291ret = -1;
2292goto out;
2293}
2294
2295/* Hardcoded value for max key value pairs, exits early */
2296/* from loop if found NULL */
2297for (i = 0; i < GF_MAX_SLOG_PAIR_COUNT; i++) {
2298fmt = va_arg(inp, char *);
2299if (fmt == NULL) {
2300break;
2301}
2302
2303/* Get number of times % is used in input for formatting, */
2304/* this count will be used to skip those many args from the */
2305/* main list and will be used to format inner format */
2306num_format_chars = 0;
2307for (k = 0; fmt[k] != '\0'; k++) {
2308/* If %% is used then that is escaped */
2309if (fmt[k] == format_char && fmt[k + 1] == format_char) {
2310k++;
2311} else if (fmt[k] == format_char) {
2312num_format_chars++;
2313}
2314}
2315
2316tmp1 = gf_strdup(tmp2);
2317if (!tmp1) {
2318ret = -1;
2319goto out;
2320}
2321
2322GF_FREE(tmp2);
2323tmp2 = NULL;
2324
2325if (num_format_chars > 0) {
2326/* Make separate valist and format the string */
2327va_copy(valist_tmp, inp);
2328ret = gf_vasprintf(&buffer, fmt, valist_tmp);
2329if (ret < 0) {
2330va_end(valist_tmp);
2331goto out;
2332}
2333va_end(valist_tmp);
2334
2335for (j = 0; j < num_format_chars; j++) {
2336/* Skip the va_arg value since these values
2337are already used for internal formatting */
2338(void)va_arg(inp, void *);
2339}
2340
2341ret = gf_asprintf(&tmp2, "%s%s{%s}", tmp1, temp_sep, buffer);
2342if (ret < 0)
2343goto out;
2344
2345GF_FREE(buffer);
2346buffer = NULL;
2347} else {
2348ret = gf_asprintf(&tmp2, "%s%s{%s}", tmp1, temp_sep, fmt);
2349if (ret < 0)
2350goto out;
2351}
2352
2353/* Set seperator for next iteration */
2354temp_sep[0] = ',';
2355temp_sep[1] = ' ';
2356temp_sep[2] = 0;
2357
2358GF_FREE(tmp1);
2359tmp1 = NULL;
2360}
2361
2362tmp1 = gf_strdup(tmp2);
2363if (!tmp1) {
2364ret = -1;
2365goto out;
2366}
2367GF_FREE(tmp2);
2368tmp2 = NULL;
2369
2370if (errnum) {
2371ret = gf_asprintf(&tmp2, "%s [%s%s{errno=%d}, {error=%s}]", event, tmp1,
2372temp_sep, errnum, strerror(errnum));
2373} else {
2374ret = gf_asprintf(&tmp2, "%s [%s]", event, tmp1);
2375}
2376
2377if (ret == -1)
2378goto out;
2379
2380*msg = gf_strdup(tmp2);
2381if (!*msg)
2382ret = -1;
2383
2384out:
2385if (buffer)
2386GF_FREE(buffer);
2387
2388if (tmp1)
2389GF_FREE(tmp1);
2390
2391if (tmp2)
2392GF_FREE(tmp2);
2393
2394return ret;
2395}
2396
2397int
2398_gf_smsg(const char *domain, const char *file, const char *function,
2399int32_t line, gf_loglevel_t level, int errnum, int trace,
2400uint64_t msgid, const char *event, ...)
2401{
2402va_list valist;
2403char *msg = NULL;
2404int ret = 0;
2405xlator_t *this = THIS;
2406
2407if (skip_logging(this, level))
2408return ret;
2409
2410va_start(valist, event);
2411ret = _do_slog_format(errnum, event, valist, &msg);
2412if (ret == -1)
2413goto out;
2414
2415/* Pass errnum as zero since it is already formated as required */
2416ret = _gf_msg(domain, file, function, line, level, 0, trace, msgid, "%s",
2417msg);
2418
2419out:
2420va_end(valist);
2421if (msg)
2422GF_FREE(msg);
2423return ret;
2424}
2425