Administrator
发布于 2026-11-12 / 2 阅读
0
0

FreeSWITCH Configuration Lab 15 | Writing and Correlating Call Records

English
中文

Dial a test extension from 7101, let FreeSWITCH ring 7102, and look for two records after hangup. Each record should carry its own channel UUID. The business call ID should be shared. Repeat the call without answering to see how timestamps and disposition differ.

The lab uses the vanilla FreeSWITCH v1.11.3 layout as its reference. Start with a private test node where 7101 and 7102 share a directory domain and 7101 uses the default XML context. The CSV module settings are global. On a node with an existing collector, identify its current file and column contract before planning a migration.

Configure the CSV writer

Query the actual configuration directory with fs_cli -x 'global_getvar conf_dir'. Back up autoload_configs/cdr_csv.conf.xml before replacing it on the test node. The startup load list in autoload_configs/modules.conf.xml needs <load module="mod_cdr_csv"/>. If the module binary is missing, install the module for the deployed build. Prepare the log parent directory and make sure the FreeSWITCH service can traverse and write it. Give the reader only the access it needs.

<configuration name="cdr_csv.conf" description="Lab CSV CDR">
  <settings>
    <!-- 模块会在这个父目录后追加 cdr-csv。 -->
    <param name="log-base" value="/var/log/freeswitch-cdr-lab"/>
    <!-- 测试要对齐两条腿,分别生成记录。 -->
    <param name="legs" value="ab"/>
    <param name="default-template" value="lab_v1"/>
    <!-- 本次集中查看 Master.csv。 -->
    <param name="master-file-only" value="true"/>
    <param name="rotate-on-hup" value="true"/>
  </settings>
  <templates>
    <!-- 单行模板,顺序与下文的列定义一致。 -->
    <template name="lab_v1">"${business_call_id}","${cdr_lab_leg}","${uuid}","${caller_id_number}","${destination_number}","${start_stamp}","${answer_stamp}","${end_stamp}","${duration}","${billsec}","${hangup_cause}"</template>
  </templates>
</configuration>

The module appends cdr-csv to log-base. This lab writes to /var/log/freeswitch-cdr-lab/cdr-csv/Master.csv. Omitting the setting places that subdirectory under the FreeSWITCH log directory. Use legs=ab to record both the originating and originated legs. The vanilla template selects a; b selects the originated side. Extra attempts can add records. Group them by business ID for business reporting and account for overlapping answer intervals before computing time totals.

The vanilla configuration selects the example template. This lab selects lab_v1. A misspelled template can fall back to a built-in format and still produce a file. Treat the column order as an interface. Separate old files from new-format files and update the collector before switching.

With master-file-only=true, the module writes Master.csv only. When disabled, a channel with an accountcode can also appear in its account CSV, rendered with a matching template if present. Master.csv still uses the global template. With rotate-on-hup=true, rotation renames the file with a timestamp suffix and opens the original path again. The cdr_csv rotate command uses the same rotation logic. With the flag off, the module reopens without renaming. Configure the collector to finish reading the old file and then track the replacement.

Set the business ID before bridging

Create dialplan/default/15_cdr_lab.xml under the configuration root. The vanilla default context includes default/*.xml. On a customized installation, check the actual context for 7101 and confirm that 7715 is free and no earlier wildcard catches it.

<include>
  <extension name="cdr_lab_7715">
    <condition field="destination_number" expression="^7715$">
      <!-- 用入口腿 UUID 做演示 ID,在 bridge 前传给外拨腿。 -->
      <action application="export" data="business_call_id=${uuid}"/>
      <action application="set" data="cdr_lab_leg=a"/>
      <!-- 留出测试未接的时间,桥接结束后结束入口腿。 -->
      <action application="set" data="call_timeout=15"/>
      <action application="set" data="hangup_after_bridge=true"/>
      <!-- B 腿标记由拨号串写在新创建的通道上。 -->
      <action application="bridge" data="{cdr_lab_leg=b}user/7102@${domain_name}"/>
    </condition>
  </extension>
</include>

The lab uses the inbound channel UUID as a business ID. export sets it on the inbound channel and propagates it to the outgoing leg. The current leg gets an a marker and the dial string gives the new leg a b marker. The markers serve the lab only. The 15-second call_timeout keeps the unanswered test short. After a successful bridge, hangup_after_bridge ends the inbound leg when the bridge ends.

In production, assign a business ID at the business entry point and pass it explicitly wherever new channels are created. Validate this for queue callbacks, attended transfers, and calls spanning multiple nodes. The lab uses two SIP extensions. Agent state, queue membership, and dispatch outcomes are available from the corresponding queue events.

Apply the configuration and compare two calls

# 查看真实配置目录和模块状态。
fs_cli -x 'global_getvar conf_dir'
fs_cli -x 'module_exists mod_cdr_csv'

# 更新 XML。已加载时重载模块,首次加载时改用 load mod_cdr_csv。
fs_cli -x 'reloadxml'
fs_cli -x 'reload mod_cdr_csv'
fs_cli -x 'module_exists mod_cdr_csv'

# 测试通话结束后,检查新输出文件。
ls -l /var/log/freeswitch-cdr-lab/cdr-csv/Master.csv

reloadxml applies the dialplan XML. Reload the running CSV module to pick up its own settings, or use load mod_cdr_csv if it is not loaded. Let test calls drain first. A call ending while the module is unavailable can miss CSV logging.

From 7101, dial 7715, answer at 7102, then hang up. Repeat and let the 15-second attempt end without answering at 7102. Each leg is written in its reporting phase after hangup. Read the newest records after both legs finish.

The file has 11 columns. The first three hold the business ID, lab leg marker, and channel UUID. Columns four and five record the caller and destination as seen on that leg. The inbound destination is 7715 in this lab, while the route rings 7102. Columns six through eight contain creation, answer, and hangup times. duration is whole seconds from creation to hangup and billsec is whole seconds from answer to hangup. An unanswered leg records zero billable seconds, and a call answered and ended in the same second can also show zero. Read the answer timestamp alongside it. The last field is the hangup cause for that leg. An early answer by an IVR can precede agent pickup.

Run this read-only check with an account that can read the lab file. It prints the shared ID and each leg's UUID, answer seconds, and hangup cause. If you add free-form values, test how quotes and newlines are handled before ingesting them. The module expands values directly into the template. Quoting the placeholders leaves responsibility for escaping the values with the deployment.

uv run --python 3.10 python - <<'PY'
import csv
from collections import defaultdict
from pathlib import Path

# path 指向试验输出,groups 按业务 ID 收集本次拨号留下的通道行。
path = Path("/var/log/freeswitch-cdr-lab/cdr-csv/Master.csv")
groups = defaultdict(list)
try:
    with path.open(encoding="utf-8", newline="") as stream:
        for row in csv.reader(stream, strict=True):
            # 只核对测试路由的十一列和 A/B 标记。
            if len(row) == 11 and row[0] and row[1] in ("a", "b"):
                groups[row[0]].append((row[1], row[2], row[9], row[10]))
except (OSError, UnicodeError, csv.Error) as error:
    raise SystemExit(f"读取测试话单失败:{error}") from error

# 仅打印关联 ID、腿、通道 UUID、接听秒数和挂断原因。
for business_id, legs in groups.items():
    print(business_id, sorted(legs))
PY

For one answered direct bridge where both legs exist and finish, expect one a and one b under the same business ID with different UUIDs. For the unanswered test, check which legs were created and their actual causes. An unanswered B-leg that was created should show zero billable seconds. Record what was already in the file before the test or start with an empty output directory. These are expected results based on the published implementation, without a live FreeSWITCH run.

Diagnose missing records or mismatched IDs

  • No new records. Confirm that the call ended and the module is loaded. Check the effective directory, service permissions, free space, and log messages about creation or write errors.
  • The old columns persist. Confirm the configuration root and template name, then verify that the module was reloaded. Test in the new directory so old rows do not confuse the check.
  • A business ID is missing. Confirm that 7715 hit the lab route. A plain set only writes the current channel. When a transfer or queue creates another channel, pass the value at that actual origination point and retest.
  • The same call appears twice in reporting. Check whether the collector ingests both Master.csv and account files. Deduplicate by channel UUID on the single-node lab, then group by business ID. Retain the source node in multi-node collection for replay and correlation.

To roll back, stop using 7715, remove the lab XML, and restore the previous CSV configuration. Apply reloadxml and reload the previously running module with no test calls active. If the module was originally disabled, restore the startup load list and unload it on the test node. Archive the lab CSV separately, place a call through the original route, and verify the original directory, columns, and collector.

Sources


评论