From FreeSWITCH Wiki
(Redirected from Cdr)
Jump to: navigation, search


Call Detail Records (CDR)

There are several ways which FreeSWITCH can save CDR (Call Detail Record).

Popular ways include:

  • mod_cdr_csv - saves a CSV file with the variables you specify in a template.
  • mod_cdr_mongodb - saves detailed CDR data to a MongoDB database, in a format similar to mod_json_cdr.
  • mod_cdr_pg_csv - Asterisk Compatible CDR Module with PostgreSQL interface.
  • mod_cdr_sqlite - saves directly to an sqlite DB with the variables you specify in a template.
  • mod_json_cdr - Saves to file or POSTs a JSON representation of the channel variable and callflow. It can post directly to CouchDB.
  • mod_xml_cdr - Saves to file or POSTs an XML representation of the channel variable and callflow.
  • mod_radius_cdr - RADIUS CDR Module.

CDR Handling

You can instruct FreeSWITCH to not log a call, or only log leg B or the like. See: Variable_process_cdr

You can also specify hangup causes that should not generate a CDR. See Variable_skip_cdr_causes (added V1.2.3 3cf238fc)

Calculating Various Time Values For A Call

FreeSWITCH CDRs contain lots of information. From those values one can extract other information:

What time a call started ringing (PDD)

When a call starts ringing it either has media or does not have media. If it has media then progress_media_time will be set; if not, progress_time will be set. The value not set will be zero. Therefore this formula will always yield the exact time the call started to ring:

ring_start_time = progress_time + progress_media_time

The PDD (post-dial delay) is the period of silence between the call starting and the call ringing, therefore for calls that ring:

pdd = ring_start_time - created_time

How long was the talk time

If the call was answered then there is a talk time. If call was unanswered then answered_time will be 0. If answered_time is greater than zero:

length_of_talk_time = hangup_time - answered_time

Avi Marcus: Isn't this just billsec or billmsec/1000 ?

How long did the phone ring

How long the phone rang is determined either by the hangup_time or the answered_time:

if ( answered_time == 0 ) then
    length_of_phone_ringing = hangup_time - created_time
    length_of_phone_ringing = answered_time - created_time

How long was the call on hold

As of September 13th, 2012, FreeSWITCH also provides the hold_events field. This field is structured as a flattened, multi-dimensional array of uepoch values representing the start and stop time of hold events for the channel. For example: {{1347907323618493,1347907328495937},{1347907309458486,1347907314655415},{1347907298602214,1347907304095908},{1347907285118780,1347907291355494}}

It is structured in native PostgreSQL array text format so it is very easy to work with in PostgreSQL. Here is an example showing how to work with such a field in PostgreSQL:

	hold_recs BIGINT[][];
	tmp RECORD;
	hold_recs := '{{1347907323618493,1347907328495937},{1347907309458486,1347907314655415},{1347907298602214,1347907304095908},{1347907285118780,1347907291355494}}';

	FOR tmp IN
		WITH uepochs AS (
			SELECT unnest((SELECT hr1[array_lower(hold_recs, 1):array_upper(hold_recs, 1)][1:1] FROM (SELECT hold_recs::BIGINT[] AS hr1) AS ss)) AS start_time,
			       unnest((SELECT hr1[array_lower(hold_recs, 1):array_upper(hold_recs, 1)][2:2] FROM (SELECT hold_recs::BIGINT[] AS hr1) AS ss)) AS stop_time
		SELECT (TIMESTAMP WITH TIME ZONE 'epoch' + start_time * INTERVAL '1 microsecond') AS start_time, (TIMESTAMP WITH TIME ZONE 'epoch' + stop_time * INTERVAL '1 microsecond') AS stop_time FROM uepochs
		RAISE NOTICE 'Start time: %; Stop time: %', tmp.start_time, tmp.stop_time;
LANGUAGE plpgsql;

SELECT * FROM test_hold();

The "WITH" clause converts the multi-dimensional array into a set of row result records with two columns: start_time and stop_time. The "SELECT" clause below the "WITH" clause converts the resulting micro-second Epoch timestamps into TIMESTAMP WITH TIME ZONE types and returns the resulting set of rows. The FOR loop assigns each row in turn to the RECORD variable "tmp," which is used in the RAISE NOTICE line to print out the resulting values. This allows you to treat the hold_events field as a TEXT type in the database and convert it into a record-based result-set on demand anywhere you need to iterate through the hold records.

Putting Together CDRs

If you are saving both A and B leg for CDRs, you'll need to piece them together.

  • The self-uuid is stored in "uuid"
  • A-legs - it's an A-leg if:
    • "originatee" or "origination" or "originate_disposition" is set. (NOTE: The original channel that triggered a loopback shows up as B leg in xml_cdr)
  • B-legs - it's a B-leg if:
    • "ent_originate_aleg_uuid" is set. "ent_originate_aleg_uuid" contains the UUID of the A leg.
    • "originator" or "originating_leg_uuid" is set. Both contain the UUID of the A leg.
  • What's the connecting leg?
    • For enterprise originates: on B-leg, use ent_originate_aleg_uuid.
    • "bridge_uuid" (not in bypass mode)
    • "signal_bond"
    • "last_bridge_to"
    • In B-leg: originator or originating_leg_uuid
    • In A-leg: Parse originated_legs or originate_causes (added 2012-10-18, commit 3099445a95933a52954c64d5f2fd314a55577c9d)