惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
L
LINUX DO - 最新话题
TaoSecurity Blog
TaoSecurity Blog
S
Security Affairs
H
Hacker News: Front Page
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Hacker News: Ask HN
Hacker News: Ask HN
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
Google DeepMind News
Google DeepMind News
Schneier on Security
Schneier on Security
云风的 BLOG
云风的 BLOG
I
InfoQ
The Register - Security
The Register - Security
T
Tor Project blog
T
Threat Research - Cisco Blogs
Spread Privacy
Spread Privacy
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
The GitHub Blog
The GitHub Blog
MongoDB | Blog
MongoDB | Blog
Webroot Blog
Webroot Blog
Recent Announcements
Recent Announcements
Vercel News
Vercel News
F
Fortinet All Blogs
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
SecWiki News
SecWiki News
G
Google Developers Blog
N
Netflix TechBlog - Medium
U
Unit 42
Martin Fowler
Martin Fowler
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
O
OpenAI News
博客园 - 叶小钗
T
Tailwind CSS Blog
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Help Net Security
Help Net Security
A
About on SuperTechFans
Recorded Future
Recorded Future
Last Week in AI
Last Week in AI
Hugging Face - Blog
Hugging Face - Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
D
DataBreaches.Net
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
The Blog of Author Tim Ferriss
PCI Perspectives
PCI Perspectives
F
Full Disclosure
美团技术团队
L
Lohrmann on Cybersecurity
H
Hackread – Cybersecurity News, Data Breaches, AI and More

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Asterisk CDR Analysis — Extract Insights from Call Detail Records
Moisi Trungu · 2026-05-08 · via DEV Community

Asterisk CDR Analysis — Extract Insights from Call Detail Records

Master the complete workflow for analyzing Asterisk Call Detail Records in production ViciDial environments—from database queries to real-time metrics and automated reporting.

Introduction

Call Detail Records (CDRs) are the backbone of telecom operations. Every call passing through your Asterisk system generates raw data that contains critical insights: which numbers convert, where calls fail, which agents perform best, and where money is being wasted. In production ViciDial environments, CDR data accumulates rapidly—thousands of records daily—making manual inspection impossible.

This tutorial walks you through extracting, transforming, and analyzing CDR data using SQL queries, command-line tools, and custom scripts. You'll learn to identify trends, debug call failures, optimize routing, and generate compliance reports. These are skills you need right now if you're managing an Asterisk system under load.

Prerequisites

Before starting, verify you have:

  • Access to the Asterisk server with root or sudo privileges
  • MySQL/MariaDB client installed and credentials for the asterisk database
  • Asterisk CLI access (typically via asterisk -rx command)
  • ViciDial installation (version 2.14 or later, though techniques apply to 2.12+)
  • Basic SQL knowledge (SELECT, WHERE, GROUP BY, JOIN)
  • Understanding of Asterisk dialplan basics (extensions, applications, variables)
  • Log file access at /var/log/asterisk/messages
  • Cron or systemd timer for scheduled tasks (optional but recommended)

Verify database connectivity:

mysql -u root -p asterisk -e "SELECT COUNT(*) FROM vicidial_log LIMIT 1;"

Enter fullscreen mode Exit fullscreen mode

If this fails, check /etc/asterisk/asterisk.conf for database credentials and verify the MySQL service is running.

Understanding Asterisk CDR Structure in ViciDial

Native Asterisk CDR vs. ViciDial Logging

Asterisk generates CDRs natively via the CDR module, but ViciDial overlays custom logging on top. Understanding both is essential:

Native Asterisk CDR:

  • Stored in /var/log/asterisk/cdr-csv/ (CSV format)
  • Fields: accountcode, src, dst, dcontext, channel, dstchannel, lastapp, lastdata, start, answer, end, duration, billsec, disposition, amaflags, uniqueid, userfield

ViciDial Enhanced Logging:

  • Stored in MySQL tables: vicidial_log, vicidial_closer_log, vicidial_carrier_log
  • Includes agent activity, campaign info, lead scoring, inbound routing
  • Real-time accessible for live dashboards and compliance

Critical ViciDial Tables for CDR Analysis

vicidial_log — The primary call record table:

DESCRIBE vicidial_log;

Enter fullscreen mode Exit fullscreen mode

Key fields:

  • uniqueid — Unique call identifier (matches Asterisk)
  • lead_id — ViciDial lead identifier
  • user — Agent username
  • campaign_id — Campaign code
  • call_date — Call timestamp
  • length_in_sec — Total call duration
  • talk_sec — Actual conversation time
  • hold_sec — Time on hold
  • status — Final disposition (SALE, XFER, NO ANS, etc.)
  • phone_number — Dialed number
  • called_number — Actual number called

vicidial_closer_log — Detailed disposition and result data:

DESCRIBE vicidial_closer_log;

Enter fullscreen mode Exit fullscreen mode

Includes agent notes, call outcomes, lead disposition changes.

vicidial_carrier_log — Inbound/outbound carrier metrics (if applicable).

Setting Up Your Analysis Environment

Create a Dedicated Reporting Database User

Don't query production using root. Create a read-only user:

CREATE USER 'cdr_reporter'@'localhost' IDENTIFIED BY 'SecurePassword123!';
GRANT SELECT ON asterisk.* TO 'cdr_reporter'@'localhost';
GRANT SELECT ON asterisk.vicidial_log TO 'cdr_reporter'@'localhost';
GRANT SELECT ON asterisk.vicidial_closer_log TO 'cdr_reporter'@'localhost';
FLUSH PRIVILEGES;

Enter fullscreen mode Exit fullscreen mode

Create Helper Views for Common Queries

Simplify repeated analysis with SQL views:

CREATE VIEW cdr_daily_summary AS
SELECT 
  DATE(call_date) AS call_day,
  campaign_id,
  COUNT(*) AS total_calls,
  SUM(CASE WHEN status IN ('SALE', 'XFER') THEN 1 ELSE 0 END) AS conversions,
  ROUND(SUM(CASE WHEN status IN ('SALE', 'XFER') THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS conversion_rate,
  SUM(talk_sec) AS total_talk_seconds,
  ROUND(AVG(talk_sec), 2) AS avg_talk_seconds,
  ROUND(AVG(length_in_sec), 2) AS avg_call_duration
FROM vicidial_log
WHERE call_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY DATE(call_date), campaign_id;

Enter fullscreen mode Exit fullscreen mode

Test the view:

mysql -u cdr_reporter -p asterisk -e "SELECT * FROM cdr_daily_summary LIMIT 10;"

Enter fullscreen mode Exit fullscreen mode

Core CDR Analysis Queries

1. Identifying Failed Calls and Bottlenecks

SELECT 
  call_date,
  user,
  campaign_id,
  phone_number,
  status,
  length_in_sec,
  talk_sec
FROM vicidial_log
WHERE call_date >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
  AND status IN ('NO ANS', 'BUSY', 'NOT CALLABLE', 'DNC')
  AND campaign_id = 'YOUR_CAMPAIGN_ID'
ORDER BY call_date DESC
LIMIT 100;

Enter fullscreen mode Exit fullscreen mode

This reveals which numbers aren't working and why. High "NO ANS" rates might indicate:

  • Stale lead lists
  • Calling outside business hours
  • Network/codec issues on carrier side
  • Asterisk routing misconfiguration

2. Agent Performance Benchmarking

SELECT 
  user,
  COUNT(*) AS calls_handled,
  SUM(CASE WHEN status IN ('SALE', 'XFER') THEN 1 ELSE 0 END) AS conversions,
  ROUND(SUM(CASE WHEN status IN ('SALE', 'XFER') THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS conversion_rate,
  ROUND(AVG(talk_sec), 2) AS avg_talk_sec,
  SUM(talk_sec) AS total_talk_sec,
  ROUND(AVG(length_in_sec), 2) AS avg_call_length
FROM vicidial_log
WHERE call_date >= DATE_SUB(NOW(), INTERVAL 7 DAY)
  AND campaign_id = 'YOUR_CAMPAIGN_ID'
  AND user NOT IN ('SYSTEM', 'TRANSFER')
GROUP BY user
ORDER BY conversion_rate DESC;

Enter fullscreen mode Exit fullscreen mode

3. Inbound vs. Outbound Call Analysis

SELECT 
  CASE WHEN direction = 'INBOUND' THEN 'Inbound' ELSE 'Outbound' END AS call_type,
  COUNT(*) AS total_calls,
  COUNT(CASE WHEN status IN ('SALE', 'XFER') THEN 1 END) AS conversions,
  ROUND(COUNT(CASE WHEN status IN ('SALE', 'XFER') THEN 1 END) / COUNT(*) * 100, 2) AS conversion_rate,
  ROUND(AVG(talk_sec), 2) AS avg_talk_sec,
  ROUND(AVG(length_in_sec), 2) AS avg_call_duration
FROM vicidial_log
WHERE call_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY direction;

Enter fullscreen mode Exit fullscreen mode

4. Campaign Performance Comparison

SELECT 
  campaign_id,
  COUNT(*) AS calls,
  SUM(CASE WHEN status IN ('SALE', 'XFER') THEN 1 ELSE 0 END) AS conversions,
  ROUND(SUM(CASE WHEN status IN ('SALE', 'XFER') THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS conversion_rate,
  ROUND(AVG(talk_sec), 2) AS avg_talk_sec,
  SUM(talk_sec) / 3600 AS hours_talk_time
FROM vicidial_log
WHERE call_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
  AND user NOT IN ('SYSTEM', 'TRANSFER')
GROUP BY campaign_id
ORDER BY conversion_rate DESC;

Enter fullscreen mode Exit fullscreen mode

5. Detecting Call Quality Issues

SELECT 
  DATE(call_date) AS call_date,
  COUNT(*) AS total_calls,
  COUNT(CASE WHEN length_in_sec < 5 THEN 1 END) AS dropped_early,
  ROUND(COUNT(CASE WHEN length_in_sec < 5 THEN 1 END) / COUNT(*) * 100, 2) AS drop_rate_pct,
  COUNT(CASE WHEN status IN ('NO ANS', 'BUSY') THEN 1 END) AS unreachable,
  ROUND(COUNT(CASE WHEN status IN ('NO ANS', 'BUSY') THEN 1 END) / COUNT(*) * 100, 2) AS unreachable_pct
FROM vicidial_log
WHERE call_date >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY DATE(call_date)
ORDER BY call_date DESC;

Enter fullscreen mode Exit fullscreen mode

Early call drops (< 5 seconds) indicate network issues, bad numbers, or hangup detection problems.

Real-Time Monitoring with Asterisk CLI

Monitor Active Calls

asterisk -rx "core show channels"

Enter fullscreen mode Exit fullscreen mode

Output includes current calls, channel types, and duration. Useful for:

  • Detecting hung calls
  • Verifying call routing during testing
  • Identifying resource exhaustion

Check SIP Registration Status

asterisk -rx "sip show peers"

Enter fullscreen mode Exit fullscreen mode

Ensure all configured trunks and agents are registered. Missing registrations explain inbound call failures.

Monitor CDR Recording

asterisk -rx "cdr show status"

Enter fullscreen mode Exit fullscreen mode

Verify CDR module is active. If output shows "CDR logging disabled," check /etc/asterisk/cdr.conf:

[general]
enable=yes

Enter fullscreen mode Exit fullscreen mode

Then reload:

asterisk -rx "module reload cdr"

Enter fullscreen mode Exit fullscreen mode

Create a Live Dashboard Script

#!/bin/bash
# live_cdr_monitor.sh - Real-time call metrics

while true; do
  clear
  echo "=== ViciDial CDR Live Monitor ==="
  echo "Time: $(date)"
  echo ""

  # Current active calls
  echo "--- Active Calls ---"
  asterisk -rx "core show channels" | grep -E "^SIP|^Agent" | head -5

  # Calls in last hour
  echo ""
  echo "--- Last Hour Metrics ---"
  mysql -u cdr_reporter -p'SecurePassword123!' asterisk -e "
    SELECT 
      COUNT(*) as calls,
      SUM(CASE WHEN status IN ('SALE', 'XFER') THEN 1 ELSE 0 END) as conversions,
      ROUND(SUM(CASE WHEN status IN ('SALE', 'XFER') THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) as conv_rate
    FROM vicidial_log
    WHERE call_date >= DATE_SUB(NOW(), INTERVAL 1 HOUR);
  "

  sleep 30
done

Enter fullscreen mode Exit fullscreen mode

Make it executable:

chmod +x live_cdr_monitor.sh
./live_cdr_monitor.sh

Enter fullscreen mode Exit fullscreen mode

Exporting and Processing CDR Data

Export to CSV for Excel/Sheets Analysis

#!/bin/bash
# export_cdr_csv.sh

OUTPUT_FILE="/tmp/cdr_export_$(date +%Y%m%d_%H%M%S).csv"

mysql -u cdr_reporter -p'SecurePassword123!' asterisk \
  -e "SELECT 
        call_date,
        user,
        campaign_id,
        phone_number,
        status,
        length_in_sec,
        talk_sec,
        hold_sec
      FROM vicidial_log
      WHERE call_date >= DATE_SUB(NOW(), INTERVAL 7 DAY)
      ORDER BY call_date DESC;" \
  --batch --skip-column-names | \
  sed 's/\t/,/g' > "$OUTPUT_FILE"

echo "CDR export saved to: $OUTPUT_FILE"
ls -lh "$OUTPUT_FILE"

Enter fullscreen mode Exit fullscreen mode

Run:

chmod +x export_cdr_csv.sh
./export_cdr_csv.sh

Enter fullscreen mode Exit fullscreen mode

Parse Raw Asterisk CDR Files

If you're using CSV CDR storage:

#!/bin/bash
# analyze_csv_cdr.sh

CDR_DIR="/var/log/asterisk/cdr-csv"

# Show recent CDR file structure
head -1 "$CDR_DIR"/*.csv | tail -1

# Count calls by status in last day
find "$CDR_DIR" -name "*.csv" -mtime -1 -exec cat {} \; | \
  awk -F',' '{print $NF}' | sort | uniq -c | sort -rn

Enter fullscreen mode Exit fullscreen mode

Advanced Analysis: Detecting Patterns and Anomalies

Finding Your Best Performing Time Windows

SELECT 
  HOUR(call_date) AS hour_of_day,
  COUNT(*) AS calls,
  SUM(CASE WHEN status IN ('SALE', 'XFER') THEN 1 ELSE 0 END) AS conversions,
  ROUND(SUM(CASE WHEN status IN ('SALE', 'XFER') THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS conversion_rate,
  ROUND(AVG(talk_sec), 2) AS avg_talk_sec
FROM vicidial_log
WHERE call_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
  AND DAYOFWEEK(call_date) NOT IN (1, 7)
GROUP BY HOUR(call_date)
ORDER BY conversion_rate DESC;

Enter fullscreen mode Exit fullscreen mode

This reveals peak efficiency hours. Most campaigns see 20-30% variation across day parts. Concentrate calls during peak windows.

Identifying Caller ID Spoofing / Fraud

SELECT 
  phone_number,
  COUNT(*) AS call_count,
  COUNT(DISTINCT user) AS unique_agents,
  COUNT(DISTINCT campaign_id) AS unique_campaigns,
  ROUND(SUM(CASE WHEN status IN ('SALE', 'XFER') THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS conversion_rate
FROM vicidial_log
WHERE call_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY phone_number
HAVING call_count > 100
  AND conversion_rate < 5
  AND unique_agents > 5
ORDER BY call_count DESC
LIMIT 20;

Enter fullscreen mode Exit fullscreen mode

Numbers appearing across multiple agents/campaigns with poor conversion and high volume are often fraud or spam sources. Add to DNC list immediately.

Measuring First Call Resolution (FCR)

SELECT 
  vl1.lead_id,
  COUNT(*) AS call_attempts,
  vl1.status AS final_status,
  MIN(vl1.call_date) AS first_call,
  MAX(vl1.call_date) AS last_call,
  ROUND(TIMESTAMPDIFF(HOUR, MIN(vl1.call_date), MAX(vl1.call_date)), 1) AS hours_to_resolution
FROM vicidial_log vl1
WHERE vl1.call_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
  AND vl1.status IN ('SALE', 'XFER')
GROUP BY vl1.lead_id
HAVING call_attempts > 1
ORDER BY call_attempts DESC
LIMIT 50;

Enter fullscreen mode Exit fullscreen mode

Multiple calls to same lead = poor FCR. High numbers indicate ineffective agent training or bad lead qualification.

Compliance and Call Recording Verification

SELECT 
  call_date,
  user,
  campaign_id,
  phone_number,
  status,
  recording_id,
  talk_sec,
  CASE 
    WHEN recording_id IS NULL THEN 'NO RECORDING'
    WHEN talk_sec > 0 THEN 'RECORDED'
    ELSE 'NO CONVERSATION'
  END AS compliance_status
FROM vicidial_log
WHERE call_date >= DATE_SUB(NOW(), INTERVAL 1 DAY)
  AND status IN ('SALE', 'XFER')
  AND recording_id IS NULL
ORDER BY call_date DESC;

Enter fullscreen mode Exit fullscreen mode

Missing recordings on completed calls violate compliance in many jurisdictions. This query exposes gaps.

Creating Automated Reports and Alerts

Daily Email Report Script

#!/bin/bash
# daily_cdr_report.sh

REPORT_DATE=$(date +"%Y-%m-%d")
RECIPIENT="manager@yourcompany.com"

mysql -u cdr_reporter -p'SecurePassword123!' asterisk > /tmp/daily_report.txt << EOF

SELECT '=== DAILY CDR SUMMARY ===' as report;
SELECT CONCAT('Report Date: ', DATE(NOW()));
SELECT '';

SELECT 'CAMPAIGN PERFORMANCE' as section;
SELECT 
  campaign_id,
  COUNT(*) AS calls,
  SUM(CASE WHEN status IN ('SALE', 'XFER') THEN 1 ELSE 0 END) AS conversions,
  ROUND(SUM(CASE WHEN status IN ('SALE', 'XFER') THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS conv_rate
FROM vicidial_log
WHERE DATE(call_date) = DATE(NOW()) - INTERVAL 1 DAY
GROUP BY campaign_id
ORDER BY conv_rate DESC;

SELECT '';
SELECT 'TOP AGENTS' as section;
SELECT 
  user,
  COUNT(*) AS calls,
  SUM(CASE WHEN status IN ('SALE', 'XFER') THEN 1 ELSE 0 END) AS conversions,
  ROUND(AVG(talk_sec), 2) AS avg_talk_sec
FROM vicidial_log
WHERE DATE(call_date) = DATE(NOW()) - INTERVAL 1 DAY
  AND user NOT IN ('SYSTEM', 'TRANSFER')
GROUP BY user
ORDER BY conversions DESC
LIMIT 10;

SELECT '';
SELECT 'CALL QUALITY METRICS' as section;
SELECT 
  COUNT(*) AS total_calls,
  COUNT(CASE WHEN length_in_sec < 5 THEN 1 END) AS dropped_calls,
  ROUND(COUNT(CASE WHEN length_in_sec < 5 THEN 1 END) / COUNT(*) * 100, 2) AS drop_rate
FROM vicidial_log
WHERE DATE(call_date) = DATE(NOW()) - INTERVAL 1 DAY;

EOF

mail -s "ViciDial CDR Report - $REPORT_DATE" "$RECIPIENT" < /tmp/daily_report.txt

Enter fullscreen mode Exit fullscreen mode

Schedule in crontab:

crontab -e

Enter fullscreen mode Exit fullscreen mode

Add:

0 8 * * * /root/daily_cdr_report.sh

Enter fullscreen mode Exit fullscreen mode

Alert on Anomalies

#!/bin/bash
# cdr_anomaly_alert.sh

THRESHOLD_DROP_RATE=15  # Alert if drop rate exceeds 15%
ALERT_EMAIL="ops@yourcompany.com"

DROP_RATE=$(mysql -u cdr_reporter -p'SecurePassword123!' asterisk -se "
  SELECT ROUND(
    COUNT(CASE WHEN length_in_sec < 5 THEN 1 END) / COUNT(*) * 100, 2
  )
  FROM vicidial_log
  WHERE call_date >= DATE_SUB(NOW(), INTERVAL 1 HOUR);
")

if (( $(echo "$DROP_RATE > $THRESHOLD_DROP_RATE" | bc -l) )); then
  echo "ALERT: Call drop rate is $DROP_RATE% (threshold: $THRESHOLD_DROP_RATE%)" | \
    mail -s "CRITICAL: High Call Drop Rate Detected" "$ALERT_EMAIL"
fi

Enter fullscreen mode Exit fullscreen mode

Run every 15 minutes:

*/15 * * * * /root/cdr_anomaly_alert.sh

Enter fullscreen mode Exit fullscreen mode

Troubleshooting CDR Issues

No CDR Records Being Generated

Symptom: asterisk -rx "cdr show status" shows "CDR logging disabled"

Solution:

  1. Check /etc/asterisk/cdr.conf:
[general]
enable=yes

Enter fullscreen mode Exit fullscreen mode

  1. Reload CDR module:
asterisk -rx "module reload cdr"

Enter fullscreen mode Exit fullscreen mode

  1. If using CEL (Channel Event Logging), verify it's enabled:
asterisk -rx "cel show status"

Enter fullscreen mode Exit fullscreen mode

CDR Records Exist But Have NULL Fields

Symptom: Database shows talk_sec, hold_sec as NULL

Reason: Variables not being set in dialplan. Asterisk requires explicit tracking via DIALPLAN() application.

Solution: In /etc/asterisk/extensions-vicidial.conf, ensure:

exten => s,n,Set(CDR(userfield)=${CAMPAIGN_ID})
exten => s,n,Set(CHANNEL(language)=en)

Enter fullscreen mode Exit fullscreen mode

Database Queries Slow / Timeouts

Symptom: Large SELECT queries hang or timeout after 30 seconds

Solution: Add indexes:

ALTER TABLE vicidial_log ADD INDEX idx_call_date (call_date);
ALTER TABLE vicidial_log ADD INDEX idx_campaign_user (campaign_id, user);
ALTER TABLE vicidial_log ADD INDEX idx_status (status);

Enter fullscreen mode Exit fullscreen mode

Verify indexes exist:

mysql -u root -p asterisk -e "SHOW INDEXES FROM vicidial_log;"

Enter fullscreen mode Exit fullscreen mode

Asterisk CLI Commands Failing (Permission Denied)

Symptom: asterisk -rx returns "permission denied"

Solution: Your user isn't in the asterisk group:

usermod -a -G asterisk $USER
newgrp asterisk
asterisk -rx "core show channels"

Enter fullscreen mode Exit fullscreen mode

Or run with sudo:

sudo asterisk -rx "core show channels"

Enter fullscreen mode Exit fullscreen mode

MySQL Connection Issues from Scripts

Symptom: Bash scripts fail with "Access denied for user"

Solution:

  1. Create .my.cnf in home directory:
cat > ~/.my.cnf << EOF
[client]
user=cdr_reporter
password=SecurePassword123!
host=localhost
database=asterisk
EOF

chmod 600 ~/.my.cnf

Enter fullscreen mode Exit fullscreen mode

  1. Now scripts work without -u and -p flags:
mysql -e "SELECT COUNT(*) FROM vicidial_log;"

Enter fullscreen mode Exit fullscreen mode

Recording Files Not Found Despite recording_id in Database

Symptom: recording_id populated but actual audio files missing

Reason: Recordings stored in separate location, deleted by cleanup script, or disabled

Solution:

  1. Check where recordings are stored:
grep -r "monitor_format" /etc/asterisk/*.conf | grep -v "^#"
grep -r "recording" /etc/asterisk/*.conf | grep -v "^#"

Enter fullscreen mode Exit fullscreen mode

  1. Common locations:
ls -lah /var/spool/asterisk/monitor/
ls -lah /var/spool/asterisk/vicidial/recordings/

Enter fullscreen mode Exit fullscreen mode

  1. Check ViciDial settings for recording retention:
mysql asterisk -e "SELECT variable_value FROM system_settings WHERE variable_name = 'recording_archive_days';"

Enter fullscreen mode Exit fullscreen mode

Summary

Asterisk CDR analysis transforms raw call data into actionable business intelligence. The techniques in this tutorial enable you to:

Operational improvements:

  • Identify and fix call quality issues within hours, not weeks
  • Optimize agent scheduling around peak conversion windows
  • Detect fraud and spam sources automatically
  • Verify compliance with recording and retention regulations

Performance insights:

  • Benchmark agent and campaign performance objectively
  • Correlate talk time, hold time, and conversions for each agent
  • Track FCR (First Call Resolution) to identify training gaps
  • Monitor real-time metrics with custom dashboards

Cost optimization:

  • Eliminate unprofitable campaigns and time windows
  • Reduce waste on unreachable numbers and DNC violations
  • Optimize trunk routing and carrier selection based on call success rates

Key takeaways:

  1. Start with the raw data: vicidial_log and vicidial_closer_log contain everything you need
  2. Use database views: Create reusable views to simplify repeated analysis
  3. Establish baselines: Know your historical performance before chasing improvements
  4. Automate reporting: Scheduled queries and emails keep stakeholders aligned
  5. Monitor in real-time: Catch problems while they're happening, not in historical reports
  6. Index aggressively: Database performance degrades quickly without proper indexes
  7. Document your queries: Future you will thank present you

The Asterisk CDR system is powerful but requires intentional design and discipline. Start with the core queries provided here, customize them to your specific campaigns and metrics, and gradually build a comprehensive reporting infrastructure. Your operations team and finance department will demand this data sooner or later—better to build it on your schedule.