Add High Resolution Times to CDRs for Asterisk

People expressed an interest in having access to the exact length of calls to a finer degree than seconds. See the CHANGES and UPGRADE.txt for usage also updated the sample configs to note the change.

Patch by snuffy.

(closes issue #16559)
Reported by: cianmaher
Tested by: cianmaher, snuffy

Review: https://reviewboard.asterisk.org/r/461/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@269153 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Bradley Latus
2010-06-08 23:48:17 +00:00
parent 3c26b511e0
commit 4405813297
14 changed files with 258 additions and 12 deletions

View File

@@ -223,6 +223,8 @@ Dialplan Functions
Returns "0" if there is a B channel associated with the call. Returns "0" if there is a B channel associated with the call.
Returns "1" if no B channel is associated with the call. The call is either Returns "1" if no B channel is associated with the call. The call is either
on hold or is a call waiting call. on hold or is a call waiting call.
* Added option to dialplan function CDR(), the 'f' option
allows for high resolution times for billsec and duration fields.
Dialplan Variables Dialplan Variables
------------------ ------------------
@@ -414,6 +416,9 @@ CDR
See configs/cdr_syslog.conf.sample for more information. See configs/cdr_syslog.conf.sample for more information.
* A 'sequence' field has been added to CDRs which can be combined with * A 'sequence' field has been added to CDRs which can be combined with
linkedid or uniqueid to uniquely identify a CDR. linkedid or uniqueid to uniquely identify a CDR.
* Handling of billsec and duration field has changed. If your table definition
specifies those fields as float,double or similar they will now be logged with
microsecond accuracy instead of a whole integer.
Calendaring for Asterisk Calendaring for Asterisk
------------------------ ------------------------

View File

@@ -88,6 +88,10 @@ From 1.6.2 to 1.8:
* When a call is redirected inside of a Dial, the app and appdata fields of the * When a call is redirected inside of a Dial, the app and appdata fields of the
CDR will now be set to "AppDial" and "(Outgoing Line)" instead of being blank. CDR will now be set to "AppDial" and "(Outgoing Line)" instead of being blank.
* The CDR handling of billsec and duration field has changed. If your table
definition specifies those fields as float,double or similar they will now
be logged with microsecond accuracy instead of a whole integer.
From 1.6.1 to 1.6.2: From 1.6.1 to 1.6.2:
* SIP no longer sends the 183 progress message for early media by * SIP no longer sends the 183 progress message for early media by

View File

@@ -288,6 +288,40 @@ db_reconnect:
ast_str_append(&sql2, 0, ","); ast_str_append(&sql2, 0, ",");
} }
if (!strcasecmp(cdrname, "billsec") &&
(strstr(entry->type, "float") ||
strstr(entry->type, "double") ||
strstr(entry->type, "decimal") ||
strstr(entry->type, "numeric") ||
strstr(entry->type, "real"))) {
if (!ast_tvzero(cdr->answer)) {
snprintf(workspace, sizeof(workspace), "%lf",
(double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
} else {
ast_copy_string(workspace, "0", sizeof(workspace));
}
if (!ast_strlen_zero(workspace)) {
value = workspace;
}
}
if (!strcasecmp(cdrname, "duration") &&
(strstr(entry->type, "float") ||
strstr(entry->type, "double") ||
strstr(entry->type, "decimal") ||
strstr(entry->type, "numeric") ||
strstr(entry->type, "real"))) {
snprintf(workspace, sizeof(workspace), "%lf",
(double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
if (!ast_strlen_zero(workspace)) {
value = workspace;
}
}
ast_str_make_space(&escape, (valsz = strlen(value)) * 2 + 1); ast_str_make_space(&escape, (valsz = strlen(value)) * 2 + 1);
mysql_real_escape_string(&mysql, ast_str_buffer(escape), value, valsz); mysql_real_escape_string(&mysql, ast_str_buffer(escape), value, valsz);

View File

@@ -611,6 +611,23 @@ static int odbc_log(struct ast_cdr *cdr)
continue; continue;
} else { } else {
double number = 0.0; double number = 0.0;
if (!strcasecmp(entry->cdrname, "billsec")) {
if (!ast_tvzero(cdr->answer)) {
snprintf(colbuf, sizeof(colbuf), "%lf",
(double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
} else {
ast_copy_string(colbuf, "0", sizeof(colbuf));
}
} else if (!strcasecmp(entry->cdrname, "duration")) {
snprintf(colbuf, sizeof(colbuf), "%lf",
(double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
if (!ast_strlen_zero(colbuf)) {
colptr = colbuf;
}
}
if (sscanf(colptr, "%30lf", &number) != 1) { if (sscanf(colptr, "%30lf", &number) != 1) {
ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name); ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
continue; continue;
@@ -628,6 +645,23 @@ static int odbc_log(struct ast_cdr *cdr)
continue; continue;
} else { } else {
double number = 0.0; double number = 0.0;
if (!strcasecmp(entry->cdrname, "billsec")) {
if (!ast_tvzero(cdr->answer)) {
snprintf(colbuf, sizeof(colbuf), "%lf",
(double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
} else {
ast_copy_string(colbuf, "0", sizeof(colbuf));
}
} else if (!strcasecmp(entry->cdrname, "duration")) {
snprintf(colbuf, sizeof(colbuf), "%lf",
(double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
if (!ast_strlen_zero(colbuf)) {
colptr = colbuf;
}
}
if (sscanf(colptr, "%30lf", &number) != 1) { if (sscanf(colptr, "%30lf", &number) != 1) {
ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name); ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
continue; continue;

View File

@@ -53,6 +53,7 @@ enum {
CONFIG_LOGUNIQUEID = 1 << 0, CONFIG_LOGUNIQUEID = 1 << 0,
CONFIG_USEGMTIME = 1 << 1, CONFIG_USEGMTIME = 1 << 1,
CONFIG_DISPOSITIONSTRING = 1 << 2, CONFIG_DISPOSITIONSTRING = 1 << 2,
CONFIG_HRTIME = 1 << 3,
}; };
static struct ast_flags config = { 0 }; static struct ast_flags config = { 0 };
@@ -96,8 +97,23 @@ static SQLHSTMT execute_cb(struct odbc_obj *obj, void *data)
SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dstchannel), 0, cdr->dstchannel, 0, NULL); SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dstchannel), 0, cdr->dstchannel, 0, NULL);
SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastapp), 0, cdr->lastapp, 0, NULL); SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastapp), 0, cdr->lastapp, 0, NULL);
SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastdata), 0, cdr->lastdata, 0, NULL); SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastdata), 0, cdr->lastdata, 0, NULL);
SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->duration, 0, NULL);
SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL); if (ast_test_flag(&config, CONFIG_HRTIME)) {
double hrbillsec = 0.0;
double hrduration;
if (!ast_tvzero(cdr->answer)) {
hrbillsec = (double) ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0;
}
hrduration = (double) ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0;
SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_FLOAT, 0, 0, &hrbillsec, 0, NULL);
SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_FLOAT, 0, 0, &hrduration, 0, NULL);
} else {
SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->duration, 0, NULL);
SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL);
}
if (ast_test_flag(&config, CONFIG_DISPOSITIONSTRING)) if (ast_test_flag(&config, CONFIG_DISPOSITIONSTRING))
SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ast_cdr_disp2str(cdr->disposition)) + 1, 0, ast_cdr_disp2str(cdr->disposition), 0, NULL); SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ast_cdr_disp2str(cdr->disposition)) + 1, 0, ast_cdr_disp2str(cdr->disposition), 0, NULL);
else else
@@ -203,6 +219,14 @@ static int odbc_load_module(int reload)
ast_debug(1, "cdr_odbc: Logging in local time\n"); ast_debug(1, "cdr_odbc: Logging in local time\n");
} }
if (((tmp = ast_variable_retrieve(cfg, "global", "hrtime"))) && ast_true(tmp)) {
ast_set_flag(&config, CONFIG_HRTIME);
ast_debug(1, "cdr_odbc: Logging billsec and duration fields as floats\n");
} else {
ast_clear_flag(&config, CONFIG_HRTIME);
ast_debug(1, "cdr_odbc: Logging billsec and duration fields as integers\n");
}
if ((tmp = ast_variable_retrieve(cfg, "global", "table")) == NULL) { if ((tmp = ast_variable_retrieve(cfg, "global", "table")) == NULL) {
ast_log(LOG_WARNING, "cdr_odbc: table not specified. Assuming cdr\n"); ast_log(LOG_WARNING, "cdr_odbc: table not specified. Assuming cdr\n");
tmp = "cdr"; tmp = "cdr";

View File

@@ -211,12 +211,12 @@ static int pgsql_log(struct ast_cdr *cdr)
} else if (strncmp(cur->type, "float", 5) == 0) { } else if (strncmp(cur->type, "float", 5) == 0) {
struct timeval *when = cur->name[0] == 'd' ? &cdr->start : &cdr->answer; struct timeval *when = cur->name[0] == 'd' ? &cdr->start : &cdr->answer;
LENGTHEN_BUF2(31); LENGTHEN_BUF2(31);
ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec - when->tv_sec + cdr->end.tv_usec / 1000000.0 - when->tv_usec / 1000000.0); ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
} else { } else {
/* Char field, probably */ /* Char field, probably */
struct timeval *when = cur->name[0] == 'd' ? &cdr->start : &cdr->answer; struct timeval *when = cur->name[0] == 'd' ? &cdr->start : &cdr->answer;
LENGTHEN_BUF2(31); LENGTHEN_BUF2(31);
ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double)cdr->end.tv_sec - when->tv_sec + cdr->end.tv_usec / 1000000.0 - when->tv_usec / 1000000.0); ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
} }
} else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) { } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
if (strncmp(cur->type, "int", 3) == 0) { if (strncmp(cur->type, "int", 3) == 0) {

View File

@@ -49,8 +49,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/utils.h" #include "asterisk/utils.h"
#include "asterisk/paths.h" #include "asterisk/paths.h"
#define LOG_UNIQUEID 0 #define LOG_UNIQUEID 0
#define LOG_USERFIELD 0 #define LOG_USERFIELD 0
#define LOG_HRTIME 0
/* When you change the DATE_FORMAT, be sure to change the CHAR(19) below to something else */ /* When you change the DATE_FORMAT, be sure to change the CHAR(19) below to something else */
#define DATE_FORMAT "%Y-%m-%d %T" #define DATE_FORMAT "%Y-%m-%d %T"
@@ -74,8 +75,13 @@ static const char sql_create_table[] = "CREATE TABLE cdr ("
" start CHAR(19)," " start CHAR(19),"
" answer CHAR(19)," " answer CHAR(19),"
" end CHAR(19)," " end CHAR(19),"
#if LOG_HRTIME
" duration FLOAT,"
" billsec FLOAT,"
#else
" duration INTEGER," " duration INTEGER,"
" billsec INTEGER," " billsec INTEGER,"
#endif
" disposition INTEGER," " disposition INTEGER,"
" amaflags INTEGER," " amaflags INTEGER,"
" accountcode VARCHAR(20)" " accountcode VARCHAR(20)"
@@ -101,6 +107,10 @@ static int sqlite_log(struct ast_cdr *cdr)
char *zErr = 0; char *zErr = 0;
char startstr[80], answerstr[80], endstr[80]; char startstr[80], answerstr[80], endstr[80];
int count; int count;
#if LOG_HRTIME
double hrbillsec = 0.0;
double hrduration;
#endif
ast_mutex_lock(&sqlite_lock); ast_mutex_lock(&sqlite_lock);
@@ -108,6 +118,13 @@ static int sqlite_log(struct ast_cdr *cdr)
format_date(answerstr, sizeof(answerstr), &cdr->answer); format_date(answerstr, sizeof(answerstr), &cdr->answer);
format_date(endstr, sizeof(endstr), &cdr->end); format_date(endstr, sizeof(endstr), &cdr->end);
#if LOG_HRTIME
if (!ast_tvzero(cdr->answer)) {
hrbillsec = (double) ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0;
}
hrduration = (double) ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0;
#endif
for(count=0; count<5; count++) { for(count=0; count<5; count++) {
res = sqlite_exec_printf(db, res = sqlite_exec_printf(db,
"INSERT INTO cdr (" "INSERT INTO cdr ("
@@ -126,7 +143,11 @@ static int sqlite_log(struct ast_cdr *cdr)
"'%q', '%q', '%q', '%q', " "'%q', '%q', '%q', '%q', "
"'%q', '%q', '%q', '%q', " "'%q', '%q', '%q', '%q', "
"'%q', '%q', '%q', " "'%q', '%q', '%q', "
#if LOG_HRTIME
"%f, %f, %d, %d, "
#else
"%d, %d, %d, %d, " "%d, %d, %d, %d, "
#endif
"'%q'" "'%q'"
# if LOG_UNIQUEID # if LOG_UNIQUEID
",'%q'" ",'%q'"
@@ -138,7 +159,11 @@ static int sqlite_log(struct ast_cdr *cdr)
cdr->clid, cdr->src, cdr->dst, cdr->dcontext, cdr->clid, cdr->src, cdr->dst, cdr->dcontext,
cdr->channel, cdr->dstchannel, cdr->lastapp, cdr->lastdata, cdr->channel, cdr->dstchannel, cdr->lastapp, cdr->lastdata,
startstr, answerstr, endstr, startstr, answerstr, endstr,
#if LOG_HRTIME
hrduration, hrbillsec, cdr->disposition, cdr->amaflags,
#else
cdr->duration, cdr->billsec, cdr->disposition, cdr->amaflags, cdr->duration, cdr->billsec, cdr->disposition, cdr->amaflags,
#endif
cdr->accountcode cdr->accountcode
# if LOG_UNIQUEID # if LOG_UNIQUEID
,cdr->uniqueid ,cdr->uniqueid

View File

@@ -87,6 +87,7 @@ struct cdr_tds_config {
AST_STRING_FIELD(table); AST_STRING_FIELD(table);
AST_STRING_FIELD(charset); AST_STRING_FIELD(charset);
AST_STRING_FIELD(language); AST_STRING_FIELD(language);
AST_STRING_FIELD(hrtime);
); );
DBPROCESS *dbproc; DBPROCESS *dbproc;
unsigned int connected:1; unsigned int connected:1;
@@ -149,7 +150,36 @@ retry:
} }
if (settings->has_userfield) { if (settings->has_userfield) {
erc = dbfcmd(settings->dbproc, if (settings->hrtime) {
double hrbillsec = 0.0;
double hrduration;
if (!ast_tvzero(cdr->answer)) {
hrbillsec = (double)(ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0);
}
hrduration = (double)(ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0);
erc = dbfcmd(settings->dbproc,
"INSERT INTO %s "
"("
"accountcode, src, dst, dcontext, clid, channel, "
"dstchannel, lastapp, lastdata, start, answer, [end], duration, "
"billsec, disposition, amaflags, uniqueid, userfield"
") "
"VALUES "
"("
"'%s', '%s', '%s', '%s', '%s', '%s', "
"'%s', '%s', '%s', %s, %s, %s, %lf, "
"%lf, '%s', '%s', '%s', '%s'"
")",
settings->table,
accountcode, src, dst, dcontext, clid, channel,
dstchannel, lastapp, lastdata, start, answer, end, hrduration,
hrbillsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid,
userfield
);
} else {
erc = dbfcmd(settings->dbproc,
"INSERT INTO %s " "INSERT INTO %s "
"(" "("
"accountcode, src, dst, dcontext, clid, channel, " "accountcode, src, dst, dcontext, clid, channel, "
@@ -168,8 +198,37 @@ retry:
cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid, cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid,
userfield userfield
); );
}
} else { } else {
erc = dbfcmd(settings->dbproc, if (settings->hrtime) {
double hrbillsec = 0.0;
double hrduration;
if (!ast_tvzero(cdr->answer)) {
hrbillsec = (double)(ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0);
}
hrduration = (double)(ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0);
erc = dbfcmd(settings->dbproc,
"INSERT INTO %s "
"("
"accountcode, src, dst, dcontext, clid, channel, "
"dstchannel, lastapp, lastdata, start, answer, [end], duration, "
"billsec, disposition, amaflags, uniqueid"
") "
"VALUES "
"("
"'%s', '%s', '%s', '%s', '%s', '%s', "
"'%s', '%s', '%s', %s, %s, %s, %lf, "
"%lf, '%s', '%s', '%s'"
")",
settings->table,
accountcode, src, dst, dcontext, clid, channel,
dstchannel, lastapp, lastdata, start, answer, end, hrduration,
hrbillsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid
);
} else {
erc = dbfcmd(settings->dbproc,
"INSERT INTO %s " "INSERT INTO %s "
"(" "("
"accountcode, src, dst, dcontext, clid, channel, " "accountcode, src, dst, dcontext, clid, channel, "
@@ -187,6 +246,7 @@ retry:
dstchannel, lastapp, lastdata, start, answer, end, cdr->duration, dstchannel, lastapp, lastdata, start, answer, end, cdr->duration,
cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid
); );
}
} }
if (erc == FAIL) { if (erc == FAIL) {
@@ -502,6 +562,13 @@ static int tds_load_module(int reload)
ast_string_field_set(settings, table, "cdr"); ast_string_field_set(settings, table, "cdr");
} }
ptr = ast_variable_retrieve(cfg, "global", "hrtime");
if (ptr && ast_true(ptr)) {
ast_string_field_set(settings, hrtime, ptr);
} else {
ast_log(LOG_NOTICE, "High Resolution Time not found, using integers for billsec and duration fields by default.\n");
}
mssql_disconnect(); mssql_disconnect();
if (mssql_connect()) { if (mssql_connect()) {

View File

@@ -8,5 +8,7 @@
; ;
;[mappings] ;[mappings]
;Master.csv => ${CSV_QUOTE(${CDR(clid)})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})},${CSV_QUOTE(${CDR(dcontext)})},${CSV_QUOTE(${CDR(channel)})},${CSV_QUOTE(${CDR(dstchannel)})},${CSV_QUOTE(${CDR(lastapp)})},${CSV_QUOTE(${CDR(lastdata)})},${CSV_QUOTE(${CDR(start)})},${CSV_QUOTE(${CDR(answer)})},${CSV_QUOTE(${CDR(end)})},${CSV_QUOTE(${CDR(duration)})},${CSV_QUOTE(${CDR(billsec)})},${CSV_QUOTE(${CDR(disposition)})},${CSV_QUOTE(${CDR(amaflags)})},${CSV_QUOTE(${CDR(accountcode)})},${CSV_QUOTE(${CDR(uniqueid)})},${CSV_QUOTE(${CDR(userfield)})},${CDR(sequence)} ;Master.csv => ${CSV_QUOTE(${CDR(clid)})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})},${CSV_QUOTE(${CDR(dcontext)})},${CSV_QUOTE(${CDR(channel)})},${CSV_QUOTE(${CDR(dstchannel)})},${CSV_QUOTE(${CDR(lastapp)})},${CSV_QUOTE(${CDR(lastdata)})},${CSV_QUOTE(${CDR(start)})},${CSV_QUOTE(${CDR(answer)})},${CSV_QUOTE(${CDR(end)})},${CSV_QUOTE(${CDR(duration)})},${CSV_QUOTE(${CDR(billsec)})},${CSV_QUOTE(${CDR(disposition)})},${CSV_QUOTE(${CDR(amaflags)})},${CSV_QUOTE(${CDR(accountcode)})},${CSV_QUOTE(${CDR(uniqueid)})},${CSV_QUOTE(${CDR(userfield)})},${CDR(sequence)}
;
; High Resolution Time for billsec and duration fields
;Master.csv => ${CSV_QUOTE(${CDR(clid)})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})},${CSV_QUOTE(${CDR(dcontext)})},${CSV_QUOTE(${CDR(channel)})},${CSV_QUOTE(${CDR(dstchannel)})},${CSV_QUOTE(${CDR(lastapp)})},${CSV_QUOTE(${CDR(lastdata)})},${CSV_QUOTE(${CDR(start)})},${CSV_QUOTE(${CDR(answer)})},${CSV_QUOTE(${CDR(end)})},${CSV_QUOTE(${CDR(duration,f)})},${CSV_QUOTE(${CDR(billsec,f)})},${CSV_QUOTE(${CDR(disposition)})},${CSV_QUOTE(${CDR(amaflags)})},${CSV_QUOTE(${CDR(accountcode)})},${CSV_QUOTE(${CDR(uniqueid)})},${CSV_QUOTE(${CDR(userfield)})},${CDR(sequence)}
;Simple.csv => ${CSV_QUOTE(${EPOCH})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})} ;Simple.csv => ${CSV_QUOTE(${EPOCH})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})}

View File

@@ -8,3 +8,4 @@
;dispositionstring=yes ;dispositionstring=yes
;table=cdr ;"cdr" is default table name ;table=cdr ;"cdr" is default table name
;usegmtime=no ; set to "yes" to log in GMT ;usegmtime=no ; set to "yes" to log in GMT
;hrtime=yes ;Enables microsecond accuracy with the billsec and duration fields

View File

@@ -5,3 +5,6 @@
table => cdr table => cdr
columns => calldate, clid, dcontext, channel, dstchannel, lastapp, lastdata, duration, billsec, disposition, amaflags, accountcode, uniqueid, userfield, test columns => calldate, clid, dcontext, channel, dstchannel, lastapp, lastdata, duration, billsec, disposition, amaflags, accountcode, uniqueid, userfield, test
values => '${CDR(start)}','${CDR(clid)}','${CDR(dcontext)}','${CDR(channel)}','${CDR(dstchannel)}','${CDR(lastapp)}','${CDR(lastdata)}','${CDR(duration)}','${CDR(billsec)}','${CDR(disposition)}','${CDR(amaflags)}','${CDR(accountcode)}','${CDR(uniqueid)}','${CDR(userfield)}','${CDR(test)}' values => '${CDR(start)}','${CDR(clid)}','${CDR(dcontext)}','${CDR(channel)}','${CDR(dstchannel)}','${CDR(lastapp)}','${CDR(lastdata)}','${CDR(duration)}','${CDR(billsec)}','${CDR(disposition)}','${CDR(amaflags)}','${CDR(accountcode)}','${CDR(uniqueid)}','${CDR(userfield)}','${CDR(test)}'
;Enable High Resolution Times for billsec and duration fields
;values => '${CDR(start)}','${CDR(clid)}','${CDR(dcontext)}','${CDR(channel)}','${CDR(dstchannel)}','${CDR(lastapp)}','${CDR(lastdata)}','${CDR(duration,f)}','${CDR(billsec,f)}','${CDR(disposition)}','${CDR(amaflags)}','${CDR(accountcode)}','${CDR(uniqueid)}','${CDR(userfield)}','${CDR(test)}'

View File

@@ -73,6 +73,8 @@
;template = "${CDR(clid)}","${CDR(src)}","${CDR(dst)}","${CDR(dcontext)}","${CDR(channel)}","${CDR(dstchannel)}","${CDR(lastapp)}","${CDR(lastdata)}","${CDR(start)}","${CDR(answer)}","${CDR(end)}","${CDR(duration)}","${CDR(billsec)}","${CDR(disposition)}","${CDR(amaflags)}","${CDR(accountcode)}","${CDR(uniqueid)}","${CDR(userfield)}" ;template = "${CDR(clid)}","${CDR(src)}","${CDR(dst)}","${CDR(dcontext)}","${CDR(channel)}","${CDR(dstchannel)}","${CDR(lastapp)}","${CDR(lastdata)}","${CDR(start)}","${CDR(answer)}","${CDR(end)}","${CDR(duration)}","${CDR(billsec)}","${CDR(disposition)}","${CDR(amaflags)}","${CDR(accountcode)}","${CDR(uniqueid)}","${CDR(userfield)}"
; High Resolution Time for billsec and duration fields
;template = "${CDR(clid)}","${CDR(src)}","${CDR(dst)}","${CDR(dcontext)}","${CDR(channel)}","${CDR(dstchannel)}","${CDR(lastapp)}","${CDR(lastdata)}","${CDR(start)}","${CDR(answer)}","${CDR(end)}","${CDR(duration,f)}","${CDR(billsec,f)}","${CDR(disposition)}","${CDR(amaflags)}","${CDR(accountcode)}","${CDR(uniqueid)}","${CDR(userfield)}"
;[cdr-simple] ;[cdr-simple]
; Since we don't specify a facility or priority for this logging location, the ; Since we don't specify a facility or priority for this logging location, the

View File

@@ -65,3 +65,14 @@
; Default value: iso_1 ; Default value: iso_1
;charset=BIG5 ;charset=BIG5
; High Resolution Times
;
; The 'hrtime' setting is used to store high resolution (sub second) times for
; billsec and duration fields.
;
; Accepted value: true or false
; Default value: false
;hrtime=false

View File

@@ -106,6 +106,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</parameter> </parameter>
<parameter name="options" required="false"> <parameter name="options" required="false">
<optionlist> <optionlist>
<option name="f">
<para>Returns billsec or duration fields as floating point values.</para>
</option>
<option name="l"> <option name="l">
<para>Uses the most recent CDR on a channel with multiple records</para> <para>Uses the most recent CDR on a channel with multiple records</para>
</option> </option>
@@ -174,9 +177,11 @@ enum cdr_option_flags {
OPT_UNPARSED = (1 << 1), OPT_UNPARSED = (1 << 1),
OPT_LAST = (1 << 2), OPT_LAST = (1 << 2),
OPT_SKIPLOCKED = (1 << 3), OPT_SKIPLOCKED = (1 << 3),
OPT_FLOAT = (1 << 4),
}; };
AST_APP_OPTIONS(cdr_func_options, { AST_APP_OPTIONS(cdr_func_options, {
AST_APP_OPTION('f', OPT_FLOAT),
AST_APP_OPTION('l', OPT_LAST), AST_APP_OPTION('l', OPT_LAST),
AST_APP_OPTION('r', OPT_RECURSIVE), AST_APP_OPTION('r', OPT_RECURSIVE),
AST_APP_OPTION('s', OPT_SKIPLOCKED), AST_APP_OPTION('s', OPT_SKIPLOCKED),
@@ -213,9 +218,38 @@ static int cdr_read(struct ast_channel *chan, const char *cmd, char *parse,
while (ast_test_flag(cdr, AST_CDR_FLAG_LOCKED) && cdr->next) while (ast_test_flag(cdr, AST_CDR_FLAG_LOCKED) && cdr->next)
cdr = cdr->next; cdr = cdr->next;
ast_cdr_getvar(cdr, args.variable, &ret, buf, len, if (!strcasecmp("billsec", args.variable) && ast_test_flag(&flags, OPT_FLOAT)) {
ast_test_flag(&flags, OPT_RECURSIVE), if (!ast_tvzero(cdr->answer)) {
ast_test_flag(&flags, OPT_UNPARSED)); double hrtime;
if(!ast_tvzero(cdr->end))
hrtime = (double)(ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0);
else
hrtime = (double)(ast_tvdiff_us(ast_tvnow(), cdr->answer) / 1000000.0);
snprintf(buf, len, "%lf", hrtime);
} else {
snprintf(buf, len, "%lf", 0.0);
}
ret = buf;
} else if (!strcasecmp("duration", args.variable) && ast_test_flag(&flags, OPT_FLOAT)) {
double hrtime;
if(!ast_tvzero(cdr->end))
hrtime = (double)(ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0);
else
hrtime = (double)(ast_tvdiff_us(ast_tvnow(), cdr->start) / 1000000.0);
snprintf(buf, len, "%lf", hrtime);
if (!ast_strlen_zero(buf)) {
ret = buf;
}
} else {
ast_cdr_getvar(cdr, args.variable, &ret, buf, len,
ast_test_flag(&flags, OPT_RECURSIVE),
ast_test_flag(&flags, OPT_UNPARSED));
}
return ret ? 0 : -1; return ret ? 0 : -1;
} }