Skip to content

Implement Call Detail Record (CDR) Generation #36

Description

@thorsager

Problem

The PBX currently has no mechanism to record call metadata for billing, auditing, or operational analysis. Without CDRs, operators cannot answer questions like "who called whom, when, and for how long", "which trunk carried the most traffic", or "what is the call completion rate". This is a fundamental requirement for any production telephony deployment.

Proposed Solution

1. CDR Data Model

Each completed call produces a CDR record containing:

Field Type Description
call_id string SIP Call-ID of the Alice leg
start_time RFC3339 Time of initial INVITE
answer_time RFC3339 Time of 200 OK (answer)
end_time RFC3339 Time of BYE or session timeout
duration float64 Answer-to-end duration in seconds
caller string From URI (Alice)
callee string To URI (Bob/trunk target)
disposition string answered, busy, rejected, canceled, failed, timeout
trunk string Trunk name if routed through one, empty if internal
alice_ip string Source IP of Alice
bob_ip string Destination IP of Bob/trunk peer
alice_rtp_packets int RTP packets sent by Alice leg
bob_rtp_packets int RTP packets sent by Bob leg
alice_rtp_bytes int RTP bytes sent by Alice leg
bob_rtp_bytes int RTP bytes sent by Bob leg

2. Output Format

Write CDRs to a configurable file path as newline-delimited JSON (one JSON object per line):

{"call_id":"abc123","start_time":"2026-07-06T10:00:00Z","answer_time":"...","duration":42.5,"caller":"sip:alice@pbx","callee":"sip:+14155551234@trunk","disposition":"answered","trunk":"twilio","alice_rtp_packets":2100,"bob_rtp_packets":2098}

3. Implementation

  • New package: internal/cdr/ — CDR record type, JSON serialization, writer with rotation
  • CLI flag: --cdr-file path/to/cdrs.jsonl
  • Wire into B2BUA handler:
    • Record start_time on INVITE arrival
    • Record answer_time on 200 OK
    • Record end_time on BYE, CANCEL, or error
    • Count RTP packets in the bridge (media/bridge.go → increment counters per direction)
    • On call cleanup (BYE handler or bridge exit), write the CDR record
  • Thread-safe writer with optional buffering and fsync interval

4. Future Extensions (out of scope)

  • CDR export to external billing systems (REST, SFTP)
  • Real-time CDR streaming via WebSocket
  • CDR database storage (PostgreSQL, etc.)
  • Per-leg RTP quality metrics (jitter, packet loss)

Testing

  1. Unit test — CDR JSON format and field population
  2. Integration test — Make a call, verify a CDR is written to the CDR file with correct start/answer/end times and disposition=answered
  3. Integration test — Rejected call (Bob sends 486) → CDR with disposition=busy
  4. Integration test — Canceled call (Alice sends CANCEL before answer) → CDR with disposition=canceled

References

  • RFC 3261 §20.30: Call-ID header
  • Standard CDR format from Asterisk CDR (csv) and FreeSWITCH CDR (JSON)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions