Customizable parameters in CDR builder and CDR file writer.

git-svn-id: http://voip.null.ro/svn/yate@838 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2006-06-05 14:25:44 +00:00
parent 009a08e3eb
commit 0fff94b48b
4 changed files with 193 additions and 101 deletions

View File

@ -0,0 +1,21 @@
[general]
; resolution: keyword: Resolution for time representation: sec, msec or usec
;resolution=msec
[parameters]
; Each line consists of name=bool where name is the name of the parameter being
; monitored while bool allows or disallows subsequent overwrites of the initial
; non-empty value
;billid=true
;reason=true
;address=false
;caller=false
;called=false
;calledfull=false
;username=false
; The following parameters are handled internally and cannot be changed:
; time, chan, operation, direction, status, duration, billtime, ringtime

View File

@ -1,3 +1,17 @@
[general]
; file: string: Name of the file to write the CDR to
;file=
file=/tmp/cdr.tsv
; tabs: bool: Use tab-separated instead of comma-separated if format is missing
;tabs=false
tabs=true
; format: string: Custom format to use, overrides default. Each ${parameter}
; is replaced with the value of that parameter in the call.cdr message
; tab-separated (.tsv)
;format=${time} ${billid} ${chan} ${address} ${caller} ${called} ${billtime} ${ringtime} ${duration} ${direction} ${status} ${reason}
; comma-separated (.csv)
;format=${time},"${billid}","${chan}","${address}","${caller}","${called}",${billtime},${ringtime},${duration},"${direction}","${status}","${reason}"

View File

@ -58,7 +58,7 @@ public:
virtual bool received(Message &msg);
};
class CdrBuilder : public String
class CdrBuilder : public NamedList
{
public:
CdrBuilder(const char *name);
@ -67,7 +67,7 @@ public:
void update(const Message& msg, int type, u_int64_t val);
void emit(const char *operation = 0);
String getStatus() const;
static CdrBuilder *find(String &id);
static CdrBuilder* find(String &id);
private:
u_int64_t
m_start,
@ -76,20 +76,66 @@ private:
m_answer,
m_hangup;
String m_dir;
String m_billid;
String m_address;
String m_caller;
String m_called;
String m_username;
String m_calledfull;
String m_status;
String m_reason;
bool m_first;
};
static ObjList cdrs;
class Param : public String
{
public:
inline Param(const char* name, bool replace)
: String(name), m_overwrite(replace)
{ }
inline bool overwrite() const
{ return m_overwrite; }
inline void overwrite(bool replace)
{ m_overwrite = replace; }
private:
bool m_overwrite;
};
static ObjList s_cdrs;
static Mutex s_mutex;
static ObjList s_params;
static int s_res = 1;
// Time resolutions
static TokenDict const s_timeRes[] = {
{ "sec", 0 },
{ "msec", 1 },
{ "usec", 2 },
{ 0, 0 },
};
// Default but overridable parameters
static struct _params {
const char* name;
bool overwrite;
} const s_defParams[] = {
{ "billid", true },
{ "reason", true },
{ "address", false },
{ "caller", false },
{ "called", false },
{ "calledfull", false },
{ "username", false },
{ 0, false },
};
// Internally built, non-overridable parameters
static const char* const s_forbidden[] = {
"time",
"chan",
"operation",
"direction",
"status",
"duration",
"billtime",
"ringtime",
0
};
static const char* printTime(char* buf,u_int64_t usec)
{
switch (s_res) {
@ -111,7 +157,7 @@ static const char* printTime(char* buf,u_int64_t usec)
}
CdrBuilder::CdrBuilder(const char *name)
: String(name), m_dir("unknown"), m_status("unknown"), m_first(true)
: NamedList(name), m_dir("unknown"), m_status("unknown"), m_first(true)
{
m_start = m_call = m_ringing = m_answer = m_hangup = 0;
}
@ -148,32 +194,37 @@ void CdrBuilder::emit(const char *operation)
char buf[64];
Message *m = new Message("call.cdr");
m->addParam("operation",operation);
m->addParam("time",printTime(buf,t_start));
m->addParam("chan",c_str());
m->addParam("address",m_address);
m->addParam("operation",operation);
m->addParam("direction",m_dir);
m->addParam("billid",m_billid);
m->addParam("caller",m_caller);
m->addParam("called",m_called);
m->addParam("username",m_username);
m->addParam("calledfull",m_calledfull);
m->addParam("duration",printTime(buf,t_hangup - t_start));
m->addParam("billtime",printTime(buf,t_hangup - t_answer));
m->addParam("ringtime",printTime(buf,t_answer - t_ringing));
m->addParam("status",m_status);
m->addParam("reason",m_reason);
if (m_dir == "incoming")
m->addParam("external",m_caller);
else if (m_dir == "outgoing")
m->addParam("external",m_called);
if (!getValue("external")) {
const char* ext = 0;
if (m_dir == "incoming")
ext = getValue("caller");
else if (m_dir == "outgoing")
ext = getValue("called");
if (ext)
m->setParam("external",ext);
}
unsigned int n = length();
for (unsigned int i = 0; i < n; i++) {
const NamedString* s = getParam(i);
if (!s)
continue;
m->addParam(s->name(),*s);
}
Engine::enqueue(m);
}
String CdrBuilder::getStatus() const
{
String s(m_status);
s << "|" << m_caller << "|" << m_called;
s << "|" << getValue("caller") << "|" << getValue("called");
return s;
}
@ -196,72 +247,57 @@ void CdrBuilder::update(int type, u_int64_t val)
break;
case CdrHangup:
m_hangup = val;
cdrs.remove(this);
s_cdrs.remove(this);
return;
}
}
void CdrBuilder::update(const Message& msg, int type, u_int64_t val)
{
const char* p = msg.getValue("billid");
if (p)
m_billid = p;
if (m_address.null()) {
p = msg.getValue("address");
if (p)
m_address = p;
unsigned int n = msg.length();
for (unsigned int i = 0; i < n; i++) {
const NamedString* s = msg.getParam(i);
if (!s)
continue;
if (s->null())
continue;
if (s->name() == "status") {
m_status = *s;
if ((m_status == "incoming") || (m_status == "outgoing"))
m_dir = m_status;
}
else if (s->name() == "direction")
m_dir = *s;
else {
// search the parameter
Lock lock(s_mutex);
Param* p = static_cast<Param*>(s_params[s->name()]);
if (!p)
continue;
bool overwrite = p->overwrite();
lock.drop();
NamedString* str = getParam(s->name());
// parameter is not yet stored - store a copy
if (!str)
addParam(s->name(),*s);
// parameter is stored but we should overwrite it
else if (overwrite)
*str = *s;
}
}
if (m_caller.null()) {
p = msg.getValue("caller");
if (p)
m_caller = p;
}
if (m_called.null()) {
p = msg.getValue("called");
if (p)
m_called = p;
}
if (m_username.null()) {
p = msg.getValue("username");
if (p)
m_username = p;
}
if (m_calledfull.null()) {
p = msg.getValue("calledfull");
if (p)
m_calledfull = p;
}
p = msg.getValue("status");
if (p) {
m_status = p;
if ((m_status == "incoming") || (m_status == "outgoing"))
m_dir = m_status;
}
p = msg.getValue("direction");
if (p)
m_dir = p;
p = msg.getValue("reason");
if (p)
m_reason = p;
update(type,val);
if (type == CdrHangup) {
cdrs.remove(this);
s_cdrs.remove(this);
return;
}
emit();
}
CdrBuilder *CdrBuilder::find(String &id)
CdrBuilder* CdrBuilder::find(String &id)
{
ObjList *l = &cdrs;
for (; l; l=l->next()) {
CdrBuilder *b = static_cast<CdrBuilder *>(l->get());
if (b && (*b == id))
return b;
}
return 0;
return static_cast<CdrBuilder*>(s_cdrs[id]);
}
bool CdrHandler::received(Message &msg)
@ -269,7 +305,7 @@ bool CdrHandler::received(Message &msg)
static Mutex mutex;
Lock lock(mutex);
if (m_type == EngHalt) {
cdrs.clear();
s_cdrs.clear();
return false;
}
if (!msg.getBoolValue("cdrtrack",true))
@ -287,7 +323,7 @@ bool CdrHandler::received(Message &msg)
CdrBuilder *b = CdrBuilder::find(id);
if (!b && ((m_type == CdrStart) || (m_type == CdrCall))) {
b = new CdrBuilder(id);
cdrs.append(b);
s_cdrs.append(b);
}
if (b)
b->update(msg,m_type,msg.msgTime().usec());
@ -310,8 +346,8 @@ bool StatusHandler::received(Message &msg)
if (sel && ::strcmp(sel,"cdrbuild"))
return false;
String st("name=cdrbuild,type=cdr,format=Status|Caller|Called");
st << ";cdrs=" << cdrs.count() << ";";
ObjList *l = &cdrs;
st << ";cdrs=" << s_cdrs.count() << ";";
ObjList *l = &s_cdrs;
bool first = true;
for (; l; l=l->next()) {
CdrBuilder *b = static_cast<CdrBuilder *>(l->get());
@ -346,6 +382,36 @@ CdrBuildPlugin::CdrBuildPlugin()
void CdrBuildPlugin::initialize()
{
Output("Initializing module CdrBuild");
Configuration cfg(Engine::configFile("cdrbuild"));
s_res = cfg.getIntValue("general","resolution",s_timeRes,1);
s_mutex.lock();
s_params.clear();
const struct _params* params = s_defParams;
for (; params->name; params++)
s_params.append(new Param(params->name,params->overwrite));
const NamedList* sect = cfg.getSection("parameters");
if (sect) {
unsigned int n = sect->length();
for (unsigned int i = 0; i < n; i++) {
const NamedString* p = sect->getParam(i);
if (!p)
continue;
const char* const* f = s_forbidden;
for (; *f; f++)
if (p->name() == *f)
break;
if (*f) {
Debug("cdrbuild",DebugWarn,"Cannot override parameter '%s'",p->name().c_str());
continue;
}
Param* par = static_cast<Param*>(s_params[p->name()]);
if (par)
par->overwrite(p->toBoolean(par->overwrite()));
else
s_params.append(new Param(p->name(),p->toBoolean(false)));
}
}
s_mutex.unlock();
if (m_first) {
m_first = false;
Engine::install(new CdrHandler("chan.startup",CdrStart));

View File

@ -33,13 +33,13 @@ class CdrFileHandler : public MessageHandler
{
public:
CdrFileHandler(const char *name)
: MessageHandler(name), m_tabs(0), m_file(0) { }
: MessageHandler(name), m_file(0) { }
virtual ~CdrFileHandler();
virtual bool received(Message &msg);
void init(const char *fname, bool tabsep);
void init(const char *fname, bool tabsep, const char* format);
private:
bool m_tabs;
FILE *m_file;
String m_format;
Mutex m_lock;
};
@ -52,12 +52,16 @@ CdrFileHandler::~CdrFileHandler()
}
}
void CdrFileHandler::init(const char *fname, bool tabsep)
void CdrFileHandler::init(const char *fname, bool tabsep, const char* format)
{
Lock lock(m_lock);
if (m_file)
::fclose(m_file);
m_tabs = tabsep;
m_format = format;
if (m_format.null())
m_format = tabsep
? "${time}\t${billid}\t${chan}\t${address}\t${caller}\t${called}\t${billtime}\t${ringtime}\t${duration}\t${direction}\t${status}\t${reason}"
: "${time},\"${billid}\",\"${chan}\",\"${address}\",\"${caller}\",\"${called}\",${billtime},${ringtime},${duration},\"${direction}\",\"${status}\",\"${reason}\"";
m_file = fname ? ::fopen(fname,"a") : 0;
}
@ -68,24 +72,11 @@ bool CdrFileHandler::received(Message &msg)
return false;
Lock lock(m_lock);
if (m_file) {
const char *format = m_tabs
? "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
: "%s,\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",%s,%s,%s,\"%s\",\"%s\",\"%s\"\n";
::fprintf(m_file,format,
c_safe(msg.getValue("time")),
c_safe(msg.getValue("billid")),
c_safe(msg.getValue("chan")),
c_safe(msg.getValue("address")),
c_safe(msg.getValue("caller")),
c_safe(msg.getValue("called")),
c_safe(msg.getValue("billtime")),
c_safe(msg.getValue("ringtime")),
c_safe(msg.getValue("duration")),
c_safe(msg.getValue("direction")),
c_safe(msg.getValue("status")),
c_safe(msg.getValue("reason"))
);
if (m_file && m_format) {
String str = m_format;
str += "\n";
msg.replaceParams(str);
::fputs(str.safe(),m_file);
::fflush(m_file);
}
return false;
@ -116,7 +107,7 @@ void CdrFilePlugin::initialize()
Engine::install(m_handler);
}
if (m_handler)
m_handler->init(file,cfg.getBoolValue("general","tabs"));
m_handler->init(file,cfg.getBoolValue("general","tabs"),cfg.getValue("general","format"));
}
INIT_PLUGIN(CdrFilePlugin);