2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
20 * \brief Asterisk Logger
36 #define SYSLOG_NAMES /* so we can map syslog facilities names to their numeric values,
37 from <syslog.h> which is included by logger.h */
42 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
44 static int syslog_level_map[] = {
46 LOG_INFO, /* arbitrary equivalent of LOG_EVENT */
54 #define SYSLOG_NLEVELS 6
56 #include "asterisk/logger.h"
57 #include "asterisk/lock.h"
58 #include "asterisk/options.h"
59 #include "asterisk/channel.h"
60 #include "asterisk/config.h"
61 #include "asterisk/term.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/utils.h"
64 #include "asterisk/manager.h"
66 #define MAX_MSG_QUEUE 200
68 #if defined(__linux__) && !defined(__NR_gettid)
69 #include <asm/unistd.h>
72 #if defined(__linux__) && defined(__NR_gettid)
73 #define GETTID() syscall(__NR_gettid)
75 #define GETTID() getpid()
79 static char dateformat[256] = "%b %e %T"; /* Original Asterisk Format */
81 AST_MUTEX_DEFINE_STATIC(msglist_lock);
82 AST_MUTEX_DEFINE_STATIC(loglock);
83 static int filesize_reload_needed = 0;
84 static int global_logmask = -1;
87 unsigned int queue_log:1;
88 unsigned int event_log:1;
89 } logfiles = { 1, 1 };
91 static struct msglist {
94 } *list = NULL, *last = NULL;
96 static char hostname[MAXHOSTNAMELEN];
105 int logmask; /* What to log to this channel */
106 int disabled; /* If this channel is disabled or not */
107 int facility; /* syslog facility */
108 enum logtypes type; /* Type of log channel */
109 FILE *fileptr; /* logfile logging file pointer */
110 char filename[256]; /* Filename */
111 struct logchannel *next; /* Next channel in chain */
114 static struct logchannel *logchannels = NULL;
116 static int msgcnt = 0;
118 static FILE *eventlog = NULL;
120 static char *levels[] = {
130 static int colors[] = {
140 static int make_components(char *s, int lineno)
146 w = strsep(&stringp, ",");
148 while(*w && (*w < 33))
150 if (!strcasecmp(w, "error"))
151 res |= (1 << __LOG_ERROR);
152 else if (!strcasecmp(w, "warning"))
153 res |= (1 << __LOG_WARNING);
154 else if (!strcasecmp(w, "notice"))
155 res |= (1 << __LOG_NOTICE);
156 else if (!strcasecmp(w, "event"))
157 res |= (1 << __LOG_EVENT);
158 else if (!strcasecmp(w, "debug"))
159 res |= (1 << __LOG_DEBUG);
160 else if (!strcasecmp(w, "verbose"))
161 res |= (1 << __LOG_VERBOSE);
162 else if (!strcasecmp(w, "dtmf"))
163 res |= (1 << __LOG_DTMF);
165 fprintf(stderr, "Logfile Warning: Unknown keyword '%s' at line %d of logger.conf\n", w, lineno);
167 w = strsep(&stringp, ",");
172 static struct logchannel *make_logchannel(char *channel, char *components, int lineno)
174 struct logchannel *chan;
180 if (ast_strlen_zero(channel))
182 chan = malloc(sizeof(struct logchannel));
184 if (!chan) /* Can't allocate memory */
187 memset(chan, 0, sizeof(struct logchannel));
188 if (!strcasecmp(channel, "console")) {
189 chan->type = LOGTYPE_CONSOLE;
190 } else if (!strncasecmp(channel, "syslog", 6)) {
193 * syslog.facility => level,level,level
195 facility = strchr(channel, '.');
196 if(!facility++ || !facility) {
202 * Walk through the list of facilitynames (defined in sys/syslog.h)
203 * to see if we can find the one we have been given
206 cptr = facilitynames;
207 while (cptr->c_name) {
208 if (!strcasecmp(facility, cptr->c_name)) {
209 chan->facility = cptr->c_val;
216 if (!strcasecmp(facility, "kern"))
217 chan->facility = LOG_KERN;
218 else if (!strcasecmp(facility, "USER"))
219 chan->facility = LOG_USER;
220 else if (!strcasecmp(facility, "MAIL"))
221 chan->facility = LOG_MAIL;
222 else if (!strcasecmp(facility, "DAEMON"))
223 chan->facility = LOG_DAEMON;
224 else if (!strcasecmp(facility, "AUTH"))
225 chan->facility = LOG_AUTH;
226 else if (!strcasecmp(facility, "SYSLOG"))
227 chan->facility = LOG_SYSLOG;
228 else if (!strcasecmp(facility, "LPR"))
229 chan->facility = LOG_LPR;
230 else if (!strcasecmp(facility, "NEWS"))
231 chan->facility = LOG_NEWS;
232 else if (!strcasecmp(facility, "UUCP"))
233 chan->facility = LOG_UUCP;
234 else if (!strcasecmp(facility, "CRON"))
235 chan->facility = LOG_CRON;
236 else if (!strcasecmp(facility, "LOCAL0"))
237 chan->facility = LOG_LOCAL0;
238 else if (!strcasecmp(facility, "LOCAL1"))
239 chan->facility = LOG_LOCAL1;
240 else if (!strcasecmp(facility, "LOCAL2"))
241 chan->facility = LOG_LOCAL2;
242 else if (!strcasecmp(facility, "LOCAL3"))
243 chan->facility = LOG_LOCAL3;
244 else if (!strcasecmp(facility, "LOCAL4"))
245 chan->facility = LOG_LOCAL4;
246 else if (!strcasecmp(facility, "LOCAL5"))
247 chan->facility = LOG_LOCAL5;
248 else if (!strcasecmp(facility, "LOCAL6"))
249 chan->facility = LOG_LOCAL6;
250 else if (!strcasecmp(facility, "LOCAL7"))
251 chan->facility = LOG_LOCAL7;
254 if (0 > chan->facility) {
255 fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
260 chan->type = LOGTYPE_SYSLOG;
261 snprintf(chan->filename, sizeof(chan->filename), "%s", channel);
262 openlog("asterisk", LOG_PID, chan->facility);
264 if (channel[0] == '/') {
265 if(!ast_strlen_zero(hostname)) {
266 snprintf(chan->filename, sizeof(chan->filename) - 1,"%s.%s", channel, hostname);
268 ast_copy_string(chan->filename, channel, sizeof(chan->filename));
272 if(!ast_strlen_zero(hostname)) {
273 snprintf(chan->filename, sizeof(chan->filename), "%s/%s.%s",(char *)ast_config_AST_LOG_DIR, channel, hostname);
275 snprintf(chan->filename, sizeof(chan->filename), "%s/%s", (char *)ast_config_AST_LOG_DIR, channel);
277 chan->fileptr = fopen(chan->filename, "a");
278 if (!chan->fileptr) {
279 /* Can't log here, since we're called with a lock */
280 fprintf(stderr, "Logger Warning: Unable to open log file '%s': %s\n", chan->filename, strerror(errno));
282 chan->type = LOGTYPE_FILE;
284 chan->logmask = make_components(components, lineno);
288 static void init_logger_chain(void)
290 struct logchannel *chan, *cur;
291 struct ast_config *cfg;
292 struct ast_variable *var;
295 /* delete our list of log channels */
296 ast_mutex_lock(&loglock);
304 ast_mutex_unlock(&loglock);
310 cfg = ast_config_load("logger.conf");
312 /* If no config file, we're fine */
316 ast_mutex_lock(&loglock);
317 if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) {
319 if(gethostname(hostname, sizeof(hostname)-1)) {
320 ast_copy_string(hostname, "unknown", sizeof(hostname));
321 ast_log(LOG_WARNING, "What box has no hostname???\n");
327 if ((s = ast_variable_retrieve(cfg, "general", "dateformat"))) {
328 ast_copy_string(dateformat, s, sizeof(dateformat));
330 ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
331 if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) {
332 logfiles.queue_log = ast_true(s);
334 if ((s = ast_variable_retrieve(cfg, "general", "event_log"))) {
335 logfiles.event_log = ast_true(s);
338 var = ast_variable_browse(cfg, "logfiles");
340 chan = make_logchannel(var->name, var->value, var->lineno);
342 chan->next = logchannels;
344 global_logmask |= chan->logmask;
349 ast_config_destroy(cfg);
350 ast_mutex_unlock(&loglock);
353 static FILE *qlog = NULL;
354 AST_MUTEX_DEFINE_STATIC(qloglock);
356 void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
359 ast_mutex_lock(&qloglock);
362 fprintf(qlog, "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
363 vfprintf(qlog, fmt, ap);
368 ast_mutex_unlock(&qloglock);
371 static void queue_log_init(void)
376 ast_mutex_lock(&qloglock);
382 snprintf(filename, sizeof(filename), "%s/%s", (char *)ast_config_AST_LOG_DIR, "queue_log");
383 if (logfiles.queue_log) {
384 qlog = fopen(filename, "a");
386 ast_mutex_unlock(&qloglock);
388 ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
390 ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
393 int reload_logger(int rotate)
395 char old[AST_CONFIG_MAX_PATH] = "";
396 char new[AST_CONFIG_MAX_PATH];
397 struct logchannel *f;
401 ast_mutex_lock(&loglock);
408 mkdir((char *)ast_config_AST_LOG_DIR, 0755);
409 snprintf(old, sizeof(old), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
411 if (logfiles.event_log) {
414 snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, EVENTLOG,x);
415 myf = fopen((char *)new, "r");
416 if (myf) /* File exists */
424 fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
427 eventlog = fopen(old, "a");
433 f->disabled = 0; /* Re-enable logging at reload */
434 manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
436 if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
437 fclose(f->fileptr); /* Close file */
440 ast_copy_string(old, f->filename, sizeof(old));
443 snprintf(new, sizeof(new), "%s.%d", f->filename, x);
444 myf = fopen((char *)new, "r");
454 fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
460 ast_mutex_unlock(&loglock);
462 filesize_reload_needed = 0;
467 if (logfiles.event_log) {
469 ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
471 ast_verbose("Asterisk Event Logger restarted\n");
474 ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
480 static int handle_logger_reload(int fd, int argc, char *argv[])
482 if(reload_logger(0)) {
483 ast_cli(fd, "Failed to reload the logger\n");
484 return RESULT_FAILURE;
486 return RESULT_SUCCESS;
489 static int handle_logger_rotate(int fd, int argc, char *argv[])
491 if(reload_logger(1)) {
492 ast_cli(fd, "Failed to reload the logger and rotate log files\n");
493 return RESULT_FAILURE;
495 return RESULT_SUCCESS;
498 /*--- handle_logger_show_channels: CLI command to show logging system
500 static int handle_logger_show_channels(int fd, int argc, char *argv[])
502 #define FORMATL "%-35.35s %-8.8s %-9.9s "
503 struct logchannel *chan;
505 ast_mutex_lock(&loglock);
508 ast_cli(fd,FORMATL, "Channel", "Type", "Status");
509 ast_cli(fd, "Configuration\n");
510 ast_cli(fd,FORMATL, "-------", "----", "------");
511 ast_cli(fd, "-------------\n");
513 ast_cli(fd, FORMATL, chan->filename, chan->type==LOGTYPE_CONSOLE ? "Console" : (chan->type==LOGTYPE_SYSLOG ? "Syslog" : "File"),
514 chan->disabled ? "Disabled" : "Enabled");
516 if (chan->logmask & (1 << __LOG_DEBUG))
517 ast_cli(fd, "Debug ");
518 if (chan->logmask & (1 << __LOG_DTMF))
519 ast_cli(fd, "DTMF ");
520 if (chan->logmask & (1 << __LOG_VERBOSE))
521 ast_cli(fd, "Verbose ");
522 if (chan->logmask & (1 << __LOG_WARNING))
523 ast_cli(fd, "Warning ");
524 if (chan->logmask & (1 << __LOG_NOTICE))
525 ast_cli(fd, "Notice ");
526 if (chan->logmask & (1 << __LOG_ERROR))
527 ast_cli(fd, "Error ");
528 if (chan->logmask & (1 << __LOG_EVENT))
529 ast_cli(fd, "Event ");
535 ast_mutex_unlock(&loglock);
537 return RESULT_SUCCESS;
541 void (*verboser)(const char *string, int opos, int replacelast, int complete);
546 static char logger_reload_help[] =
547 "Usage: logger reload\n"
548 " Reloads the logger subsystem state. Use after restarting syslogd(8) if you are using syslog logging.\n";
550 static char logger_rotate_help[] =
551 "Usage: logger rotate\n"
552 " Rotates and Reopens the log files.\n";
554 static char logger_show_channels_help[] =
555 "Usage: logger show channels\n"
556 " Show configured logger channels.\n";
558 static struct ast_cli_entry logger_show_channels_cli =
559 { { "logger", "show", "channels", NULL },
560 handle_logger_show_channels, "List configured log channels",
561 logger_show_channels_help };
563 static struct ast_cli_entry reload_logger_cli =
564 { { "logger", "reload", NULL },
565 handle_logger_reload, "Reopens the log files",
566 logger_reload_help };
568 static struct ast_cli_entry rotate_logger_cli =
569 { { "logger", "rotate", NULL },
570 handle_logger_rotate, "Rotates and reopens the log files",
571 logger_rotate_help };
573 static int handle_SIGXFSZ(int sig)
575 /* Indicate need to reload */
576 filesize_reload_needed = 1;
580 int init_logger(void)
584 /* auto rotate if sig SIGXFSZ comes a-knockin */
585 (void) signal(SIGXFSZ,(void *) handle_SIGXFSZ);
587 /* register the relaod logger cli command */
588 ast_cli_register(&reload_logger_cli);
589 ast_cli_register(&rotate_logger_cli);
590 ast_cli_register(&logger_show_channels_cli);
592 /* initialize queue logger */
595 /* create log channels */
598 /* create the eventlog */
599 if (logfiles.event_log) {
600 mkdir((char *)ast_config_AST_LOG_DIR, 0755);
601 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
602 eventlog = fopen((char *)tmp, "a");
604 ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
606 ast_verbose("Asterisk Event Logger Started %s\n",(char *)tmp);
609 ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
616 void close_logger(void)
618 struct msglist *m, *tmp;
620 ast_mutex_lock(&msglist_lock);
632 ast_mutex_unlock(&msglist_lock);
636 static void strip_coloring(char *str)
638 char *src, *dest, *end;
643 /* find the first potential escape sequence in the string */
645 src = strchr(str, '\033');
651 /* at the top of this loop, *src will always be an ESC character */
652 if ((src[1] == '[') && ((end = strchr(src + 2, 'm'))))
657 /* copy characters, checking for ESC as we go */
658 while (*src && (*src != '\033'))
665 static void ast_log_vsyslog(int level, const char *file, int line, const char *function, const char *fmt, va_list args)
670 if (level >= SYSLOG_NLEVELS) {
671 /* we are locked here, so cannot ast_log() */
672 fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", level);
675 if (level == __LOG_VERBOSE) {
676 snprintf(buf, sizeof(buf), "VERBOSE[%ld]: ", (long)GETTID());
678 } else if (level == __LOG_DTMF) {
679 snprintf(buf, sizeof(buf), "DTMF[%ld]: ", (long)GETTID());
682 snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: ",
683 levels[level], (long)GETTID(), file, line, function);
685 s = buf + strlen(buf);
686 vsnprintf(s, sizeof(buf) - strlen(buf), fmt, args);
688 syslog(syslog_level_map[level], "%s", buf);
692 * send log messages to syslog and/or the console
694 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
696 struct logchannel *chan;
704 /* don't display LOG_DEBUG messages unless option_verbose _or_ option_debug
705 are non-zero; LOG_DEBUG messages can still be displayed if option_debug
706 is zero, if option_verbose is non-zero (this allows for 'level zero'
707 LOG_DEBUG messages to be displayed, if the logmask on any channel
710 if (!option_verbose && !option_debug && (level == __LOG_DEBUG)) {
714 /* Ignore anything that never gets logged anywhere */
715 if (!(global_logmask & (1 << level)))
718 /* Ignore anything other than the currently debugged file if there is one */
719 if ((level == __LOG_DEBUG) && !ast_strlen_zero(debug_filename) && strcasecmp(debug_filename, file))
722 /* begin critical section */
723 ast_mutex_lock(&loglock);
726 localtime_r(&t, &tm);
727 strftime(date, sizeof(date), dateformat, &tm);
729 if (logfiles.event_log && level == __LOG_EVENT) {
732 fprintf(eventlog, "%s asterisk[%d]: ", date, getpid());
733 vfprintf(eventlog, fmt, ap);
737 ast_mutex_unlock(&loglock);
743 while(chan && !chan->disabled) {
744 /* Check syslog channels */
745 if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << level))) {
747 ast_log_vsyslog(level, file, line, function, fmt, ap);
749 /* Console channels */
750 } else if ((chan->logmask & (1 << level)) && (chan->type == LOGTYPE_CONSOLE)) {
752 char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
754 if (level != __LOG_VERBOSE) {
755 sprintf(linestr, "%d", line);
756 snprintf(buf, sizeof(buf), ast_opt_timestamp ? "[%s] %s[%ld]: %s:%s %s: " : "%s %s[%ld]: %s:%s %s: ",
758 term_color(tmp1, levels[level], colors[level], 0, sizeof(tmp1)),
760 term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
761 term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
762 term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
764 ast_console_puts(buf);
766 vsnprintf(buf, sizeof(buf), fmt, ap);
768 ast_console_puts(buf);
771 } else if ((chan->logmask & (1 << level)) && (chan->fileptr)) {
773 snprintf(buf, sizeof(buf), ast_opt_timestamp ? "[%s] %s[%ld]: " : "%s %s[%ld] %s: ", date,
774 levels[level], (long)GETTID(), file);
775 res = fprintf(chan->fileptr, buf);
776 if (res <= 0 && buf[0] != '\0') { /* Error, no characters printed */
777 fprintf(stderr,"**** Asterisk Logging Error: ***********\n");
778 if (errno == ENOMEM || errno == ENOSPC) {
779 fprintf(stderr, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan->filename);
781 fprintf(stderr, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan->filename, strerror(errno));
782 manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan->filename, errno, strerror(errno));
785 /* No error message, continue printing */
787 vsnprintf(buf, sizeof(buf), fmt, ap);
790 fputs(buf, chan->fileptr);
791 fflush(chan->fileptr);
798 * we don't have the logger chain configured yet,
799 * so just log to stdout
801 if (level != __LOG_VERBOSE) {
803 vsnprintf(buf, sizeof(buf), fmt, ap);
809 ast_mutex_unlock(&loglock);
810 /* end critical section */
811 if (filesize_reload_needed) {
813 ast_log(LOG_EVENT,"Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
815 ast_verbose("Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
819 void ast_verbose(const char *fmt, ...)
821 static char stuff[4096];
823 static int replacelast = 0;
833 if (ast_opt_timestamp) {
840 localtime_r(&t, &tm);
841 strftime(date, sizeof(date), dateformat, &tm);
842 datefmt = alloca(strlen(date) + 3 + strlen(fmt) + 1);
844 sprintf(datefmt, "[%s] %s", date, fmt);
849 /* this lock is also protecting against multiple threads
850 being in this function at the same time, so it must be
851 held before any of the static variables are accessed
853 ast_mutex_lock(&msglist_lock);
855 /* there is a potential security problem here: if formatting
856 the current date using 'dateformat' results in a string
857 containing '%', then the vsnprintf() call below will
858 probably try to access random memory
860 vsnprintf(stuff + len, sizeof(stuff) - len, fmt, ap);
866 complete = (stuff[len - 1] == '\n') ? 1 : 0;
868 /* If we filled up the stuff completely, then log it even without the '\n' */
869 if (len >= sizeof(stuff) - 1) {
875 if (msgcnt < MAX_MSG_QUEUE) {
876 /* Allocate new structure */
877 if ((m = malloc(sizeof(*m))))
880 /* Recycle the oldest entry */
886 m->msg = strdup(stuff);
896 ast_log(LOG_ERROR, "Out of memory\n");
902 for (v = verboser; v; v = v->next)
903 v->verboser(stuff, olen, replacelast, complete);
905 ast_log(LOG_VERBOSE, "%s", stuff);
911 replacelast = len = 0;
914 ast_mutex_unlock(&msglist_lock);
917 int ast_verbose_dmesg(void (*v)(const char *string, int opos, int replacelast, int complete))
920 ast_mutex_lock(&msglist_lock);
923 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
927 ast_mutex_unlock(&msglist_lock);
931 int ast_register_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
935 /* XXX Should be more flexible here, taking > 1 verboser XXX */
936 if ((tmp = malloc(sizeof (struct verb)))) {
938 ast_mutex_lock(&msglist_lock);
939 tmp->next = verboser;
943 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
947 ast_mutex_unlock(&msglist_lock);
953 int ast_unregister_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
956 struct verb *tmp, *tmpl=NULL;
957 ast_mutex_lock(&msglist_lock);
960 if (tmp->verboser == v) {
962 tmpl->next = tmp->next;
964 verboser = tmp->next;
973 ast_mutex_unlock(&msglist_lock);