diff --git a/README.md b/README.md index ff651726e..f6732b940 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,7 @@ option and which are reproduced below: -b, --b2b-url=STRING XMLRPC URL of B2B UA -L, --log-level=INT Mask log priorities above this level --log-facility=daemon|local0|... Syslog facility to use for logging + --log-facility-cdr=local0|... Syslog facility to use for logging CDRs -E, --log-stderr Log on stderr instead of syslog -x, --xmlrpc-format=INT XMLRPC timeout request format to use. 0: SEMS DI, 1: call-id only --num-threads=INT Number of worker threads to create @@ -309,6 +310,10 @@ The options are described in more detail below. The syslog facilty to use when sending log messages to the syslog daemon. Defaults to `daemon`. +* --log-facilty-cdr=daemon|local0|...|local7|... + + Same as --log-facility with the difference that only CDRs are written to this log facility. + * -E, --log-stderr Log to stderr instead of syslog. Only useful in combination with `--foreground`. diff --git a/daemon/call.c b/daemon/call.c index c806b5eb6..e44e83d7e 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -831,6 +831,7 @@ forward: if (ret == -1) { ret = -errno; + ilog(LOG_DEBUG,"Error when sending message. Error:%s\n",strerror(errno)); stream->stats.errors++; mutex_lock(&cm->statspslock); cm->statsps.errors++; @@ -2311,6 +2312,10 @@ void call_destroy(struct call *c) { struct call_media *md; GList *k, *o; char buf[64]; + static const int CDRBUFLENGTH = 4096*2; + char cdrbuffer[CDRBUFLENGTH]; memset(&cdrbuffer,0,CDRBUFLENGTH); + char* cdrbufcur = cdrbuffer; + int cdrlinecnt = 0; rwlock_lock_w(&m->hashlock); ret = g_hash_table_remove(m->callhash, &c->callid); @@ -2328,8 +2333,26 @@ void call_destroy(struct call *c) { ilog(LOG_INFO, "Final packet stats:"); + /* CDRs and statistics */ + cdrbufcur += sprintf(cdrbufcur,"ci=%s, ",c->callid.s); for (l = c->monologues; l; l = l->next) { ml = l->data; + if (_log_facility_cdr) { + cdrbufcur += sprintf(cdrbufcur, "ml%i_start_time=%u, " + "ml%i_end_time=%u, " + "ml%i_duration=%u, " + "ml%i_termination=%s, " + "ml%i_local_tag=%s, " + "ml%i_remote_tag=%s, ", + + cdrlinecnt, (unsigned int)ml->created, + cdrlinecnt, (unsigned int)poller_now, + cdrlinecnt, (unsigned int)poller_now-(unsigned int)ml->created, + cdrlinecnt, "TOBEDONE", + cdrlinecnt, ml->tag.s, + cdrlinecnt, ml->active_dialogue ? ml->active_dialogue->tag.s : "(none)"); + } + ilog(LOG_INFO, "--- Tag '"STR_FORMAT"', created " "%u:%02u ago, in dialogue with '"STR_FORMAT"'", STR_FMT(&ml->tag), @@ -2348,6 +2371,24 @@ void call_destroy(struct call *c) { continue; smart_ntop_p(buf, &ps->endpoint.ip46, sizeof(buf)); + + if (_log_facility_cdr) { + const char* protocol = (!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? "rtcp" : "rtp"; + cdrbufcur += sprintf(cdrbufcur, + "ml%i_midx%u_%s_endpoint_ip=%s, " + "ml%i_midx%u_%s_endpoint_port=%u, " + "ml%i_midx%u_%s_local_relay_port=%u, " + "ml%i_midx%u_%s_relayed_packets=%llu, " + "ml%i_midx%u_%s_relayed_bytes=%llu, " + "ml%i_midx%u_%s_relayed_errors=%llu, ", + cdrlinecnt, md->index, protocol, buf, + cdrlinecnt, md->index, protocol, ps->endpoint.port, + cdrlinecnt, md->index, protocol, (unsigned int) (ps->sfd ? ps->sfd->fd.localport : 0), + cdrlinecnt, md->index, protocol, (unsigned long long) ps->stats.packets, + cdrlinecnt, md->index, protocol, (unsigned long long) ps->stats.bytes, + cdrlinecnt, md->index, protocol, (unsigned long long) ps->stats.errors); + } + ilog(LOG_INFO, "------ Media #%u, port %5u <> %15s:%-5hu%s, " "%llu p, %llu b, %llu e", md->index, @@ -2359,8 +2400,14 @@ void call_destroy(struct call *c) { (unsigned long long) ps->stats.errors); } } + if (_log_facility_cdr) + ++cdrlinecnt; } + if (_log_facility_cdr) + /* log it */ + cdrlog(cdrbuffer); + for (l = c->streams; l; l = l->next) { ps = l->data; diff --git a/daemon/log.c b/daemon/log.c index 94d9f6421..a46b5a9bb 100644 --- a/daemon/log.c +++ b/daemon/log.c @@ -57,6 +57,7 @@ static const char* const prio_str[] = { gboolean _log_stderr = 0; int _log_facility = LOG_DAEMON; +int _log_facility_cdr = 0; static GHashTable *__log_limiter; @@ -167,6 +168,13 @@ out: free(msg); } +void cdrlog(const char* cdrbuffer) { + int previous; + int mask = LOG_MASK (LOG_INFO); + previous = setlogmask(mask); + syslog(LOG_INFO | _log_facility_cdr, "%s", cdrbuffer); + setlogmask(previous); +} void log_init() { mutex_init(&__log_limiter_lock); diff --git a/daemon/log.h b/daemon/log.h index d837a4b60..9d09eba14 100644 --- a/daemon/log.h +++ b/daemon/log.h @@ -22,6 +22,7 @@ struct log_info { extern gboolean _log_stderr; extern int _log_facility; +extern int _log_facility_cdr; typedef struct _fac_code { @@ -46,14 +47,11 @@ extern unsigned int max_log_line_length; void log_init(void); void ilog(int prio, const char *fmt, ...)__attribute__ ((format (printf, 2, 3))); - +void cdrlog(const char* cdrbuffer); #include "obj.h" - - - INLINE void log_info_clear() { switch (log_info.e) { case LOG_INFO_NONE: diff --git a/daemon/main.c b/daemon/main.c index 16f4786ee..bc35434f2 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -311,6 +311,7 @@ static void options(int *argc, char ***argv) { char *listenngs = NULL; char *redisps = NULL; char *log_facility_s = NULL; + char *log_facility_cdr_s = NULL; int version = 0; int sip_source = 0; @@ -334,6 +335,7 @@ static void options(int *argc, char ***argv) { { "b2b-url", 'b', 0, G_OPTION_ARG_STRING, &b2b_url, "XMLRPC URL of B2B UA" , "STRING" }, { "log-level", 'L', 0, G_OPTION_ARG_INT, (void *)&log_level,"Mask log priorities above this level","INT" }, { "log-facility",0, 0, G_OPTION_ARG_STRING, &log_facility_s, "Syslog facility to use for logging", "daemon|local0|...|local7"}, + { "log-facility-cdr",0, 0, G_OPTION_ARG_STRING, &log_facility_cdr_s, "Syslog facility to use for logging CDRs", "daemon|local0|...|local7"}, { "log-stderr", 'E', 0, G_OPTION_ARG_NONE, &_log_stderr, "Log on stderr instead of syslog", NULL }, { "xmlrpc-format",'x', 0, G_OPTION_ARG_INT, &xmlrpc_fmt, "XMLRPC timeout request format to use. 0: SEMS DI, 1: call-id only", "INT" }, { "num-threads", 0, 0, G_OPTION_ARG_INT, &num_threads, "Number of worker threads to create", "INT" }, @@ -407,6 +409,13 @@ static void options(int *argc, char ***argv) { } } + if (log_facility_cdr_s) { + if (!parse_log_facility(log_facility_cdr_s, &_log_facility_cdr)) { + print_available_log_facilities(); + die ("Invalid log facility for CDR '%s' (--log-facility-cdr)\n", log_facility_cdr_s); + } + } + if (_log_stderr) { write_log = log_to_stderr; max_log_line_length = 0; diff --git a/debian/ngcp-rtpengine-daemon.default b/debian/ngcp-rtpengine-daemon.default index 74c8a44c1..9cf8cab7b 100644 --- a/debian/ngcp-rtpengine-daemon.default +++ b/debian/ngcp-rtpengine-daemon.default @@ -19,4 +19,5 @@ TABLE=0 # B2B_URL=http://127.0.0.1:8090/ # LOG_LEVEL=6 # LOG_FACILITY=daemon +# LOG_FACILITY_CDR=daemon # NUM_THREADS=5 diff --git a/debian/ngcp-rtpengine-daemon.init b/debian/ngcp-rtpengine-daemon.init index d68cf0d4a..82637eb55 100755 --- a/debian/ngcp-rtpengine-daemon.init +++ b/debian/ngcp-rtpengine-daemon.init @@ -68,6 +68,7 @@ fi OPTIONS="$OPTIONS --table=$TABLE" [ -z "$LOG_LEVEL" ] || OPTIONS="$OPTIONS --log-level=$LOG_LEVEL" [ -z "$LOG_FACILITY" ] || OPTIONS="$OPTIONS --log-facility=$LOG_FACILITY" +[ -z "$LOG_FACILITY_CDR" ] || OPTIONS="$OPTIONS --log-facility-cdr=$LOG_FACILITY_CDR" [ -z "$NUM_THREADS" ] || OPTIONS="$OPTIONS --num-threads=$NUM_THREADS" if test "$FORK" = "no" ; then OPTIONS="$OPTIONS --foreground" diff --git a/el/rtpengine.init b/el/rtpengine.init index 876b2ea64..5c0c97d4b 100644 --- a/el/rtpengine.init +++ b/el/rtpengine.init @@ -146,6 +146,11 @@ build_opts() { then OPTS+=" --num-threads=$NUM_THREADS" fi + + if [[ -n "$LOG_FACILITY_CDR" ]] + then + OPTS+=" --log-facility-cdr=$LOG_FACILITY_CDR" + fi } start() { diff --git a/el/rtpengine.sysconfig b/el/rtpengine.sysconfig index 70e93370a..2baa2997e 100644 --- a/el/rtpengine.sysconfig +++ b/el/rtpengine.sysconfig @@ -34,6 +34,7 @@ LISTEN_UDP=127.0.0.1:2222 # IP address and port combination for UDP # #LOG_LEVEL=6 # Log level to use #LOG_FACILITY=daemon # Syslog facility to use +#LOG_FACILITY_CDR=daemon # Syslog facility to write CDRs #NUM_THREADS=5 # How many worker threads to launch # The following items are for use with NGCP