|
|
|
|
@ -86,6 +86,11 @@ static int display_callids;
|
|
|
|
|
|
|
|
|
|
AST_THREADSTORAGE(unique_callid);
|
|
|
|
|
|
|
|
|
|
static int logger_queue_size;
|
|
|
|
|
static int logger_queue_limit = 1000;
|
|
|
|
|
static int logger_messages_discarded;
|
|
|
|
|
static unsigned int high_water_alert;
|
|
|
|
|
|
|
|
|
|
static enum rotatestrategy {
|
|
|
|
|
NONE = 0, /* Do not rotate log files at all, instead rely on external mechanisms */
|
|
|
|
|
SEQUENTIAL = 1 << 0, /* Original method - create a new file, in order */
|
|
|
|
|
@ -719,6 +724,16 @@ static int init_logger_chain(const char *altconf)
|
|
|
|
|
fprintf(stderr, "rotatetimestamp option has been deprecated. Please use rotatestrategy instead.\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ((s = ast_variable_retrieve(cfg, "general", "logger_queue_limit"))) {
|
|
|
|
|
if (sscanf(s, "%30d", &logger_queue_limit) != 1) {
|
|
|
|
|
fprintf(stderr, "logger_queue_limit has an invalid value. Leaving at default of %d.\n",
|
|
|
|
|
logger_queue_limit);
|
|
|
|
|
}
|
|
|
|
|
if (logger_queue_limit < 10) {
|
|
|
|
|
fprintf(stderr, "logger_queue_limit must be >= 10. Setting to 10.\n");
|
|
|
|
|
logger_queue_limit = 10;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var = ast_variable_browse(cfg, "logfiles");
|
|
|
|
|
for (; var; var = var->next) {
|
|
|
|
|
@ -1297,6 +1312,7 @@ static char *handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struc
|
|
|
|
|
case CLI_GENERATE:
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
ast_cli(a->fd, "Logger queue limit: %d\n\n", logger_queue_limit);
|
|
|
|
|
ast_cli(a->fd, FORMATL, "Channel", "Type", "Formatter", "Status");
|
|
|
|
|
ast_cli(a->fd, "Configuration\n");
|
|
|
|
|
ast_cli(a->fd, FORMATL, "-------", "----", "---------", "------");
|
|
|
|
|
@ -1585,6 +1601,79 @@ static void logger_print_normal(struct logmsg *logmsg)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct logmsg * __attribute__((format(printf, 7, 0))) format_log_message_ap(int level,
|
|
|
|
|
int sublevel, const char *file, int line, const char *function, ast_callid callid,
|
|
|
|
|
const char *fmt, va_list ap)
|
|
|
|
|
{
|
|
|
|
|
struct logmsg *logmsg = NULL;
|
|
|
|
|
struct ast_str *buf = NULL;
|
|
|
|
|
struct ast_tm tm;
|
|
|
|
|
struct timeval now = ast_tvnow();
|
|
|
|
|
int res = 0;
|
|
|
|
|
char datestring[256];
|
|
|
|
|
|
|
|
|
|
if (!(buf = ast_str_thread_get(&log_buf, LOG_BUF_INIT_SIZE))) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Build string */
|
|
|
|
|
res = ast_str_set_va(&buf, BUFSIZ, fmt, ap);
|
|
|
|
|
|
|
|
|
|
/* If the build failed, then abort and free this structure */
|
|
|
|
|
if (res == AST_DYNSTR_BUILD_FAILED) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create a new logging message */
|
|
|
|
|
if (!(logmsg = ast_calloc_with_stringfields(1, struct logmsg, res + 128))) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Copy string over */
|
|
|
|
|
ast_string_field_set(logmsg, message, ast_str_buffer(buf));
|
|
|
|
|
|
|
|
|
|
/* Set type */
|
|
|
|
|
if (level == __LOG_VERBOSE) {
|
|
|
|
|
logmsg->type = LOGMSG_VERBOSE;
|
|
|
|
|
} else {
|
|
|
|
|
logmsg->type = LOGMSG_NORMAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (display_callids && callid) {
|
|
|
|
|
logmsg->callid = callid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create our date/time */
|
|
|
|
|
ast_localtime(&now, &tm, NULL);
|
|
|
|
|
ast_strftime(datestring, sizeof(datestring), dateformat, &tm);
|
|
|
|
|
ast_string_field_set(logmsg, date, datestring);
|
|
|
|
|
|
|
|
|
|
/* Copy over data */
|
|
|
|
|
logmsg->level = level;
|
|
|
|
|
logmsg->sublevel = sublevel;
|
|
|
|
|
logmsg->line = line;
|
|
|
|
|
ast_string_field_set(logmsg, level_name, levels[level]);
|
|
|
|
|
ast_string_field_set(logmsg, file, file);
|
|
|
|
|
ast_string_field_set(logmsg, function, function);
|
|
|
|
|
logmsg->lwp = ast_get_tid();
|
|
|
|
|
|
|
|
|
|
return logmsg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct logmsg * __attribute__((format(printf, 7, 0))) format_log_message(int level,
|
|
|
|
|
int sublevel, const char *file, int line, const char *function, ast_callid callid,
|
|
|
|
|
const char *fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
struct logmsg *logmsg;
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
logmsg = format_log_message_ap(level, sublevel, file, line, function, callid, fmt, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
return logmsg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Actual logging thread */
|
|
|
|
|
static void *logger_thread(void *data)
|
|
|
|
|
{
|
|
|
|
|
@ -1601,8 +1690,21 @@ static void *logger_thread(void *data)
|
|
|
|
|
ast_cond_wait(&logcond, &logmsgs.lock);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (high_water_alert) {
|
|
|
|
|
msg = format_log_message(__LOG_WARNING, 0, "logger", 0, "***", 0,
|
|
|
|
|
"Logging resumed. %d message%s discarded.\n",
|
|
|
|
|
logger_messages_discarded, logger_messages_discarded == 1 ? "" : "s");
|
|
|
|
|
if (msg) {
|
|
|
|
|
AST_LIST_INSERT_TAIL(&logmsgs, msg, list);
|
|
|
|
|
}
|
|
|
|
|
high_water_alert = 0;
|
|
|
|
|
logger_messages_discarded = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next = AST_LIST_FIRST(&logmsgs);
|
|
|
|
|
AST_LIST_HEAD_INIT_NOLOCK(&logmsgs);
|
|
|
|
|
logger_queue_size = 0;
|
|
|
|
|
AST_LIST_UNLOCK(&logmsgs);
|
|
|
|
|
|
|
|
|
|
/* Otherwise go through and process each message in the order added */
|
|
|
|
|
@ -1861,79 +1963,36 @@ void ast_callid_threadstorage_auto_clean(ast_callid callid, int callid_created)
|
|
|
|
|
/*!
|
|
|
|
|
* \brief send log messages to syslog and/or the console
|
|
|
|
|
*/
|
|
|
|
|
static void __attribute__((format(printf, 7, 0))) ast_log_full(int level, int sublevel, const char *file, int line, const char *function, ast_callid callid, const char *fmt, va_list ap)
|
|
|
|
|
static void __attribute__((format(printf, 7, 0))) ast_log_full(int level, int sublevel,
|
|
|
|
|
const char *file, int line, const char *function, ast_callid callid,
|
|
|
|
|
const char *fmt, va_list ap)
|
|
|
|
|
{
|
|
|
|
|
struct logmsg *logmsg = NULL;
|
|
|
|
|
struct ast_str *buf = NULL;
|
|
|
|
|
struct ast_tm tm;
|
|
|
|
|
struct timeval now = ast_tvnow();
|
|
|
|
|
int res = 0;
|
|
|
|
|
char datestring[256];
|
|
|
|
|
|
|
|
|
|
if (level == __LOG_VERBOSE && ast_opt_remote && ast_opt_exec) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(buf = ast_str_thread_get(&log_buf, LOG_BUF_INIT_SIZE)))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (level != __LOG_VERBOSE && AST_RWLIST_EMPTY(&logchannels)) {
|
|
|
|
|
/*
|
|
|
|
|
* we don't have the logger chain configured yet,
|
|
|
|
|
* so just log to stdout
|
|
|
|
|
*/
|
|
|
|
|
int result;
|
|
|
|
|
result = ast_str_set_va(&buf, BUFSIZ, fmt, ap); /* XXX BUFSIZ ? */
|
|
|
|
|
if (result != AST_DYNSTR_BUILD_FAILED) {
|
|
|
|
|
term_filter_escapes(ast_str_buffer(buf));
|
|
|
|
|
fputs(ast_str_buffer(buf), stdout);
|
|
|
|
|
AST_LIST_LOCK(&logmsgs);
|
|
|
|
|
if (logger_queue_size >= logger_queue_limit && !close_logger_thread) {
|
|
|
|
|
logger_messages_discarded++;
|
|
|
|
|
if (!high_water_alert && !close_logger_thread) {
|
|
|
|
|
logmsg = format_log_message(__LOG_WARNING, 0, "logger", 0, "***", 0,
|
|
|
|
|
"Log queue threshold (%d) exceeded. Discarding new messages.\n", logger_queue_limit);
|
|
|
|
|
AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list);
|
|
|
|
|
high_water_alert = 1;
|
|
|
|
|
ast_cond_signal(&logcond);
|
|
|
|
|
}
|
|
|
|
|
AST_LIST_UNLOCK(&logmsgs);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
AST_LIST_UNLOCK(&logmsgs);
|
|
|
|
|
|
|
|
|
|
/* Ignore anything that never gets logged anywhere */
|
|
|
|
|
if (level != __LOG_VERBOSE && !(global_logmask & (1 << level)))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Build string */
|
|
|
|
|
res = ast_str_set_va(&buf, BUFSIZ, fmt, ap);
|
|
|
|
|
|
|
|
|
|
/* If the build failed, then abort and free this structure */
|
|
|
|
|
if (res == AST_DYNSTR_BUILD_FAILED)
|
|
|
|
|
logmsg = format_log_message_ap(level, sublevel, file, line, function, callid, fmt, ap);
|
|
|
|
|
if (!logmsg) {
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Create a new logging message */
|
|
|
|
|
if (!(logmsg = ast_calloc_with_stringfields(1, struct logmsg, res + 128)))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Copy string over */
|
|
|
|
|
ast_string_field_set(logmsg, message, ast_str_buffer(buf));
|
|
|
|
|
|
|
|
|
|
/* Set type */
|
|
|
|
|
if (level == __LOG_VERBOSE) {
|
|
|
|
|
logmsg->type = LOGMSG_VERBOSE;
|
|
|
|
|
} else {
|
|
|
|
|
logmsg->type = LOGMSG_NORMAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (display_callids && callid) {
|
|
|
|
|
logmsg->callid = callid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create our date/time */
|
|
|
|
|
ast_localtime(&now, &tm, NULL);
|
|
|
|
|
ast_strftime(datestring, sizeof(datestring), dateformat, &tm);
|
|
|
|
|
ast_string_field_set(logmsg, date, datestring);
|
|
|
|
|
|
|
|
|
|
/* Copy over data */
|
|
|
|
|
logmsg->level = level;
|
|
|
|
|
logmsg->sublevel = sublevel;
|
|
|
|
|
logmsg->line = line;
|
|
|
|
|
ast_string_field_set(logmsg, level_name, levels[level]);
|
|
|
|
|
ast_string_field_set(logmsg, file, file);
|
|
|
|
|
ast_string_field_set(logmsg, function, function);
|
|
|
|
|
logmsg->lwp = ast_get_tid();
|
|
|
|
|
|
|
|
|
|
/* If the logger thread is active, append it to the tail end of the list - otherwise skip that step */
|
|
|
|
|
if (logthread != AST_PTHREADT_NULL) {
|
|
|
|
|
AST_LIST_LOCK(&logmsgs);
|
|
|
|
|
@ -1942,6 +2001,7 @@ static void __attribute__((format(printf, 7, 0))) ast_log_full(int level, int su
|
|
|
|
|
logmsg_free(logmsg);
|
|
|
|
|
} else {
|
|
|
|
|
AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list);
|
|
|
|
|
logger_queue_size++;
|
|
|
|
|
ast_cond_signal(&logcond);
|
|
|
|
|
}
|
|
|
|
|
AST_LIST_UNLOCK(&logmsgs);
|
|
|
|
|
@ -2300,3 +2360,12 @@ const char *ast_logger_get_dateformat(void)
|
|
|
|
|
return dateformat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ast_logger_set_queue_limit(int queue_limit)
|
|
|
|
|
{
|
|
|
|
|
logger_queue_limit = queue_limit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ast_logger_get_queue_limit(void)
|
|
|
|
|
{
|
|
|
|
|
return logger_queue_limit;
|
|
|
|
|
}
|
|
|
|
|
|