Module: sems
Branch: master
Commit: 33161cc3efbce5241d8268500832c791e9fd98c3
URL:    
http://git.sip-router.org/cgi-bin/gitweb.cgi/sems/?a=commit;h=33161cc3efbce5241d8268500832c791e9fd98c3

Author: Stefan Sayer <[email protected]>
Committer: Stefan Sayer <[email protected]>
Date:   Fri Oct  7 20:29:26 2011 +0200

sbc:cc_syslog_cdr: saner timestamps, connect durations, quoting

---

 apps/sbc/call_control/syslog_cdr/SyslogCDR.cpp     |  120 ++++++++++++++++----
 apps/sbc/call_control/syslog_cdr/SyslogCDR.h       |    2 +
 .../call_control/syslog_cdr/etc/cc_syslog_cdr.conf |   11 ++
 doc/Readme.syslog_cdr.txt                          |   16 ---
 4 files changed, 113 insertions(+), 36 deletions(-)

diff --git a/apps/sbc/call_control/syslog_cdr/SyslogCDR.cpp 
b/apps/sbc/call_control/syslog_cdr/SyslogCDR.cpp
index f1e5124..273083c 100644
--- a/apps/sbc/call_control/syslog_cdr/SyslogCDR.cpp
+++ b/apps/sbc/call_control/syslog_cdr/SyslogCDR.cpp
@@ -66,7 +66,7 @@ SyslogCDR* SyslogCDR::instance()
 }
 
 SyslogCDR::SyslogCDR()
-  : level(2), syslog_prefix("CDR: ")
+  : level(2), syslog_prefix("CDR: "), quoting_enabled(true)
 {
 }
 
@@ -92,6 +92,9 @@ int SyslogCDR::onLoad() {
     cdr_format = explode(cfg.getParameter("cdr_format"), ",");
   }
 
+  quoting_enabled = cfg.hasParameter("quoting_enabled") ?
+    cfg.getParameter("quoting_enabled") == "yes" : quoting_enabled;
+
   if (level > 4) {
     WARN("log level > 4 not supported\n");
     level = 4;
@@ -155,13 +158,65 @@ void SyslogCDR::start(const string& ltag, SBCCallProfile* 
call_profile,
   call_profile->cc_vars["cdr::v"] = values;
 }
 
+string getTimeDiffString(int from_ts_sec, int from_ts_usec,
+                        int to_ts_sec, int to_ts_usec,
+                        bool ms_precision) {
+  string res;
+
+  struct timeval start;
+  start.tv_sec = from_ts_sec;
+  start.tv_usec = from_ts_usec;
+  struct timeval diff;
+  diff.tv_sec = to_ts_sec;
+  diff.tv_usec = to_ts_usec;
+  if (!from_ts_sec || !to_ts_sec || timercmp(&start, &diff, >)) {
+    diff.tv_sec = diff.tv_usec = 0;
+  } else {
+    timersub(&diff,&start,&diff);
+  }
+
+  if (ms_precision) {
+    diff.tv_usec /= 1000;
+    string msecs = int2str((unsigned int)diff.tv_usec);
+    if (msecs.length()==1)
+      msecs = "00"+msecs;
+    else if (msecs.length()==2)
+      msecs = "0"+msecs;
+
+    res+=int2str((unsigned int)diff.tv_sec)+"."+ msecs;
+      
+  } else {
+    if (diff.tv_usec>=500000)
+      diff.tv_sec++;
+    res += int2str((unsigned int)diff.tv_sec);
+  }
+  return res;
+}
+
+string do_quote(string s) {
+  string res = "\"";
+  for (string::iterator it = s.begin();it!=s.end();it++) {
+    if (*it == '"') {
+      res +="\"\"";
+    } else {
+      res += *it;
+    }
+  }
+  res += "\"";
+  return res;
+}
+
+// inlining (?)
+#define csv_quote(_str) (quoting_enabled?do_quote(_str) : _str)
+
 void SyslogCDR::end(const string& ltag, SBCCallProfile* call_profile,
                    int start_ts_sec, int start_ts_usec,
                    int connect_ts_sec, int connect_ts_usec,
                    int end_ts_sec, int end_ts_usec) {
   if (!call_profile) return;
 
-  static const int log2syslog_level[] = { LOG_ERR, LOG_WARNING, LOG_INFO, 
LOG_DEBUG, LOG_NOTICE };
+  static const int log2syslog_level[] = { LOG_ERR, LOG_WARNING, LOG_INFO,
+                                         LOG_DEBUG, LOG_NOTICE };
 
   struct timeval start;
   start.tv_sec = connect_ts_sec;
@@ -188,49 +243,74 @@ void SyslogCDR::end(const string& ltag, SBCCallProfile* 
call_profile,
     for (vector<string>::iterator it=cdr_format.begin(); it != 
cdr_format.end(); it++) {
       if (it->size() && (*it)[0]=='$') {
        if (*it == "$ltag") {
-         cdr+=ltag+",";
+         cdr+=csv_quote(ltag) +",";
        } else if (*it == "$start_ts") {
-         cdr+=int2str(start_ts_sec)+"."+int2str(start_ts_usec)+",";
+         cdr+=csv_quote(int2str(start_ts_sec)+"."+int2str(start_ts_usec)) +",";
        } else if (*it == "$connect_ts") {
-         cdr+=int2str(connect_ts_sec)+"."+int2str(connect_ts_usec)+",";
+         cdr+=csv_quote(int2str(connect_ts_sec)+"."+int2str(connect_ts_usec)) 
+",";
        } else if (*it == "$end_ts") {
-         cdr+=int2str(end_ts_sec)+"."+int2str(end_ts_usec)+",";
+         cdr+=csv_quote(int2str(end_ts_sec)+"."+int2str(end_ts_usec)) +",";
        } else if (*it == "$duration") {
-         cdr+=int2str((unsigned int)diff.tv_sec)+"."+
-           int2str((unsigned int)diff.tv_usec)+",";
+         cdr+=csv_quote(getTimeDiffString(start_ts_sec, start_ts_usec,
+                                          end_ts_sec, end_ts_usec, true)) +",";
+       } else if (*it == "$duration_sec") {
+         cdr+=csv_quote(getTimeDiffString(start_ts_sec, start_ts_usec,
+                                          end_ts_sec, end_ts_usec, false)) 
+",";
+       } else if (*it == "$bill_duration") {
+         cdr+=csv_quote(getTimeDiffString(connect_ts_sec, connect_ts_usec,
+                                          end_ts_sec, end_ts_usec, true)) +",";
+       } else if (*it == "$bill_duration_sec") {
+         cdr+=csv_quote(getTimeDiffString(connect_ts_sec, connect_ts_usec,
+                                          end_ts_sec, end_ts_usec, false)) 
+",";
+       } else if (*it == "$setup_duration") {
+         if (!connect_ts_sec) {
+           cdr+=csv_quote(getTimeDiffString(start_ts_sec, start_ts_usec,
+                                            end_ts_sec, end_ts_usec, true)) 
+",";
+         } else {
+           cdr+=csv_quote(getTimeDiffString(start_ts_sec, start_ts_usec,
+                                            connect_ts_sec, connect_ts_usec, 
true)) +",";
+         }
+       } else if (*it == "$setup_duration_sec") {
+         if (!connect_ts_sec) {
+           cdr+=csv_quote(getTimeDiffString(start_ts_sec, start_ts_usec,
+                                            end_ts_sec, end_ts_usec, false)) 
+",";
+         } else {
+           cdr+=csv_quote(getTimeDiffString(start_ts_sec, start_ts_usec,
+                                            connect_ts_sec, connect_ts_usec, 
false)) +",";
+         }
        } else if (*it == "$start_tm") {
-         cdr+=timeString(start_ts_sec)+",";
+         cdr+=csv_quote(timeString(start_ts_sec)) +",";
        } else if (*it == "$connect_tm") {
-         cdr+=timeString(connect_ts_sec)+",";
+         cdr+=csv_quote(timeString(connect_ts_sec)) +",";
        } else if (*it == "$end_tm") {
-         cdr+=timeString(end_ts_sec)+",";
+         cdr+=csv_quote(timeString(end_ts_sec)) +",";
        } else {
          ERROR("in configuration: unknown value '%s' in cdr_format\n",
                it->c_str());
        }
       } else {
        if (!values.hasMember(*it)) {
-           cdr+=",";
+         cdr+=csv_quote(string("")) + ",";
        } else {
          if (isArgCStr(values[*it])) {
-           cdr+=string(values[*it].asCStr())+",";
+           cdr+=csv_quote(string(values[*it].asCStr())) +",";
          } else {
-           cdr+=AmArg::print(values[*it])+",";
+           cdr+=csv_quote(AmArg::print(values[*it])) +",";
          }
        }
       }
     }
   } else {
     // default format: ltag, start_ts, connect_ts, end_ts, <other data...>
-    cdr = ltag + "," +
-      int2str(start_ts_sec)+"."+int2str(start_ts_usec)+","+
-      int2str(connect_ts_sec)+"."+int2str(connect_ts_usec)+","+
-      int2str(end_ts_sec)+"."+int2str(end_ts_usec)+",";
+    cdr = csv_quote(ltag) + "," +
+      csv_quote(int2str(start_ts_sec)+"."+int2str(start_ts_usec)) +","+
+      csv_quote(int2str(connect_ts_sec)+"."+int2str(connect_ts_usec)) +","+
+      csv_quote(int2str(end_ts_sec)+"."+int2str(end_ts_usec)) +",";
     for (AmArg::ValueStruct::const_iterator i=values.begin(); 
i!=values.end();i++) {
       if (isArgCStr(i->second)) {
-       cdr+=string(i->second.asCStr())+",";
+       cdr+=csv_quote(string(i->second.asCStr())) +",";
       } else {
-       cdr+=AmArg::print(i->second)+",";
+       cdr+=csv_quote(AmArg::print(i->second)) +",";
       }
     }
   }
diff --git a/apps/sbc/call_control/syslog_cdr/SyslogCDR.h 
b/apps/sbc/call_control/syslog_cdr/SyslogCDR.h
index 395d00a..77b166d 100644
--- a/apps/sbc/call_control/syslog_cdr/SyslogCDR.h
+++ b/apps/sbc/call_control/syslog_cdr/SyslogCDR.h
@@ -49,6 +49,8 @@ class SyslogCDR : public AmDynInvoke
   string syslog_prefix;
   vector<string> cdr_format;
 
+  bool quoting_enabled;
+
   /* map<string, CDR*> cdrs; */
   /* AmMutex cdrs_mut; */
 
diff --git a/apps/sbc/call_control/syslog_cdr/etc/cc_syslog_cdr.conf 
b/apps/sbc/call_control/syslog_cdr/etc/cc_syslog_cdr.conf
index d7624ae..1a22e63 100644
--- a/apps/sbc/call_control/syslog_cdr/etc/cc_syslog_cdr.conf
+++ b/apps/sbc/call_control/syslog_cdr/etc/cc_syslog_cdr.conf
@@ -16,11 +16,22 @@
 #Default: 2 (LOG_INFO)
 #loglevel=4
 
+#quoting_enabled=[yes, no]   - enable double quotes (" ") around values
+#  if enabled, double quotes will be replaced with two times double quotes
+#  e.g. "Joe"   ->  """Joe"""
+# default: yes
+
 #cdr_format=<csv list of items>
 # items can be either 
 #     $ltag                             - local tag (ID for call)
 #     $start_ts, $connect_ts, $end_ts   - timestamp (sec.usec since epoch)
 #     $start_tm, $connect_tm, $end_tm   - timestamp (%F %T fmt)
+#     $duration                         - duration start to end (sec.ms)
+#     $duration_sec                     - duration start to end (sec)
+#     $bill_duration                    - duration connect to end (sec.ms)
+#     $bill_duration_sec                - duration connect to end (sec)
+#     $setup_duration                   - duration start to connect/end 
(sec.ms)
+#     $setup_duration_sec               - duration start to connect/end (sec)
 # or any of the configured values in sbc profile
 #
 #Example:
diff --git a/doc/Readme.syslog_cdr.txt b/doc/Readme.syslog_cdr.txt
deleted file mode 100644
index 9f58f66..0000000
--- a/doc/Readme.syslog_cdr.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-syslog CDR generation (e.g. for SBC)
-------------------------------------
-
-This module implements the "cdr" DI interface to generate CDRs and write them 
in CSV
-format to syslog.
-
-The CDR is stored in memory until the call is ended.
-
-
-The CSV format is:
- A leg local tag,Call-ID,From Tag,To Tag,start TS,connect TS,end TS, (...)
- 
-Where (...) is all further values as configured in the profile (cdr_*) in 
alphabetical
-order.
-
-

_______________________________________________
Semsdev mailing list
[email protected]
http://lists.iptel.org/mailman/listinfo/semsdev

Reply via email to