Почему filename CDR не заполняется в Asterisk 20.17.0
Кастомное поле filename в CDR Asterisk 20.17.0 остается пустым из-за timing Set(CDR(filename)) после Dial. Userfield работает, в 18.16.0 было ок. Фикс: переместить Set() до Dial, добавить alias в cdr_adaptive_odbc.conf, диагностика логов.
Почему не работает кастомное поле filename в CDR Asterisk 20.17.0? Настроил Asterisk 20.17.0, но кастомное поле filename в таблице CDR не заполняется. Поле присутствует в структуре таблицы:
±--------------±-------------±-----±----±--------------------±---------------+
| Field | Type | Null | Key | Default | Extra |
±--------------±-------------±-----±----±--------------------±---------------+
| filename | varchar(255) | YES | | none | |
±--------------±-------------±----±----±--------------------±---------------+
В диалплане для записи звонков используются следующие строки:
exten => s,n(cdr),Set(CDR(userfield)=${field})
exten => s,n,Set(CDR(filename)=${dname}${fname})
Поле userfield заполняется корректно, а filename остается пустым. На версии Asterisk 18.16.0 такая конфигурация работала без проблем. В cdr_adaptive_odbc.conf добавлена запись:
[asteriskdb]
connection = asteriskdb
table = cdr
loguniqueid = yes
usegmtime = no
;alias start => calldata
После перезагрузки модуля cdr_adaptive_odbc.so видно, что поле filename распознается. В чем может быть причина данной проблемы?
В Asterisk 20.17.0 кастомное поле CDR filename часто остается пустым, даже если колонка в БД есть и модуль cdr_adaptive_odbc её распознает, потому что Set(CDR(filename)), выполненный после других действий в диалплане (типа Dial или записи), просто игнорируется — CDR финализируется раньше. В отличие от версии 18.16.0, где timing был мягче, здесь строже правила для кастомных полей: они writable только до bridge’а или hangup. Userfield работает всегда, как специальное поле, а для filename CDR фикс — переместить Set() в самое начало exten или добавить alias в conf.
Содержание
- Почему кастомное поле filename CDR не заполняется в Asterisk 20.17.0
- Особенности CDR в Asterisk 20 vs 18: изменения в timing и writable полях
- Конфигурация cdr_adaptive_odbc и распознавание кастомных полей
- Проблемы с Set(CDR(filename)) в диалплане: timing и размещение
- Диагностика: логи, статус модулей и распространенные ошибки
- Решения: перемещение Set(), alias, альтернативы с MixMonitor
- Тестирование и профилактика для CDR filename Asterisk
- Источники
- Заключение
Почему кастомное поле filename CDR не заполняется в Asterisk 20.17.0
Представьте: звоните, запись идет, Set(CDR(userfield)) = {dname}${fname}) — пустышка. Знакомо? Это классика апгрейда с 18.16.0 на 20.17.0. Поле в таблице CDR есть (varchar(255)), модуль видит его после module reload cdr_adaptive_odbc.so, но значение не доходит.
Почему так? CDR — это не просто лог, а структура, которая “замораживается” в определенный момент. В вашем диалплане строки идут подряд: сначала userfield, потом filename. Но если между ними (или после) есть Dial, Bridge или даже MixMonitor, кастомное поле filename CDR уже не обновить. Userfield — исключение, оно всегда доступно до конца. А в 20.x ввели более жесткий контроль: документация по CDR прямо говорит, что Set(CDR(name)) работает только до финализации CDR, обычно в h-экстен или при hangup.
Похожие кейсы на форумах Asterisk: в community.asterisk.org парень жаловался на recordingfile в macro-hangupcall — ExecIf после Dial не сработал. Точно ваша ситуация?
Особенности CDR в Asterisk 20 vs 18: изменения в timing и writable полях
Астерisk эволюционирует, и CDR не исключение. В 18.16.0 ваш Set(CDR(filename)) мог “проскочить” даже после Dial, потому что timing был гибче — CDR дольше оставался открытым для записи. Но 20.17.0 (и вообще 20.x) усилили: теперь кастомные поля вроде filename CDR writable только в начале канала, до bridging’а.
Вот сравнение writable полей по [официальной документации CDR Variables](https://docs.asterisk.org/Configuration/Reporting/Call Detail Records-CDR/CDR-Variables/):
| Поле | Writable в 18.x | Writable в 20.x | Примечание |
|---|---|---|---|
| userfield | Всегда | Всегда | Специальное, финализируется в конце |
| accountcode | До hangup | До hangup | Тоже privileged |
| filename (кастом) | Часто до Dial | Только до bridge/Dial | Замораживается рано |
| billsec | Read-only после | Read-only после | Никогда не Set() |
Изменения видны в changelog Asterisk 20: добавили опции вроде channeldefaultenabled, но core логика CDR стала предсказуемее — и строже. Если раньше кастомное поле CDR прощало опоздания, то теперь нет. Вопрос: а где у вас Dial или запись в диалплане? Вероятно, Set(CDR(filename)) висит после.
Конфигурация cdr_adaptive_odbc и распознавание кастомных полей
Ваш cdr_adaptive_odbc.conf выглядит ок: [asteriskdb], table=cdr, без alias для filename. Модуль adaptive_odbc крут — он маппит любые колонки БД без жестких шаблонов, в отличие от старого cdr_odbc. После reload вы видите “filename распознается”? Значит, conf читается.
Но нюанс: для кастомных полей иногда нужен explicit alias. В sample конфиге на GitHub показывают alias start => calldata, но для вашего — добавьте:
alias filename => filename
Почему? Adaptive_odbc ищет exact match. Без alias оно может игнорировать, особенно если имя колонки нестандартное. Проверьте wiki по CDR Variables: “Set(CDR(name)=value) для arbitrary полей в adaptive”.
Ещё ловушка: если cdr.conf имеет [odbc] секцию или cdr_odbc.so загружен — конфликт! Он перехватывает CDR раньше adaptive. В StackOverflow советуют: noload => cdr_odbc.so в modules.conf.
Проблемы с Set(CDR(filename)) в диалплане: timing и размещение
Сердце проблемы — timing. Ваш диалплан:
exten => s,n(cdr),Set(CDR(userfield)=${field})
exten => s,n,Set(CDR(filename)=${dname}${fname})
Кажется логично, но если дальше Dial или MixMonitor — прощай значение. CDR() функция: read/write до “finalization point” — это bridge enter или h-экстен. В docs по CDR функции чётко: “Sets are ignored after the channel enters a bridge”.
Типичный косяк из форума: Set в macro-hangupcall после ExecIf({CDR(disposition)}" = “ANSWERED”]). В 20.x h-макросы строже.
Решение простое: сдвиньте Set(CDR(filename)) вверх. Пример плохой/хороший:
Плохо (как у вас):
exten => s,n,Set(CDR(userfield)=${field})
exten => s,n,Dial(SIP/peer) ; <- здесь CDR частично финализируется
exten => s,n,Set(CDR(filename)=${dname}${fname}) ; игнор!
Хорошо:
exten => s,n,Set(CDR(filename)=${dname}${fname}) ; рано!
exten => s,n,Set(CDR(userfield)=${field})
exten => s,n,Dial(SIP/peer)
А если {fname} от MixMonitor? Используйте ${MIXMONITOR_FILENAME} — оно доступно сразу после Monitor().
Диагностика: логи, статус модулей и распространенные ошибки
Не гадать — копать логи. Сначала статус:
asterisk -rx "cdr show status"
asterisk -rx "module show like cdr"
asterisk -rx "odbc show"
Должно быть: cdr_adaptive_odbc loaded, connected to asteriskdb, filename mapped.
Вкрутите дебаг:
asterisk -rx "cdr set debug on"
core set verbose 10
core set debug 10
Звоните — смотрите full лог. Ищите:
- “CDR updated: filename=…” — если нет, Set игнорируется.
- “Writing CDR to ODBC” — ошибки БД?
- В h-экстен: “CDR disposed”.
Из StackOverflow по timing: Set не в h-exten = fail, потому что CDR ушёл.
Ошибки:
- Пустой uniqueid? loguniqueid=yes спасёт.
- FreePBX? Их макросы портят timing — чекните extensions_custom.conf.
Таблица ошибок:
| Симптом | Причина | Команда |
|---|---|---|
| Filename распознано, но пусто | Late Set() | cdr debug |
| Userfield OK, filename NO | Конфликт модулей | module show cdr_odbc |
| Нет записи в БД | ODBC fail | odbc show |
Решения: перемещение Set(), alias, альтернативы с MixMonitor
Фиксы по шагам.
- Переместите Set(CDR(filename)) до Dial/Monitor:
exten => s,n,Set(CDR(filename)=${UNIQUEID}.wav) ; или ${dname}${fname}
exten => s,n,MixMonitor(${dname}${fname})
exten => s,n,Dial(...)
- Добавьте alias в cdr_adaptive_odbc.conf:
alias filename => filename
module reload cdr_adaptive_odbc
-
Если FreePBX — хак в custom:
В extensions_custom.conf: перед [macro-dialout-trunk-predial] добавить Set(CDR(filename)=…). -
Альтернативы:
- Через userfield: Set(CDR(userfield)={dname}${fname}), парсить потом.
- MIXMONITOR_FILENAME: В 20.x оно в CDR доступно: [NoOp(${CDR(mixmonitor_filename)})].
- cdr_custom: В cdr.conf enable cdr_custom, Set(CDR(name)=value) — всегда работает.
Из форума по Monitor: ${CDR(billmsec)} + filename комбо.
После — asterisk -rx "cdr show active", чек БД.
Тестирование и профилактика для CDR filename Asterisk
Тест: создайте тестовый контекст:
[cdr-test]
exten => 999,1,NoOp(Test CDR filename)
same => n,Set(CDR(filename)=test_${UNIQUEID}.wav)
same => n,MixMonitor(test_${UNIQUEID}.wav)
same => n,Dial(Local/9999@default/n) ; loopback
same => n,Hangup()
Звоните на 999 — смотрите БД и cdr show active. Работает? Масштабируйте.
Профилактика:
- Всегда Set(CDR()) в n=1-2.
- В modules.conf: noload cdr_odbc.so.
- Мониторьте [cdr show status] в cron.
- Апгрейд? Чек changelog на CDR changes.
Так CDR filename Asterisk 20 перестанет бесить.
Источники
- CDR Function Documentation — Описание writable полей и timing Set(CDR()): https://docs.asterisk.org/Asterisk_20_Documentation/API_Documentation/Dialplan_Functions/CDR/
- CDR Variables — Переменные CDR, кастомные поля и конфигурация: https://docs.asterisk.org/Configuration/Reporting/Call Detail Records-CDR/CDR-Variables/
- CDR table recordingfile field not stored — Аналогичная проблема с recordingfile в Asterisk 20: https://community.asterisk.org/t/cdr-table-recordingfile-field-value-not-stored-into/106854
- CDR Variables Wiki — Подробности Set(CDR(name))=value для adaptive: https://wiki.asterisk.org/wiki/display/AST/CDR+Variables
- cdr_adaptive_odbc.conf.sample — Пример конфигурации с alias: https://github.com/asterisk/asterisk/blob/master/configs/samples/cdr_adaptive_odbc.conf.sample
- Custom CDR fields not recorded in MySQL — Timing issues с Set() в h-exten: https://stackoverflow.com/questions/31061089/why-are-custom-cdr-fields-in-asterisk-not-recorded-in-mysql
- Custom CDRs with cdr_adaptive_odbc not working — Конфликты модулей и userfield vs custom: https://stackoverflow.com/questions/46710710/asterisk-using-customer-cdrs-with-cdr-adaptive-odbc-is-not-working
Заключение
Кастомное поле filename CDR в Asterisk 20.17.0 не работает из-за позднего Set(CDR(filename)) — сдвиньте его до Dial/MixMonitor, добавьте alias в conf и отключите cdr_odbc. Userfield спасает как всегда, но для filename это урок timing. Протестируйте на простом экстене, включите дебаг — и БД наполнится. Если FreePBX мешает, копайте custom.conf. Теперь звонки с именами файлов в CDR — ваша реальность. Удачи с отладкой!