Separate queue_log arguments into separate fields, and allow the text file to be used, even when realtime is used.

(closes issue #17082)
 Reported by: coolmig
 Patches: 
       20100720__issue17082.diff.txt uploaded by tilghman (license 14)
 Tested by: coolmig


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@278307 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Tilghman Lesher
2010-07-20 23:23:25 +00:00
parent ebf651105e
commit 82448ad7d2
5 changed files with 221 additions and 68 deletions

View File

@@ -270,6 +270,11 @@ Queue changes
queues for which he is a member, not just the queue that failed to reach queues for which he is a member, not just the queue that failed to reach
the member. the member.
* Added dialplan function QUEUE_EXISTS to check if a queue exists * Added dialplan function QUEUE_EXISTS to check if a queue exists
* The queue logger now allows events to optionally propagate to a file,
even when realtime logging is turned on. Additionally, realtime logging
supports sending the event arguments to 5 individual fields, although it
will fallback to the previous data definition, if the new table layout is
not found.
mISDN channel driver (chan_misdn) changes mISDN channel driver (chan_misdn) changes
---------------------------------------- ----------------------------------------

View File

@@ -26,18 +26,27 @@
; (defaults to yes). ; (defaults to yes).
;queue_log = no ;queue_log = no
; ;
; Determines whether the queue_log always goes to a file, even
; when a realtime backend is present (defaults to no).
;queue_log_to_file = yes
;
; Set the queue_log filename ; Set the queue_log filename
; (defaults to queue_log) ; (defaults to queue_log)
;queue_log_name = queue_log ;queue_log_name = queue_log
; ;
; Log rotation strategy: ; Log rotation strategy:
; sequential: Rename archived logs in order, such that the newest ; sequential: Rename archived logs in order, such that the newest
; has the highest sequence number [default]. ; has the highest sequence number [default]. When
; exec_after_rotate is set, ${filename} will specify
; the new archived logfile.
; rotate: Rotate all the old files, such that the oldest has the ; rotate: Rotate all the old files, such that the oldest has the
; highest sequence number [this is the expected behavior ; highest sequence number [this is the expected behavior
; for Unix administrators]. ; for Unix administrators]. When exec_after_rotate is
; set, ${filename} will specify the original root filename.
; timestamp: Rename the logfiles using a timestamp instead of a ; timestamp: Rename the logfiles using a timestamp instead of a
; sequence number when "logger rotate" is executed. ; sequence number when "logger rotate" is executed.
; When exec_after_rotate is set, ${filename} will
; specify the new archived logfile.
;rotatestrategy = rotate ;rotatestrategy = rotate
; ;
; Run a system command after rotating the files. This is mainly ; Run a system command after rotating the files. This is mainly

View File

@@ -0,0 +1,24 @@
CREATE TABLE queue_log (
-- Event date and time
time datetime,
-- "REALTIME", "NONE", or channel uniqueid
callid char(50),
-- Name of the queue affected
queuename char(50),
-- Interface name of the queue member
agent char(50),
-- One of ADDMEMBER, REMOVEMEMBER, RINGNOANSWER, EXITEMPTY, TRANSFER,
-- AGENTDUMP, ABANDON, SYSCOMPAT, CONNECT, COMPLETECALLER, COMPLETEAGENT,
-- PAUSEALL, UNPAUSEALL, PAUSE, UNPAUSE, PENALTY, ENTERQUEUE,
-- EXITWITHTIMEOUT, EXITEMPTY, EXITWITHKEY, or another defined by the user.
event char(20),
-- data1 through data5 are possible arguments to the event, the definitions
-- of which are dependent upon the type of event.
data1 char(50),
data2 char(50),
data3 char(50),
data4 char(50),
data5 char(50),
index bydate (time),
index qname (queuename,datetime)
);

View File

@@ -74,6 +74,7 @@ static char exec_after_rotate[256] = "";
static int filesize_reload_needed; static int filesize_reload_needed;
static unsigned int global_logmask = 0xFFFF; static unsigned int global_logmask = 0xFFFF;
static int queuelog_init;
static enum rotatestrategy { static enum rotatestrategy {
SEQUENTIAL = 1 << 0, /* Original method - create a new file, in order */ SEQUENTIAL = 1 << 0, /* Original method - create a new file, in order */
@@ -83,6 +84,8 @@ static enum rotatestrategy {
static struct { static struct {
unsigned int queue_log:1; unsigned int queue_log:1;
unsigned int queue_log_to_file:1;
unsigned int queue_adaptive_realtime:1;
} logfiles = { 1 }; } logfiles = { 1 };
static char hostname[MAXHOSTNAMELEN]; static char hostname[MAXHOSTNAMELEN];
@@ -206,6 +209,8 @@ AST_THREADSTORAGE(verbose_buf);
AST_THREADSTORAGE(log_buf); AST_THREADSTORAGE(log_buf);
#define LOG_BUF_INIT_SIZE 256 #define LOG_BUF_INIT_SIZE 256
static void logger_queue_init(void);
static unsigned int make_components(const char *s, int lineno) static unsigned int make_components(const char *s, int lineno)
{ {
char *w; char *w;
@@ -291,41 +296,49 @@ static void init_logger_chain(int locked)
const char *s; const char *s;
struct ast_flags config_flags = { 0 }; struct ast_flags config_flags = { 0 };
if (!(cfg = ast_config_load2("logger.conf", "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) if (!(cfg = ast_config_load2("logger.conf", "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
return; return;
}
/* delete our list of log channels */ /* delete our list of log channels */
if (!locked) if (!locked) {
AST_RWLIST_WRLOCK(&logchannels); AST_RWLIST_WRLOCK(&logchannels);
while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list))) }
while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list))) {
ast_free(chan); ast_free(chan);
}
global_logmask = 0; global_logmask = 0;
if (!locked) if (!locked) {
AST_RWLIST_UNLOCK(&logchannels); AST_RWLIST_UNLOCK(&logchannels);
}
errno = 0; errno = 0;
/* close syslog */ /* close syslog */
closelog(); closelog();
/* If no config file, we're fine, set default options. */ /* If no config file, we're fine, set default options. */
if (!cfg) { if (!cfg) {
if (errno) if (errno) {
fprintf(stderr, "Unable to open logger.conf: %s; default settings will be used.\n", strerror(errno)); fprintf(stderr, "Unable to open logger.conf: %s; default settings will be used.\n", strerror(errno));
else } else {
fprintf(stderr, "Errors detected in logger.conf: see above; default settings will be used.\n"); fprintf(stderr, "Errors detected in logger.conf: see above; default settings will be used.\n");
if (!(chan = ast_calloc(1, sizeof(*chan)))) }
if (!(chan = ast_calloc(1, sizeof(*chan)))) {
return; return;
}
chan->type = LOGTYPE_CONSOLE; chan->type = LOGTYPE_CONSOLE;
chan->logmask = __LOG_WARNING | __LOG_NOTICE | __LOG_ERROR; chan->logmask = __LOG_WARNING | __LOG_NOTICE | __LOG_ERROR;
if (!locked) if (!locked) {
AST_RWLIST_WRLOCK(&logchannels); AST_RWLIST_WRLOCK(&logchannels);
}
AST_RWLIST_INSERT_HEAD(&logchannels, chan, list); AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
global_logmask |= chan->logmask; global_logmask |= chan->logmask;
if (!locked) if (!locked) {
AST_RWLIST_UNLOCK(&logchannels); AST_RWLIST_UNLOCK(&logchannels);
}
return; return;
} }
if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) { if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) {
if (ast_true(s)) { if (ast_true(s)) {
if (gethostname(hostname, sizeof(hostname) - 1)) { if (gethostname(hostname, sizeof(hostname) - 1)) {
@@ -340,21 +353,28 @@ static void init_logger_chain(int locked)
ast_copy_string(dateformat, s, sizeof(dateformat)); ast_copy_string(dateformat, s, sizeof(dateformat));
else else
ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat)); ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) {
logfiles.queue_log = ast_true(s); logfiles.queue_log = ast_true(s);
if ((s = ast_variable_retrieve(cfg, "general", "queue_log_name"))) }
if ((s = ast_variable_retrieve(cfg, "general", "queue_log_to_file"))) {
logfiles.queue_log_to_file = ast_true(s);
}
if ((s = ast_variable_retrieve(cfg, "general", "queue_log_name"))) {
ast_copy_string(queue_log_name, s, sizeof(queue_log_name)); ast_copy_string(queue_log_name, s, sizeof(queue_log_name));
if ((s = ast_variable_retrieve(cfg, "general", "exec_after_rotate"))) }
if ((s = ast_variable_retrieve(cfg, "general", "exec_after_rotate"))) {
ast_copy_string(exec_after_rotate, s, sizeof(exec_after_rotate)); ast_copy_string(exec_after_rotate, s, sizeof(exec_after_rotate));
}
if ((s = ast_variable_retrieve(cfg, "general", "rotatestrategy"))) { if ((s = ast_variable_retrieve(cfg, "general", "rotatestrategy"))) {
if (strcasecmp(s, "timestamp") == 0) if (strcasecmp(s, "timestamp") == 0) {
rotatestrategy = TIMESTAMP; rotatestrategy = TIMESTAMP;
else if (strcasecmp(s, "rotate") == 0) } else if (strcasecmp(s, "rotate") == 0) {
rotatestrategy = ROTATE; rotatestrategy = ROTATE;
else if (strcasecmp(s, "sequential") == 0) } else if (strcasecmp(s, "sequential") == 0) {
rotatestrategy = SEQUENTIAL; rotatestrategy = SEQUENTIAL;
else } else {
fprintf(stderr, "Unknown rotatestrategy: %s\n", s); fprintf(stderr, "Unknown rotatestrategy: %s\n", s);
}
} else { } else {
if ((s = ast_variable_retrieve(cfg, "general", "rotatetimestamp"))) { if ((s = ast_variable_retrieve(cfg, "general", "rotatetimestamp"))) {
rotatestrategy = ast_true(s) ? TIMESTAMP : SEQUENTIAL; rotatestrategy = ast_true(s) ? TIMESTAMP : SEQUENTIAL;
@@ -362,17 +382,27 @@ static void init_logger_chain(int locked)
} }
} }
if (!locked) if (!locked) {
AST_RWLIST_WRLOCK(&logchannels); AST_RWLIST_WRLOCK(&logchannels);
}
var = ast_variable_browse(cfg, "logfiles"); var = ast_variable_browse(cfg, "logfiles");
for (; var; var = var->next) { for (; var; var = var->next) {
if (!(chan = make_logchannel(var->name, var->value, var->lineno))) if (!(chan = make_logchannel(var->name, var->value, var->lineno))) {
continue; continue;
}
AST_RWLIST_INSERT_HEAD(&logchannels, chan, list); AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
global_logmask |= chan->logmask; global_logmask |= chan->logmask;
} }
if (!locked) if (qlog) {
char tmp[4096];
fclose(qlog);
snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
qlog = fopen(tmp, "a");
}
if (!locked) {
AST_RWLIST_UNLOCK(&logchannels); AST_RWLIST_UNLOCK(&logchannels);
}
ast_config_destroy(cfg); ast_config_destroy(cfg);
} }
@@ -429,29 +459,69 @@ void ast_child_verbose(int level, const char *fmt, ...)
void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...) void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
{ {
va_list ap; va_list ap;
struct timeval tv;
struct ast_tm tm;
char qlog_msg[8192]; char qlog_msg[8192];
int qlog_len; int qlog_len;
char time_str[16]; char time_str[30];
if (!queuelog_init) {
queuelog_init = 1;
logger_queue_init();
}
if (ast_check_realtime("queue_log")) { if (ast_check_realtime("queue_log")) {
tv = ast_tvnow();
ast_localtime(&tv, &tm, NULL);
ast_strftime(time_str, sizeof(time_str), "%F %T.%6q", &tm);
va_start(ap, fmt); va_start(ap, fmt);
vsnprintf(qlog_msg, sizeof(qlog_msg), fmt, ap); vsnprintf(qlog_msg, sizeof(qlog_msg), fmt, ap);
va_end(ap); va_end(ap);
snprintf(time_str, sizeof(time_str), "%ld", (long)time(NULL)); if (logfiles.queue_adaptive_realtime) {
ast_store_realtime("queue_log", "time", time_str, AST_DECLARE_APP_ARGS(args,
"callid", callid, AST_APP_ARG(data)[5];
"queuename", queuename, );
"agent", agent, AST_NONSTANDARD_APP_ARGS(args, qlog_msg, '|');
"event", event, /* Ensure fields are large enough to receive data */
"data", qlog_msg, ast_realtime_require_field("queue_log", "data1", RQ_CHAR, strlen(S_OR(args.data[0], "")),
SENTINEL); "data2", RQ_CHAR, strlen(S_OR(args.data[1], "")),
} else { "data3", RQ_CHAR, strlen(S_OR(args.data[2], "")),
if (qlog) { "data4", RQ_CHAR, strlen(S_OR(args.data[3], "")),
va_start(ap, fmt); "data5", RQ_CHAR, strlen(S_OR(args.data[4], "")),
qlog_len = snprintf(qlog_msg, sizeof(qlog_msg), "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event); SENTINEL);
vsnprintf(qlog_msg + qlog_len, sizeof(qlog_msg) - qlog_len, fmt, ap);
va_end(ap); /* Store the log */
ast_store_realtime("queue_log", "time", time_str,
"callid", callid,
"queuename", queuename,
"agent", agent,
"event", event,
"data1", S_OR(args.data[0], ""),
"data2", S_OR(args.data[1], ""),
"data3", S_OR(args.data[2], ""),
"data4", S_OR(args.data[3], ""),
"data5", S_OR(args.data[4], ""),
SENTINEL);
} else {
ast_store_realtime("queue_log", "time", time_str,
"callid", callid,
"queuename", queuename,
"agent", agent,
"event", event,
"data", qlog_msg,
SENTINEL);
} }
if (!logfiles.queue_log_to_file) {
return;
}
}
if (qlog) {
va_start(ap, fmt);
qlog_len = snprintf(qlog_msg, sizeof(qlog_msg), "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
vsnprintf(qlog_msg + qlog_len, sizeof(qlog_msg) - qlog_len, fmt, ap);
va_end(ap);
AST_RWLIST_RDLOCK(&logchannels); AST_RWLIST_RDLOCK(&logchannels);
if (qlog) { if (qlog) {
fprintf(qlog, "%s\n", qlog_msg); fprintf(qlog, "%s\n", qlog_msg);
@@ -481,6 +551,8 @@ static int rotate_file(const char *filename)
if (rename(filename, new)) { if (rename(filename, new)) {
fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new); fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
res = -1; res = -1;
} else {
filename = new;
} }
break; break;
case TIMESTAMP: case TIMESTAMP:
@@ -488,6 +560,8 @@ static int rotate_file(const char *filename)
if (rename(filename, new)) { if (rename(filename, new)) {
fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new); fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
res = -1; res = -1;
} else {
filename = new;
} }
break; break;
case ROTATE: case ROTATE:
@@ -553,25 +627,25 @@ static int reload_logger(int rotate)
int queue_rotate = rotate; int queue_rotate = rotate;
struct logchannel *f; struct logchannel *f;
int res = 0; int res = 0;
struct stat st;
AST_RWLIST_WRLOCK(&logchannels); AST_RWLIST_WRLOCK(&logchannels);
if (qlog) { if (qlog) {
if (rotate < 0) { if (rotate < 0) {
/* Check filesize - this one typically doesn't need an auto-rotate */ /* Check filesize - this one typically doesn't need an auto-rotate */
snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name); if (ftello(qlog) > 0x40000000) { /* Arbitrarily, 1 GB */
if (stat(old, &st) != 0 || st.st_size > 0x40000000) { /* Arbitrarily, 1 GB */
fclose(qlog); fclose(qlog);
qlog = NULL; qlog = NULL;
} else } else {
queue_rotate = 0; queue_rotate = 0;
}
} else { } else {
fclose(qlog); fclose(qlog);
qlog = NULL; qlog = NULL;
} }
} else } else {
queue_rotate = 0; queue_rotate = 0;
}
ast_mkdir(ast_config_AST_LOG_DIR, 0777); ast_mkdir(ast_config_AST_LOG_DIR, 0777);
@@ -581,10 +655,16 @@ static int reload_logger(int rotate)
manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename); manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
} }
if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) { if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
int rotate_this = 0;
if (ftello(f->fileptr) > 0x40000000) { /* Arbitrarily, 1 GB */
/* Be more proactive about rotating massive log files */
rotate_this = 1;
}
fclose(f->fileptr); /* Close file */ fclose(f->fileptr); /* Close file */
f->fileptr = NULL; f->fileptr = NULL;
if (rotate) if (rotate || rotate_this) {
rotate_file(f->filename); rotate_file(f->filename);
}
} }
} }
@@ -593,20 +673,42 @@ static int reload_logger(int rotate)
init_logger_chain(1 /* locked */); init_logger_chain(1 /* locked */);
if (logfiles.queue_log) { if (logfiles.queue_log) {
snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name); do {
if (queue_rotate) ast_unload_realtime("queue_log");
rotate_file(old); if (ast_check_realtime("queue_log")) {
if (!ast_realtime_require_field("queue_log",
"time", RQ_DATETIME, 26, "data1", RQ_CHAR, 20,
"data2", RQ_CHAR, 20, "data3", RQ_CHAR, 20,
"data4", RQ_CHAR, 20, "data5", RQ_CHAR, 20, SENTINEL)) {
logfiles.queue_adaptive_realtime = 1;
} else {
logfiles.queue_adaptive_realtime = 0;
}
qlog = fopen(old, "a"); if (!logfiles.queue_log_to_file) {
if (qlog) { /* Skip the following section */
AST_RWLIST_UNLOCK(&logchannels); break;
ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", ""); }
AST_RWLIST_WRLOCK(&logchannels); }
ast_verb(1, "Asterisk Queue Logger restarted\n");
} else { fclose(qlog);
ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno)); qlog = NULL;
res = -1; snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
} if (queue_rotate) {
rotate_file(old);
}
qlog = fopen(old, "a");
if (qlog) {
AST_RWLIST_UNLOCK(&logchannels);
ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
AST_RWLIST_WRLOCK(&logchannels);
ast_verb(1, "Asterisk Queue Logger restarted\n");
} else {
ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
res = -1;
}
} while (0);
} }
AST_RWLIST_UNLOCK(&logchannels); AST_RWLIST_UNLOCK(&logchannels);
@@ -927,11 +1029,26 @@ static void *logger_thread(void *data)
return NULL; return NULL;
} }
static void logger_queue_init(void)
{
/* Preloaded modules are up. */
ast_unload_realtime("queue_log");
if (logfiles.queue_log && ast_check_realtime("queue_log")) {
if (!ast_realtime_require_field("queue_log",
"time", RQ_DATETIME, 26, "data1", RQ_CHAR, 20,
"data2", RQ_CHAR, 20, "data3", RQ_CHAR, 20,
"data4", RQ_CHAR, 20, "data5", RQ_CHAR, 20, SENTINEL)) {
logfiles.queue_adaptive_realtime = 1;
} else {
logfiles.queue_adaptive_realtime = 0;
}
}
ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
}
int init_logger(void) int init_logger(void)
{ {
char tmp[256];
int res = 0;
/* auto rotate if sig SIGXFSZ comes a-knockin */ /* auto rotate if sig SIGXFSZ comes a-knockin */
sigaction(SIGXFSZ, &handle_SIGXFSZ, NULL); sigaction(SIGXFSZ, &handle_SIGXFSZ, NULL);
@@ -946,16 +1063,11 @@ int init_logger(void)
ast_cli_register_multiple(cli_logger, ARRAY_LEN(cli_logger)); ast_cli_register_multiple(cli_logger, ARRAY_LEN(cli_logger));
ast_mkdir(ast_config_AST_LOG_DIR, 0777); ast_mkdir(ast_config_AST_LOG_DIR, 0777);
/* create log channels */ /* create log channels */
init_logger_chain(0 /* locked */); init_logger_chain(0 /* locked */);
if (logfiles.queue_log) { return 0;
snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
qlog = fopen(tmp, "a");
ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
}
return res;
} }
void close_logger(void) void close_logger(void)

View File

@@ -1182,6 +1182,9 @@ static int require_pgsql(const char *database, const char *tablename, va_list ap
} else if (strncmp(column->type, "float", 5) == 0 && !ast_rq_is_int(type) && type != RQ_FLOAT) { } else if (strncmp(column->type, "float", 5) == 0 && !ast_rq_is_int(type) && type != RQ_FLOAT) {
ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type); ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
res = -1; res = -1;
} else if (strncmp(column->type, "timestamp", 9) == 0 && type != RQ_DATETIME) {
ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
res = -1;
} else { /* There are other types that no module implements yet */ } else { /* There are other types that no module implements yet */
ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name); ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
res = -1; res = -1;