mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-06 12:36:58 +00:00
Add queue logging and fix indications buglet
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@2159 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -35,6 +35,8 @@
|
|||||||
#include <sys/signal.h>
|
#include <sys/signal.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include "../astconf.h"
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
#define QUEUE_STRATEGY_RINGALL 0
|
#define QUEUE_STRATEGY_RINGALL 0
|
||||||
@@ -128,6 +130,8 @@ struct queue_ent {
|
|||||||
char announce[80]; /* Announcement to play */
|
char announce[80]; /* Announcement to play */
|
||||||
char context[80]; /* Context when user exits queue */
|
char context[80]; /* Context when user exits queue */
|
||||||
int pos; /* Where we are in the queue */
|
int pos; /* Where we are in the queue */
|
||||||
|
int opos; /* Where we started in the queue */
|
||||||
|
int handled; /* Whether our call was handled */
|
||||||
time_t start; /* When we started holding */
|
time_t start; /* When we started holding */
|
||||||
struct ast_channel *chan; /* Our channel */
|
struct ast_channel *chan; /* Our channel */
|
||||||
struct queue_ent *next; /* The next queue entry */
|
struct queue_ent *next; /* The next queue entry */
|
||||||
@@ -221,6 +225,7 @@ static int join_queue(char *queuename, struct queue_ent *qe)
|
|||||||
qe->next = NULL;
|
qe->next = NULL;
|
||||||
qe->parent = q;
|
qe->parent = q;
|
||||||
qe->pos = ++pos;
|
qe->pos = ++pos;
|
||||||
|
qe->opos = pos;
|
||||||
strncpy(qe->moh, q->moh, sizeof(qe->moh));
|
strncpy(qe->moh, q->moh, sizeof(qe->moh));
|
||||||
strncpy(qe->announce, q->announce, sizeof(qe->announce));
|
strncpy(qe->announce, q->announce, sizeof(qe->announce));
|
||||||
strncpy(qe->context, q->context, sizeof(qe->context));
|
strncpy(qe->context, q->context, sizeof(qe->context));
|
||||||
@@ -699,6 +704,9 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
|
|||||||
int allowredir_out=0;
|
int allowredir_out=0;
|
||||||
int allowdisconnect=0;
|
int allowdisconnect=0;
|
||||||
char restofit[AST_MAX_EXTENSION];
|
char restofit[AST_MAX_EXTENSION];
|
||||||
|
char oldexten[AST_MAX_EXTENSION]="";
|
||||||
|
char oldcontext[AST_MAX_EXTENSION]="";
|
||||||
|
char queuename[256]="";
|
||||||
char *newnum;
|
char *newnum;
|
||||||
struct ast_channel *peer;
|
struct ast_channel *peer;
|
||||||
struct localuser *lpeer;
|
struct localuser *lpeer;
|
||||||
@@ -707,8 +715,10 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
|
|||||||
int x=0;
|
int x=0;
|
||||||
char *announce = NULL;
|
char *announce = NULL;
|
||||||
char digit = 0;
|
char digit = 0;
|
||||||
|
time_t callstart;
|
||||||
/* Hold the lock while we setup the outgoing calls */
|
/* Hold the lock while we setup the outgoing calls */
|
||||||
ast_mutex_lock(&qe->parent->lock);
|
ast_mutex_lock(&qe->parent->lock);
|
||||||
|
strncpy(queuename, qe->parent->name, sizeof(queuename) - 1);
|
||||||
cur = qe->parent->members;
|
cur = qe->parent->members;
|
||||||
if (strlen(qe->announce))
|
if (strlen(qe->announce))
|
||||||
announce = qe->announce;
|
announce = qe->announce;
|
||||||
@@ -797,6 +807,7 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
|
|||||||
/* Ah ha! Someone answered within the desired timeframe. Of course after this
|
/* Ah ha! Someone answered within the desired timeframe. Of course after this
|
||||||
we will always return with -1 so that it is hung up properly after the
|
we will always return with -1 so that it is hung up properly after the
|
||||||
conversation. */
|
conversation. */
|
||||||
|
qe->handled++;
|
||||||
if (!strcmp(qe->chan->type,"Zap")) {
|
if (!strcmp(qe->chan->type,"Zap")) {
|
||||||
if (tmp->dataquality) zapx = 0;
|
if (tmp->dataquality) zapx = 0;
|
||||||
ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
|
ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
|
||||||
@@ -822,6 +833,7 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
|
|||||||
if (res2) {
|
if (res2) {
|
||||||
/* Agent must have hung up */
|
/* Agent must have hung up */
|
||||||
ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
|
ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
|
||||||
|
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "");
|
||||||
ast_hangup(peer);
|
ast_hangup(peer);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -832,6 +844,7 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
|
|||||||
/* Make sure channels are compatible */
|
/* Make sure channels are compatible */
|
||||||
res = ast_channel_make_compatible(qe->chan, peer);
|
res = ast_channel_make_compatible(qe->chan, peer);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "");
|
||||||
ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
|
ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
|
||||||
ast_hangup(peer);
|
ast_hangup(peer);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -843,7 +856,18 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
|
|||||||
ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
|
ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
|
||||||
ast_channel_sendurl( peer, url );
|
ast_channel_sendurl( peer, url );
|
||||||
} /* /JDG */
|
} /* /JDG */
|
||||||
|
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
|
||||||
|
strncpy(oldcontext, qe->chan->context, sizeof(oldcontext) - 1);
|
||||||
|
strncpy(oldexten, qe->chan->exten, sizeof(oldexten) - 1);
|
||||||
|
time(&callstart);
|
||||||
bridge = ast_bridge_call(qe->chan, peer, allowredir_in, allowredir_out, allowdisconnect);
|
bridge = ast_bridge_call(qe->chan, peer, allowredir_in, allowredir_out, allowdisconnect);
|
||||||
|
if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
|
||||||
|
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
|
||||||
|
} else if (qe->chan->_softhangup) {
|
||||||
|
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
|
||||||
|
} else {
|
||||||
|
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
|
||||||
|
}
|
||||||
|
|
||||||
if(bridge != AST_PBX_NO_HANGUP_PEER)
|
if(bridge != AST_PBX_NO_HANGUP_PEER)
|
||||||
ast_hangup(peer);
|
ast_hangup(peer);
|
||||||
@@ -1139,41 +1163,54 @@ static int queue_exec(struct ast_channel *chan, void *data)
|
|||||||
qe.chan = chan;
|
qe.chan = chan;
|
||||||
qe.start = time(NULL);
|
qe.start = time(NULL);
|
||||||
if (!join_queue(queuename, &qe)) {
|
if (!join_queue(queuename, &qe)) {
|
||||||
|
ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : "");
|
||||||
/* Start music on hold */
|
/* Start music on hold */
|
||||||
ast_moh_start(chan, qe.moh);
|
ast_moh_start(chan, qe.moh);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
res = wait_our_turn(&qe);
|
res = wait_our_turn(&qe);
|
||||||
/* If they hungup, return immediately */
|
/* If they hungup, return immediately */
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
|
||||||
if (option_verbose > 2) {
|
if (option_verbose > 2) {
|
||||||
ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
|
ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
|
||||||
res = -1;
|
res = -1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!res)
|
if (!res)
|
||||||
break;
|
break;
|
||||||
if (valid_exit(&qe, res))
|
if (valid_exit(&qe, res)) {
|
||||||
|
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!res) {
|
if (!res) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
res = try_calling(&qe, options, announceoverride, url, &go_on);
|
res = try_calling(&qe, options, announceoverride, url, &go_on);
|
||||||
if (res)
|
if (res) {
|
||||||
|
if (res < 0) {
|
||||||
|
if (!qe.handled)
|
||||||
|
ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
|
||||||
|
} else if (res > 0)
|
||||||
|
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
res = wait_a_bit(&qe);
|
res = wait_a_bit(&qe);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
|
||||||
if (option_verbose > 2) {
|
if (option_verbose > 2) {
|
||||||
ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
|
ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
|
||||||
res = -1;
|
res = -1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (res && valid_exit(&qe, res))
|
if (res && valid_exit(&qe, res)) {
|
||||||
|
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
/* exit after a timeout if 'n' option enabled */
|
/* exit after a timeout if 'n' option enabled */
|
||||||
if (go_on) {
|
if (go_on) {
|
||||||
|
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
|
||||||
res = 0;
|
res = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
62
doc/queuelog.txt
Executable file
62
doc/queuelog.txt
Executable file
@@ -0,0 +1,62 @@
|
|||||||
|
Queue Log Information
|
||||||
|
=====================
|
||||||
|
|
||||||
|
In order to properly manage ACD queues, it is important to be able to
|
||||||
|
keep track of details of call setups and teardowns in much greater detail
|
||||||
|
than traditional call detail records provide. In order to support this,
|
||||||
|
extensive and detailed tracing of every queued call is stored in the
|
||||||
|
queue log, located (by default) in /var/log/asterisk/queue_log.
|
||||||
|
|
||||||
|
These are the events (and associated information) in the queue log:
|
||||||
|
|
||||||
|
ABANDON(position|origposition|waittime)
|
||||||
|
The caller abandoned their position in the queue. The position is the
|
||||||
|
caller's position in the queue when they hungup, the origposition is
|
||||||
|
the original position the caller was when they first entered the
|
||||||
|
queue, and the waittime is how long the call had been waiting in the
|
||||||
|
queue at the time of disconnect.
|
||||||
|
|
||||||
|
AGENTDUMP
|
||||||
|
The agent dumped the caller while listening to the queue announcement.
|
||||||
|
|
||||||
|
COMPLETEAGENT(holdtime|calltime|origposition)
|
||||||
|
The caller was connected to an agent, and the call was terminated normally
|
||||||
|
by the *agent*. The caller's hold time and the length of the call are both
|
||||||
|
recorded. The caller's original position in the queue is recorded in
|
||||||
|
origposition.
|
||||||
|
|
||||||
|
COMPLETECALLER(holdtime|calltime|origposition)
|
||||||
|
The caller was connected to an agent, and the call was terminated normally
|
||||||
|
by the *caller*. The caller's hold time and the length of the call are both
|
||||||
|
recorded. The caller's original position in the queue is recorded in
|
||||||
|
origposition.
|
||||||
|
|
||||||
|
CONFIGRELOAD
|
||||||
|
The configuration has been reloaded (e.g. with asterisk -rx reload)
|
||||||
|
|
||||||
|
CONNECT(holdtime)
|
||||||
|
The caller was connected to an agent. Hold time represents the amount
|
||||||
|
of time the caller was on hold.
|
||||||
|
|
||||||
|
ENTERQUEUE(url|callerid)
|
||||||
|
A call has entered the queue. URL (if specified) and Caller*ID are placed
|
||||||
|
in the log.
|
||||||
|
|
||||||
|
EXITWITHKEY(key|position)
|
||||||
|
The caller elected to use a menu key to exit the queue. The key and
|
||||||
|
the caller's position in the queue are recorded.
|
||||||
|
|
||||||
|
EXITWITHTIMEOUT(position)
|
||||||
|
The caller was on hold too long and the timeout expired.
|
||||||
|
|
||||||
|
QUEUESTART
|
||||||
|
The queueing system has been started for the first time this session.
|
||||||
|
|
||||||
|
SYSCOMPAT
|
||||||
|
A call was answered by an agent, but the call was dropped because the
|
||||||
|
channels were not compatible.
|
||||||
|
|
||||||
|
TRANSFER(extension,context)
|
||||||
|
Caller was transferred to a different extension. Context and extension
|
||||||
|
are recorded.
|
||||||
|
|
@@ -40,6 +40,9 @@ extern "C" {
|
|||||||
extern void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
|
extern void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
|
||||||
__attribute__ ((format (printf, 5, 6)));
|
__attribute__ ((format (printf, 5, 6)));
|
||||||
|
|
||||||
|
extern void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
|
||||||
|
__attribute__ ((format (printf, 5, 6)));
|
||||||
|
|
||||||
//! Send a verbose message (based on verbose level)
|
//! Send a verbose message (based on verbose level)
|
||||||
/*!
|
/*!
|
||||||
* This works like ast_log, but prints verbose messages to the console depending on verbosity level set.
|
* This works like ast_log, but prints verbose messages to the console depending on verbosity level set.
|
||||||
|
@@ -318,11 +318,11 @@ static inline void free_zone(struct tone_zone* zone)
|
|||||||
struct tone_zone_sound *tmp = zone->tones->next;
|
struct tone_zone_sound *tmp = zone->tones->next;
|
||||||
free((void*)zone->tones->name);
|
free((void*)zone->tones->name);
|
||||||
free((void*)zone->tones->data);
|
free((void*)zone->tones->data);
|
||||||
if (zone->ringcadance)
|
|
||||||
free((void*)zone->ringcadance);
|
|
||||||
free(zone->tones);
|
free(zone->tones);
|
||||||
zone->tones = tmp;
|
zone->tones = tmp;
|
||||||
}
|
}
|
||||||
|
if (zone->ringcadance)
|
||||||
|
free((void*)zone->ringcadance);
|
||||||
free(zone);
|
free(zone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
38
logger.c
38
logger.c
@@ -220,6 +220,43 @@ static void init_logger_chain(void)
|
|||||||
ast_mutex_unlock(&loglock);
|
ast_mutex_unlock(&loglock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FILE *qlog = NULL;
|
||||||
|
static ast_mutex_t qloglock = AST_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
ast_mutex_lock(&qloglock);
|
||||||
|
if (qlog) {
|
||||||
|
va_start(ap, fmt);
|
||||||
|
fprintf(qlog, "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
|
||||||
|
vfprintf(qlog, fmt, ap);
|
||||||
|
fprintf(qlog, "\n");
|
||||||
|
va_end(ap);
|
||||||
|
fflush(qlog);
|
||||||
|
}
|
||||||
|
ast_mutex_unlock(&qloglock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void queue_log_init(void)
|
||||||
|
{
|
||||||
|
char filename[256];
|
||||||
|
int reloaded = 0;
|
||||||
|
ast_mutex_lock(&qloglock);
|
||||||
|
if (qlog) {
|
||||||
|
reloaded = 1;
|
||||||
|
fclose(qlog);
|
||||||
|
qlog = NULL;
|
||||||
|
}
|
||||||
|
snprintf(filename, sizeof(filename), "%s/%s", (char *)ast_config_AST_LOG_DIR, "queue_log");
|
||||||
|
qlog = fopen(filename, "a");
|
||||||
|
ast_mutex_unlock(&qloglock);
|
||||||
|
if (reloaded)
|
||||||
|
ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "");
|
||||||
|
else
|
||||||
|
ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "");
|
||||||
|
}
|
||||||
|
|
||||||
int reload_logger(int rotate)
|
int reload_logger(int rotate)
|
||||||
{
|
{
|
||||||
char old[AST_CONFIG_MAX_PATH];
|
char old[AST_CONFIG_MAX_PATH];
|
||||||
@@ -294,6 +331,7 @@ int reload_logger(int rotate)
|
|||||||
return 0;
|
return 0;
|
||||||
} else
|
} else
|
||||||
ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
|
ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
|
||||||
|
queue_log_init();
|
||||||
init_logger_chain();
|
init_logger_chain();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user