2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, 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.
21 * \brief Asterisk Logger
25 * \author Mark Spencer <markster@digium.com>
29 * define _ASTERISK_LOGGER_H to prevent the inclusion of logger.h;
30 * it redefines LOG_* which we need to define syslog_level_map.
31 * Later, we force the inclusion of logger.h again.
33 #define _ASTERISK_LOGGER_H
36 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
38 #include "asterisk/_private.h"
39 #include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
46 #define MAX_BACKTRACE_FRAMES 20
49 #define SYSLOG_NAMES /* so we can map syslog facilities names to their numeric values,
50 from <syslog.h> which is included by logger.h */
53 static int syslog_level_map
[] = {
55 LOG_INFO
, /* arbitrary equivalent of LOG_EVENT */
63 #define SYSLOG_NLEVELS sizeof(syslog_level_map) / sizeof(int)
65 #undef _ASTERISK_LOGGER_H /* now include logger.h */
66 #include "asterisk/logger.h"
67 #include "asterisk/lock.h"
68 #include "asterisk/channel.h"
69 #include "asterisk/config.h"
70 #include "asterisk/term.h"
71 #include "asterisk/cli.h"
72 #include "asterisk/utils.h"
73 #include "asterisk/manager.h"
74 #include "asterisk/threadstorage.h"
75 #include "asterisk/strings.h"
76 #include "asterisk/pbx.h"
78 #if defined(__linux__) && !defined(__NR_gettid)
79 #include <asm/unistd.h>
82 #if defined(__linux__) && defined(__NR_gettid)
83 #define GETTID() syscall(__NR_gettid)
85 #define GETTID() getpid()
88 static char dateformat
[256] = "%b %e %T"; /* Original Asterisk Format */
90 static char queue_log_name
[256] = QUEUELOG
;
91 static char exec_after_rotate
[256] = "";
93 static int filesize_reload_needed
;
94 static int global_logmask
= -1;
97 SEQUENTIAL
= 1 << 0, /* Original method - create a new file, in order */
98 ROTATE
= 1 << 1, /* Rotate all files, such that the oldest file has the highest suffix */
99 TIMESTAMP
= 1 << 2, /* Append the epoch timestamp onto the end of the archived file */
100 } rotatestrategy
= SEQUENTIAL
;
103 unsigned int queue_log
:1;
104 unsigned int event_log
:1;
105 } logfiles
= { 1, 1 };
107 static char hostname
[MAXHOSTNAMELEN
];
116 int logmask
; /* What to log to this channel */
117 int disabled
; /* If this channel is disabled or not */
118 int facility
; /* syslog facility */
119 enum logtypes type
; /* Type of log channel */
120 FILE *fileptr
; /* logfile logging file pointer */
121 char filename
[256]; /* Filename */
122 AST_LIST_ENTRY(logchannel
) list
;
125 static AST_RWLIST_HEAD_STATIC(logchannels
, logchannel
);
133 enum logmsgtypes type
;
138 const char *function
;
139 AST_LIST_ENTRY(logmsg
) list
;
143 static AST_LIST_HEAD_STATIC(logmsgs
, logmsg
);
144 static pthread_t logthread
= AST_PTHREADT_NULL
;
145 static ast_cond_t logcond
;
146 static int close_logger_thread
;
148 static FILE *eventlog
;
151 /*! \brief Logging channels used in the Asterisk logging system */
152 static char *levels
[] = {
162 /*! \brief Colors used in the console for logging */
163 static int colors
[] = {
173 AST_THREADSTORAGE(verbose_buf
);
174 #define VERBOSE_BUF_INIT_SIZE 256
176 AST_THREADSTORAGE(log_buf
);
177 #define LOG_BUF_INIT_SIZE 256
179 static int make_components(const char *s
, int lineno
)
183 char *stringp
= ast_strdupa(s
);
185 while ((w
= strsep(&stringp
, ","))) {
186 w
= ast_skip_blanks(w
);
187 if (!strcasecmp(w
, "error"))
188 res
|= (1 << __LOG_ERROR
);
189 else if (!strcasecmp(w
, "warning"))
190 res
|= (1 << __LOG_WARNING
);
191 else if (!strcasecmp(w
, "notice"))
192 res
|= (1 << __LOG_NOTICE
);
193 else if (!strcasecmp(w
, "event"))
194 res
|= (1 << __LOG_EVENT
);
195 else if (!strcasecmp(w
, "debug"))
196 res
|= (1 << __LOG_DEBUG
);
197 else if (!strcasecmp(w
, "verbose"))
198 res
|= (1 << __LOG_VERBOSE
);
199 else if (!strcasecmp(w
, "dtmf"))
200 res
|= (1 << __LOG_DTMF
);
202 fprintf(stderr
, "Logfile Warning: Unknown keyword '%s' at line %d of logger.conf\n", w
, lineno
);
209 static struct logchannel
*make_logchannel(const char *channel
, const char *components
, int lineno
)
211 struct logchannel
*chan
;
217 if (ast_strlen_zero(channel
) || !(chan
= ast_calloc(1, sizeof(*chan
))))
220 if (!strcasecmp(channel
, "console")) {
221 chan
->type
= LOGTYPE_CONSOLE
;
222 } else if (!strncasecmp(channel
, "syslog", 6)) {
225 * syslog.facility => level,level,level
227 facility
= strchr(channel
, '.');
228 if (!facility
++ || !facility
) {
234 * Walk through the list of facilitynames (defined in sys/syslog.h)
235 * to see if we can find the one we have been given
238 cptr
= facilitynames
;
239 while (cptr
->c_name
) {
240 if (!strcasecmp(facility
, cptr
->c_name
)) {
241 chan
->facility
= cptr
->c_val
;
248 if (!strcasecmp(facility
, "kern"))
249 chan
->facility
= LOG_KERN
;
250 else if (!strcasecmp(facility
, "USER"))
251 chan
->facility
= LOG_USER
;
252 else if (!strcasecmp(facility
, "MAIL"))
253 chan
->facility
= LOG_MAIL
;
254 else if (!strcasecmp(facility
, "DAEMON"))
255 chan
->facility
= LOG_DAEMON
;
256 else if (!strcasecmp(facility
, "AUTH"))
257 chan
->facility
= LOG_AUTH
;
258 else if (!strcasecmp(facility
, "SYSLOG"))
259 chan
->facility
= LOG_SYSLOG
;
260 else if (!strcasecmp(facility
, "LPR"))
261 chan
->facility
= LOG_LPR
;
262 else if (!strcasecmp(facility
, "NEWS"))
263 chan
->facility
= LOG_NEWS
;
264 else if (!strcasecmp(facility
, "UUCP"))
265 chan
->facility
= LOG_UUCP
;
266 else if (!strcasecmp(facility
, "CRON"))
267 chan
->facility
= LOG_CRON
;
268 else if (!strcasecmp(facility
, "LOCAL0"))
269 chan
->facility
= LOG_LOCAL0
;
270 else if (!strcasecmp(facility
, "LOCAL1"))
271 chan
->facility
= LOG_LOCAL1
;
272 else if (!strcasecmp(facility
, "LOCAL2"))
273 chan
->facility
= LOG_LOCAL2
;
274 else if (!strcasecmp(facility
, "LOCAL3"))
275 chan
->facility
= LOG_LOCAL3
;
276 else if (!strcasecmp(facility
, "LOCAL4"))
277 chan
->facility
= LOG_LOCAL4
;
278 else if (!strcasecmp(facility
, "LOCAL5"))
279 chan
->facility
= LOG_LOCAL5
;
280 else if (!strcasecmp(facility
, "LOCAL6"))
281 chan
->facility
= LOG_LOCAL6
;
282 else if (!strcasecmp(facility
, "LOCAL7"))
283 chan
->facility
= LOG_LOCAL7
;
286 if (0 > chan
->facility
) {
287 fprintf(stderr
, "Logger Warning: bad syslog facility in logger.conf\n");
292 chan
->type
= LOGTYPE_SYSLOG
;
293 snprintf(chan
->filename
, sizeof(chan
->filename
), "%s", channel
);
294 openlog("asterisk", LOG_PID
, chan
->facility
);
296 if (channel
[0] == '/') {
297 if (!ast_strlen_zero(hostname
)) {
298 snprintf(chan
->filename
, sizeof(chan
->filename
), "%s.%s", channel
, hostname
);
300 ast_copy_string(chan
->filename
, channel
, sizeof(chan
->filename
));
304 if (!ast_strlen_zero(hostname
)) {
305 snprintf(chan
->filename
, sizeof(chan
->filename
), "%s/%s.%s", ast_config_AST_LOG_DIR
, channel
, hostname
);
307 snprintf(chan
->filename
, sizeof(chan
->filename
), "%s/%s", ast_config_AST_LOG_DIR
, channel
);
309 chan
->fileptr
= fopen(chan
->filename
, "a");
310 if (!chan
->fileptr
) {
311 /* Can't log here, since we're called with a lock */
312 fprintf(stderr
, "Logger Warning: Unable to open log file '%s': %s\n", chan
->filename
, strerror(errno
));
314 chan
->type
= LOGTYPE_FILE
;
316 chan
->logmask
= make_components(components
, lineno
);
320 static void init_logger_chain(int reload
, int locked
)
322 struct logchannel
*chan
;
323 struct ast_config
*cfg
;
324 struct ast_variable
*var
;
326 struct ast_flags config_flags
= { reload
? CONFIG_FLAG_FILEUNCHANGED
: 0 };
328 if ((cfg
= ast_config_load2("logger.conf", "logger", config_flags
)) == CONFIG_STATUS_FILEUNCHANGED
)
331 /* delete our list of log channels */
333 AST_RWLIST_WRLOCK(&logchannels
);
334 while ((chan
= AST_RWLIST_REMOVE_HEAD(&logchannels
, list
)))
337 AST_RWLIST_UNLOCK(&logchannels
);
344 /* If no config file, we're fine, set default options. */
347 fprintf(stderr
, "Unable to open logger.conf: %s; default settings will be used.\n", strerror(errno
));
349 fprintf(stderr
, "Errors detected in logger.conf: see above; default settings will be used.\n");
350 if (!(chan
= ast_calloc(1, sizeof(*chan
))))
352 chan
->type
= LOGTYPE_CONSOLE
;
353 chan
->logmask
= 28; /*warning,notice,error */
355 AST_RWLIST_WRLOCK(&logchannels
);
356 AST_RWLIST_INSERT_HEAD(&logchannels
, chan
, list
);
358 AST_RWLIST_UNLOCK(&logchannels
);
359 global_logmask
|= chan
->logmask
;
363 if ((s
= ast_variable_retrieve(cfg
, "general", "appendhostname"))) {
365 if (gethostname(hostname
, sizeof(hostname
) - 1)) {
366 ast_copy_string(hostname
, "unknown", sizeof(hostname
));
367 fprintf(stderr
, "What box has no hostname???\n");
373 if ((s
= ast_variable_retrieve(cfg
, "general", "dateformat")))
374 ast_copy_string(dateformat
, s
, sizeof(dateformat
));
376 ast_copy_string(dateformat
, "%b %e %T", sizeof(dateformat
));
377 if ((s
= ast_variable_retrieve(cfg
, "general", "queue_log")))
378 logfiles
.queue_log
= ast_true(s
);
379 if ((s
= ast_variable_retrieve(cfg
, "general", "event_log")))
380 logfiles
.event_log
= ast_true(s
);
381 if ((s
= ast_variable_retrieve(cfg
, "general", "queue_log_name")))
382 ast_copy_string(queue_log_name
, s
, sizeof(queue_log_name
));
383 if ((s
= ast_variable_retrieve(cfg
, "general", "exec_after_rotate")))
384 ast_copy_string(exec_after_rotate
, s
, sizeof(exec_after_rotate
));
385 if ((s
= ast_variable_retrieve(cfg
, "general", "rotatestrategy"))) {
386 if (strcasecmp(s
, "timestamp") == 0)
387 rotatestrategy
= TIMESTAMP
;
388 else if (strcasecmp(s
, "rotate") == 0)
389 rotatestrategy
= ROTATE
;
390 else if (strcasecmp(s
, "sequential") == 0)
391 rotatestrategy
= SEQUENTIAL
;
393 fprintf(stderr
, "Unknown rotatestrategy: %s\n", s
);
395 if ((s
= ast_variable_retrieve(cfg
, "general", "rotatetimestamp"))) {
396 rotatestrategy
= ast_true(s
) ? TIMESTAMP
: SEQUENTIAL
;
397 fprintf(stderr
, "rotatetimestamp option has been deprecated. Please use rotatestrategy instead.\n");
402 AST_RWLIST_WRLOCK(&logchannels
);
403 var
= ast_variable_browse(cfg
, "logfiles");
404 for (; var
; var
= var
->next
) {
405 if (!(chan
= make_logchannel(var
->name
, var
->value
, var
->lineno
)))
407 AST_RWLIST_INSERT_HEAD(&logchannels
, chan
, list
);
408 global_logmask
|= chan
->logmask
;
411 AST_RWLIST_UNLOCK(&logchannels
);
413 ast_config_destroy(cfg
);
416 void ast_child_verbose(int level
, const char *fmt
, ...)
418 char *msg
= NULL
, *emsg
= NULL
, *sptr
, *eptr
;
422 /* Don't bother, if the level isn't that high */
423 if (option_verbose
< level
) {
429 if ((size
= vsnprintf(msg
, 0, fmt
, ap
)) < 0) {
436 if (!(msg
= ast_malloc(size
+ 1))) {
441 vsnprintf(msg
, size
+ 1, fmt
, aq
);
444 if (!(emsg
= ast_malloc(size
* 2 + 1))) {
449 for (sptr
= msg
, eptr
= emsg
; ; sptr
++) {
460 fprintf(stdout
, "verbose \"%s\" %d\n", emsg
, level
);
465 void ast_queue_log(const char *queuename
, const char *callid
, const char *agent
, const char *event
, const char *fmt
, ...)
472 if (ast_check_realtime("queue_log")) {
474 vsnprintf(qlog_msg
, sizeof(qlog_msg
), fmt
, ap
);
476 snprintf(time_str
, sizeof(time_str
), "%ld", (long)time(NULL
));
477 ast_store_realtime("queue_log", "time", time_str
,
479 "queuename", queuename
,
487 qlog_len
= snprintf(qlog_msg
, sizeof(qlog_msg
), "%ld|%s|%s|%s|%s|", (long)time(NULL
), callid
, queuename
, agent
, event
);
488 vsnprintf(qlog_msg
+ qlog_len
, sizeof(qlog_msg
) - qlog_len
, fmt
, ap
);
491 AST_RWLIST_RDLOCK(&logchannels
);
493 fprintf(qlog
, "%s\n", qlog_msg
);
496 AST_RWLIST_UNLOCK(&logchannels
);
500 static int rotate_file(const char *filename
)
504 int x
, y
, which
, found
, res
= 0, fd
;
505 char *suffixes
[4] = { "", ".gz", ".bz2", ".Z" };
507 switch (rotatestrategy
) {
510 snprintf(new, sizeof(new), "%s.%d", filename
, x
);
511 fd
= open(new, O_RDONLY
);
517 if (rename(filename
, new)) {
518 fprintf(stderr
, "Unable to rename file '%s' to '%s'\n", filename
, new);
523 snprintf(new, sizeof(new), "%s.%ld", filename
, (long)time(NULL
));
524 if (rename(filename
, new)) {
525 fprintf(stderr
, "Unable to rename file '%s' to '%s'\n", filename
, new);
530 /* Find the next empty slot, including a possible suffix */
533 for (which
= 0; which
< sizeof(suffixes
) / sizeof(suffixes
[0]); which
++) {
534 snprintf(new, sizeof(new), "%s.%d%s", filename
, x
, suffixes
[which
]);
535 fd
= open(new, O_RDONLY
);
547 /* Found an empty slot */
548 for (y
= x
; y
> -1; y
--) {
549 for (which
= 0; which
< sizeof(suffixes
) / sizeof(suffixes
[0]); which
++) {
550 snprintf(old
, sizeof(old
), "%s.%d%s", filename
, y
- 1, suffixes
[which
]);
551 fd
= open(old
, O_RDONLY
);
553 /* Found the right suffix */
555 snprintf(new, sizeof(new), "%s.%d%s", filename
, y
, suffixes
[which
]);
556 if (rename(old
, new)) {
557 fprintf(stderr
, "Unable to rename file '%s' to '%s'\n", old
, new);
565 /* Finally, rename the current file */
566 snprintf(new, sizeof(new), "%s.0", filename
);
567 if (rename(filename
, new)) {
568 fprintf(stderr
, "Unable to rename file '%s' to '%s'\n", filename
, new);
573 if (!ast_strlen_zero(exec_after_rotate
)) {
574 struct ast_channel
*c
= ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Logger/rotate");
576 pbx_builtin_setvar_helper(c
, "filename", filename
);
577 pbx_substitute_variables_helper(c
, exec_after_rotate
, buf
, sizeof(buf
));
584 static int reload_logger(int rotate
)
586 char old
[PATH_MAX
] = "";
587 int event_rotate
= rotate
, queue_rotate
= rotate
;
588 struct logchannel
*f
;
592 AST_RWLIST_WRLOCK(&logchannels
);
596 /* Check filesize - this one typically doesn't need an auto-rotate */
597 snprintf(old
, sizeof(old
), "%s/%s", ast_config_AST_LOG_DIR
, EVENTLOG
);
598 if (stat(old
, &st
) != 0 || st
.st_size
> 0x40000000) { /* Arbitrarily, 1 GB */
612 /* Check filesize - this one typically doesn't need an auto-rotate */
613 snprintf(old
, sizeof(old
), "%s/%s", ast_config_AST_LOG_DIR
, queue_log_name
);
614 if (stat(old
, &st
) != 0 || st
.st_size
> 0x40000000) { /* Arbitrarily, 1 GB */
627 ast_mkdir(ast_config_AST_LOG_DIR
, 0777);
629 AST_RWLIST_TRAVERSE(&logchannels
, f
, list
) {
631 f
->disabled
= 0; /* Re-enable logging at reload */
632 manager_event(EVENT_FLAG_SYSTEM
, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f
->filename
);
634 if (f
->fileptr
&& (f
->fileptr
!= stdout
) && (f
->fileptr
!= stderr
)) {
635 fclose(f
->fileptr
); /* Close file */
638 rotate_file(f
->filename
);
642 filesize_reload_needed
= 0;
644 init_logger_chain(rotate
? 0 : 1 /* reload */, 1 /* locked */);
646 if (logfiles
.event_log
) {
647 snprintf(old
, sizeof(old
), "%s/%s", ast_config_AST_LOG_DIR
, EVENTLOG
);
651 eventlog
= fopen(old
, "a");
653 ast_log(LOG_EVENT
, "Restarted Asterisk Event Logger\n");
654 ast_verb(1, "Asterisk Event Logger restarted\n");
656 ast_log(LOG_ERROR
, "Unable to create event log: %s\n", strerror(errno
));
661 if (logfiles
.queue_log
) {
662 snprintf(old
, sizeof(old
), "%s/%s", ast_config_AST_LOG_DIR
, queue_log_name
);
666 qlog
= fopen(old
, "a");
668 AST_RWLIST_UNLOCK(&logchannels
);
669 ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
670 AST_RWLIST_WRLOCK(&logchannels
);
671 ast_log(LOG_EVENT
, "Restarted Asterisk Queue Logger\n");
672 ast_verb(1, "Asterisk Queue Logger restarted\n");
674 ast_log(LOG_ERROR
, "Unable to create queue log: %s\n", strerror(errno
));
679 AST_RWLIST_UNLOCK(&logchannels
);
684 /*! \brief Reload the logger module without rotating log files (also used from loader.c during
685 a full Asterisk reload) */
686 int logger_reload(void)
689 return RESULT_FAILURE
;
690 return RESULT_SUCCESS
;
693 static char *handle_logger_reload(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
697 e
->command
= "logger reload";
699 "Usage: logger reload\n"
700 " Reloads the logger subsystem state. Use after restarting syslogd(8) if you are using syslog logging.\n";
705 if (reload_logger(0)) {
706 ast_cli(a
->fd
, "Failed to reload the logger\n");
712 static char *handle_logger_rotate(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
716 e
->command
= "logger rotate";
718 "Usage: logger rotate\n"
719 " Rotates and Reopens the log files.\n";
724 if (reload_logger(1)) {
725 ast_cli(a
->fd
, "Failed to reload the logger and rotate log files\n");
731 /*! \brief CLI command to show logging system configuration */
732 static char *handle_logger_show_channels(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
734 #define FORMATL "%-35.35s %-8.8s %-9.9s "
735 struct logchannel
*chan
;
738 e
->command
= "logger show channels";
740 "Usage: logger show channels\n"
741 " List configured logger channels.\n";
746 ast_cli(a
->fd
, FORMATL
, "Channel", "Type", "Status");
747 ast_cli(a
->fd
, "Configuration\n");
748 ast_cli(a
->fd
, FORMATL
, "-------", "----", "------");
749 ast_cli(a
->fd
, "-------------\n");
750 AST_RWLIST_RDLOCK(&logchannels
);
751 AST_RWLIST_TRAVERSE(&logchannels
, chan
, list
) {
752 ast_cli(a
->fd
, FORMATL
, chan
->filename
, chan
->type
== LOGTYPE_CONSOLE
? "Console" : (chan
->type
== LOGTYPE_SYSLOG
? "Syslog" : "File"),
753 chan
->disabled
? "Disabled" : "Enabled");
754 ast_cli(a
->fd
, " - ");
755 if (chan
->logmask
& (1 << __LOG_DEBUG
))
756 ast_cli(a
->fd
, "Debug ");
757 if (chan
->logmask
& (1 << __LOG_DTMF
))
758 ast_cli(a
->fd
, "DTMF ");
759 if (chan
->logmask
& (1 << __LOG_VERBOSE
))
760 ast_cli(a
->fd
, "Verbose ");
761 if (chan
->logmask
& (1 << __LOG_WARNING
))
762 ast_cli(a
->fd
, "Warning ");
763 if (chan
->logmask
& (1 << __LOG_NOTICE
))
764 ast_cli(a
->fd
, "Notice ");
765 if (chan
->logmask
& (1 << __LOG_ERROR
))
766 ast_cli(a
->fd
, "Error ");
767 if (chan
->logmask
& (1 << __LOG_EVENT
))
768 ast_cli(a
->fd
, "Event ");
769 ast_cli(a
->fd
, "\n");
771 AST_RWLIST_UNLOCK(&logchannels
);
772 ast_cli(a
->fd
, "\n");
778 void (*verboser
)(const char *string
);
779 AST_LIST_ENTRY(verb
) list
;
782 static AST_RWLIST_HEAD_STATIC(verbosers
, verb
);
784 static struct ast_cli_entry cli_logger
[] = {
785 AST_CLI_DEFINE(handle_logger_show_channels
, "List configured log channels"),
786 AST_CLI_DEFINE(handle_logger_reload
, "Reopens the log files"),
787 AST_CLI_DEFINE(handle_logger_rotate
, "Rotates and reopens the log files")
790 static int handle_SIGXFSZ(int sig
)
792 /* Indicate need to reload */
793 filesize_reload_needed
= 1;
797 static void ast_log_vsyslog(int level
, const char *file
, int line
, const char *function
, char *str
)
801 if (level
>= SYSLOG_NLEVELS
) {
802 /* we are locked here, so cannot ast_log() */
803 fprintf(stderr
, "ast_log_vsyslog called with bogus level: %d\n", level
);
807 if (level
== __LOG_VERBOSE
) {
808 snprintf(buf
, sizeof(buf
), "VERBOSE[%ld]: %s", (long)GETTID(), str
);
810 } else if (level
== __LOG_DTMF
) {
811 snprintf(buf
, sizeof(buf
), "DTMF[%ld]: %s", (long)GETTID(), str
);
814 snprintf(buf
, sizeof(buf
), "%s[%ld]: %s:%d in %s: %s",
815 levels
[level
], (long)GETTID(), file
, line
, function
, str
);
818 term_strip(buf
, buf
, strlen(buf
) + 1);
819 syslog(syslog_level_map
[level
], "%s", buf
);
822 /*! \brief Print a normal log message to the channels */
823 static void logger_print_normal(struct logmsg
*logmsg
)
825 struct logchannel
*chan
= NULL
;
828 AST_RWLIST_RDLOCK(&logchannels
);
830 if (logfiles
.event_log
&& logmsg
->level
== __LOG_EVENT
) {
831 fprintf(eventlog
, "%s asterisk[%ld]: %s", logmsg
->date
, (long)getpid(), logmsg
->str
);
833 AST_RWLIST_UNLOCK(&logchannels
);
837 if (!AST_RWLIST_EMPTY(&logchannels
)) {
838 AST_RWLIST_TRAVERSE(&logchannels
, chan
, list
) {
839 /* If the channel is disabled, then move on to the next one */
842 /* Check syslog channels */
843 if (chan
->type
== LOGTYPE_SYSLOG
&& (chan
->logmask
& (1 << logmsg
->level
))) {
844 ast_log_vsyslog(logmsg
->level
, logmsg
->file
, logmsg
->line
, logmsg
->function
, logmsg
->str
);
845 /* Console channels */
846 } else if (chan
->type
== LOGTYPE_CONSOLE
&& (chan
->logmask
& (1 << logmsg
->level
))) {
848 char tmp1
[80], tmp2
[80], tmp3
[80], tmp4
[80];
850 /* If the level is verbose, then skip it */
851 if (logmsg
->level
== __LOG_VERBOSE
)
854 /* Turn the numerical line number into a string */
855 snprintf(linestr
, sizeof(linestr
), "%d", logmsg
->line
);
856 /* Build string to print out */
857 snprintf(buf
, sizeof(buf
), "[%s] %s[%ld]: %s:%s %s: %s",
859 term_color(tmp1
, levels
[logmsg
->level
], colors
[logmsg
->level
], 0, sizeof(tmp1
)),
861 term_color(tmp2
, logmsg
->file
, COLOR_BRWHITE
, 0, sizeof(tmp2
)),
862 term_color(tmp3
, linestr
, COLOR_BRWHITE
, 0, sizeof(tmp3
)),
863 term_color(tmp4
, logmsg
->function
, COLOR_BRWHITE
, 0, sizeof(tmp4
)),
866 ast_console_puts_mutable(buf
);
868 } else if (chan
->type
== LOGTYPE_FILE
&& (chan
->logmask
& (1 << logmsg
->level
))) {
871 /* If no file pointer exists, skip it */
875 /* Print out to the file */
876 res
= fprintf(chan
->fileptr
, "[%s] %s[%ld] %s: %s",
877 logmsg
->date
, levels
[logmsg
->level
], (long)GETTID(), logmsg
->file
, logmsg
->str
);
878 if (res
<= 0 && !ast_strlen_zero(logmsg
->str
)) {
879 fprintf(stderr
, "**** Asterisk Logging Error: ***********\n");
880 if (errno
== ENOMEM
|| errno
== ENOSPC
)
881 fprintf(stderr
, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan
->filename
);
883 fprintf(stderr
, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan
->filename
, strerror(errno
));
884 manager_event(EVENT_FLAG_SYSTEM
, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan
->filename
, errno
, strerror(errno
));
886 } else if (res
> 0) {
887 fflush(chan
->fileptr
);
891 } else if (logmsg
->level
!= __LOG_VERBOSE
) {
892 fputs(logmsg
->str
, stdout
);
895 AST_RWLIST_UNLOCK(&logchannels
);
897 /* If we need to reload because of the file size, then do so */
898 if (filesize_reload_needed
) {
900 ast_log(LOG_EVENT
, "Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
901 ast_verb(1, "Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
907 /*! \brief Print a verbose message to the verbosers */
908 static void logger_print_verbose(struct logmsg
*logmsg
)
910 struct verb
*v
= NULL
;
912 /* Iterate through the list of verbosers and pass them the log message string */
913 AST_RWLIST_RDLOCK(&verbosers
);
914 AST_RWLIST_TRAVERSE(&verbosers
, v
, list
)
915 v
->verboser(logmsg
->str
);
916 AST_RWLIST_UNLOCK(&verbosers
);
921 /*! \brief Actual logging thread */
922 static void *logger_thread(void *data
)
924 struct logmsg
*next
= NULL
, *msg
= NULL
;
927 /* We lock the message list, and see if any message exists... if not we wait on the condition to be signalled */
928 AST_LIST_LOCK(&logmsgs
);
929 if (AST_LIST_EMPTY(&logmsgs
))
930 ast_cond_wait(&logcond
, &logmsgs
.lock
);
931 next
= AST_LIST_FIRST(&logmsgs
);
932 AST_LIST_HEAD_INIT_NOLOCK(&logmsgs
);
933 AST_LIST_UNLOCK(&logmsgs
);
935 /* Otherwise go through and process each message in the order added */
936 while ((msg
= next
)) {
937 /* Get the next entry now so that we can free our current structure later */
938 next
= AST_LIST_NEXT(msg
, list
);
940 /* Depending on the type, send it to the proper function */
941 if (msg
->type
== LOGMSG_NORMAL
)
942 logger_print_normal(msg
);
943 else if (msg
->type
== LOGMSG_VERBOSE
)
944 logger_print_verbose(msg
);
946 /* Free the data since we are done */
950 /* If we should stop, then stop */
951 if (close_logger_thread
)
958 int init_logger(void)
963 /* auto rotate if sig SIGXFSZ comes a-knockin */
964 (void) signal(SIGXFSZ
, (void *) handle_SIGXFSZ
);
966 /* start logger thread */
967 ast_cond_init(&logcond
, NULL
);
968 if (ast_pthread_create(&logthread
, NULL
, logger_thread
, NULL
) < 0) {
969 ast_cond_destroy(&logcond
);
973 /* register the logger cli commands */
974 ast_cli_register_multiple(cli_logger
, sizeof(cli_logger
) / sizeof(struct ast_cli_entry
));
976 ast_mkdir(ast_config_AST_LOG_DIR
, 0777);
978 /* create log channels */
979 init_logger_chain(0 /* reload */, 0 /* locked */);
981 /* create the eventlog */
982 if (logfiles
.event_log
) {
983 snprintf(tmp
, sizeof(tmp
), "%s/%s", ast_config_AST_LOG_DIR
, EVENTLOG
);
984 eventlog
= fopen(tmp
, "a");
986 ast_log(LOG_EVENT
, "Started Asterisk Event Logger\n");
987 ast_verb(1, "Asterisk Event Logger Started %s\n", tmp
);
989 ast_log(LOG_ERROR
, "Unable to create event log: %s\n", strerror(errno
));
994 if (logfiles
.queue_log
) {
995 snprintf(tmp
, sizeof(tmp
), "%s/%s", ast_config_AST_LOG_DIR
, queue_log_name
);
996 qlog
= fopen(tmp
, "a");
997 ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
1002 void close_logger(void)
1004 struct logchannel
*f
= NULL
;
1006 /* Stop logger thread */
1007 AST_LIST_LOCK(&logmsgs
);
1008 close_logger_thread
= 1;
1009 ast_cond_signal(&logcond
);
1010 AST_LIST_UNLOCK(&logmsgs
);
1012 if (logthread
!= AST_PTHREADT_NULL
)
1013 pthread_join(logthread
, NULL
);
1015 AST_RWLIST_WRLOCK(&logchannels
);
1027 AST_RWLIST_TRAVERSE(&logchannels
, f
, list
) {
1028 if (f
->fileptr
&& (f
->fileptr
!= stdout
) && (f
->fileptr
!= stderr
)) {
1034 closelog(); /* syslog */
1036 AST_RWLIST_UNLOCK(&logchannels
);
1042 * \brief send log messages to syslog and/or the console
1044 void ast_log(int level
, const char *file
, int line
, const char *function
, const char *fmt
, ...)
1046 struct logmsg
*logmsg
= NULL
;
1047 struct ast_str
*buf
= NULL
;
1049 struct timeval tv
= ast_tvnow();
1053 if (!(buf
= ast_str_thread_get(&log_buf
, LOG_BUF_INIT_SIZE
)))
1056 if (AST_RWLIST_EMPTY(&logchannels
)) {
1058 * we don't have the logger chain configured yet,
1059 * so just log to stdout
1061 if (level
!= __LOG_VERBOSE
) {
1064 res
= ast_str_set_va(&buf
, BUFSIZ
, fmt
, ap
); /* XXX BUFSIZ ? */
1066 if (res
!= AST_DYNSTR_BUILD_FAILED
) {
1067 term_filter_escapes(buf
->str
);
1068 fputs(buf
->str
, stdout
);
1074 /* don't display LOG_DEBUG messages unless option_verbose _or_ option_debug
1075 are non-zero; LOG_DEBUG messages can still be displayed if option_debug
1076 is zero, if option_verbose is non-zero (this allows for 'level zero'
1077 LOG_DEBUG messages to be displayed, if the logmask on any channel
1080 if (!option_verbose
&& !option_debug
&& (level
== __LOG_DEBUG
))
1083 /* Ignore anything that never gets logged anywhere */
1084 if (!(global_logmask
& (1 << level
)))
1089 res
= ast_str_set_va(&buf
, BUFSIZ
, fmt
, ap
);
1092 /* If the build failed, then abort and free this structure */
1093 if (res
== AST_DYNSTR_BUILD_FAILED
)
1096 /* Create a new logging message */
1097 if (!(logmsg
= ast_calloc(1, sizeof(*logmsg
) + res
+ 1)))
1100 /* Copy string over */
1101 strcpy(logmsg
->str
, buf
->str
);
1103 /* Set type to be normal */
1104 logmsg
->type
= LOGMSG_NORMAL
;
1106 /* Create our date/time */
1107 ast_localtime(&tv
, &tm
, NULL
);
1108 ast_strftime(logmsg
->date
, sizeof(logmsg
->date
), dateformat
, &tm
);
1110 /* Copy over data */
1111 logmsg
->level
= level
;
1112 logmsg
->file
= file
;
1113 logmsg
->line
= line
;
1114 logmsg
->function
= function
;
1116 /* If the logger thread is active, append it to the tail end of the list - otherwise skip that step */
1117 if (logthread
!= AST_PTHREADT_NULL
) {
1118 AST_LIST_LOCK(&logmsgs
);
1119 AST_LIST_INSERT_TAIL(&logmsgs
, logmsg
, list
);
1120 ast_cond_signal(&logcond
);
1121 AST_LIST_UNLOCK(&logmsgs
);
1123 logger_print_normal(logmsg
);
1130 void ast_backtrace(void)
1133 int count
= 0, i
= 0;
1137 if ((addresses
= ast_calloc(MAX_BACKTRACE_FRAMES
, sizeof(*addresses
)))) {
1138 count
= backtrace(addresses
, MAX_BACKTRACE_FRAMES
);
1139 if ((strings
= backtrace_symbols(addresses
, count
))) {
1140 ast_debug(1, "Got %d backtrace record%c\n", count
, count
!= 1 ? 's' : ' ');
1141 for (i
= 0; i
< count
; i
++) {
1142 #if __WORDSIZE == 32
1143 ast_log(LOG_DEBUG
, "#%d: [%08X] %s\n", i
, (unsigned int)addresses
[i
], strings
[i
]);
1144 #elif __WORDSIZE == 64
1145 ast_log(LOG_DEBUG
, "#%d: [%016lX] %s\n", i
, (unsigned long)addresses
[i
], strings
[i
]);
1150 ast_debug(1, "Could not allocate memory for backtrace\n");
1152 ast_free(addresses
);
1155 ast_log(LOG_WARNING
, "Must run configure with '--with-execinfo' for stack backtraces.\n");
1159 void ast_verbose(const char *fmt
, ...)
1161 struct logmsg
*logmsg
= NULL
;
1162 struct ast_str
*buf
= NULL
;
1166 if (!(buf
= ast_str_thread_get(&verbose_buf
, VERBOSE_BUF_INIT_SIZE
)))
1169 if (ast_opt_timestamp
) {
1176 ast_localtime(&tv
, &tm
, NULL
);
1177 ast_strftime(date
, sizeof(date
), dateformat
, &tm
);
1178 datefmt
= alloca(strlen(date
) + 3 + strlen(fmt
) + 1);
1179 sprintf(datefmt
, "%c[%s] %s", 127, date
, fmt
);
1182 char *tmp
= alloca(strlen(fmt
) + 2);
1183 sprintf(tmp
, "%c%s", 127, fmt
);
1189 res
= ast_str_set_va(&buf
, 0, fmt
, ap
);
1192 /* If the build failed then we can drop this allocated message */
1193 if (res
== AST_DYNSTR_BUILD_FAILED
)
1196 if (!(logmsg
= ast_calloc(1, sizeof(*logmsg
) + res
+ 1)))
1199 strcpy(logmsg
->str
, buf
->str
);
1201 ast_log(LOG_VERBOSE
, "%s", logmsg
->str
+ 1);
1204 logmsg
->type
= LOGMSG_VERBOSE
;
1206 /* Add to the list and poke the thread if possible */
1207 if (logthread
!= AST_PTHREADT_NULL
) {
1208 AST_LIST_LOCK(&logmsgs
);
1209 AST_LIST_INSERT_TAIL(&logmsgs
, logmsg
, list
);
1210 ast_cond_signal(&logcond
);
1211 AST_LIST_UNLOCK(&logmsgs
);
1213 logger_print_verbose(logmsg
);
1218 int ast_register_verbose(void (*v
)(const char *string
))
1222 if (!(verb
= ast_malloc(sizeof(*verb
))))
1227 AST_RWLIST_WRLOCK(&verbosers
);
1228 AST_RWLIST_INSERT_HEAD(&verbosers
, verb
, list
);
1229 AST_RWLIST_UNLOCK(&verbosers
);
1234 int ast_unregister_verbose(void (*v
)(const char *string
))
1238 AST_RWLIST_WRLOCK(&verbosers
);
1239 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&verbosers
, cur
, list
) {
1240 if (cur
->verboser
== v
) {
1241 AST_RWLIST_REMOVE_CURRENT(list
);
1246 AST_RWLIST_TRAVERSE_SAFE_END
;
1247 AST_RWLIST_UNLOCK(&verbosers
);
1249 return cur
? 0 : -1;