Агент обмена заказами отработал, а заказ не ушёл во внешнюю систему. echo и var_dump в AJAX дают пустой ответ, AddMessage2Log без LOG_FILENAME молчит, а Debug::writeToFile с токеном из Option через неделю превращается в 400 МБ лога с секретом в /upload/. Ниже - карта трёх веток на 1С-Битрикс: Управление сайтом (CMS, не Bitrix24): AddMessage2Log, Debug::writeToFile и Logger D7, плюс как отделить их от error.log и журнала событий.
Три инструмента - три задачи: AddMessage2Log (через main.Default) для быстрой legacy-отладки с LOG_FILENAME, Debug::writeToFile для AJAX и агентов без вывода на экран, Logger::create для постоянного логирования модуля через секцию loggers в .settings.php. Необработанные исключения пишет exception_handling в error.log - это отдельная ветка. Журнал событий админки - не отладочный файл. На production держите уровень ERROR, маскируйте токены из Option и отключайте временные вызовы после отладки.
Логирование в CMS - это не "включить одну галочку". У ядра есть штатный механизм ошибок, у админки - журнал бизнес-событий, а у разработчика - свои файлы для отладки интеграций. Если смешать всё в одну кучу, вы либо не увидите нужную строку, либо случайно утёкнёте API-ключ в публичную папку.
На практике интеграторы чаще всего путают три вещи: вывод ошибок PHP на экран (гайд по exception_handling в .settings.php), таблицу b_event_log из обработчиков событий и свой debug-файл в /upload/logs/. Эта статья - про третью группу: как писать в файл из кода модуля, агента или AJAX без поломки ответа.
Выберите инструмент: своя отладка, error.log или журнал событий
Делайте: перед добавлением логов определите контекст - веб-страница, AJAX, агент, cron, HTTP-запрос к API.
Не делайте: не пишите бизнес-отладку в журнал событий через EventLogger - он для audit, не для var_dump.
| Задача | Инструмент | Куда пишет |
|---|---|---|
| Необработанное исключение PHP | exception_handling | bitrix/modules/error.log |
| Быстрая отладка в legacy-коде | AddMessage2Log + LOG_FILENAME | Файл из константы |
| AJAX, агент, cron без echo | Debug::writeToFile | /upload/logs/ или свой путь |
| Постоянный лог модуля | Logger::create + loggers | Путь из .settings.php |
| Audit "кто изменил запись" | EventLogger / b_event_log | Журнал в админке |
Для фонового агента, который крутится без браузера, смотрите также гайд по CAgent в своём модуле: echo там бессмысленен, а лог - единственный способ увидеть, что произошло внутри run().
Настройте AddMessage2Log и LOG_FILENAME, если legacy уже в проекте
Делайте: объявите константу до первого вызова - в dbconn.php или /local/php_interface/init.php:
define('LOG_FILENAME', $_SERVER['DOCUMENT_ROOT'] . '/upload/logs/custom.log');
AddMessage2Log(['order_id' => 42, 'step' => 'sync'], 'my_module');
Не делайте: не оставляйте AddMessage2Log на production без необходимости - с D7 функция идёт через фабрику логгеров с id main.Default, и её можно переопределить в секции loggers .settings.php.
Типичная ошибка: если LOG_FILENAME не задан, AddMessage2Log «молчит» — файл не создаётся, и логирование фактически не работает. Это самая частая причина «я вызвал, а записи нет». Параметры traceDepth и ShowArgs добавляют стек вызовов — удобно на staging, шумно на бою.
Используйте Debug::writeToFile в AJAX, агентах и cron
Делайте: для контекстов без HTML-вывода пишите в файл через D7-класс Debug (доступен с main 12.0.7):
use Bitrix\Main\Diag\Debug;
Debug::writeToFile($arOrder, 'order_sync', '/upload/logs/sync.log');
Debug::startTimeLabel('agent');
// ... код агента ...
Debug::endTimeLabel('agent');
Не делайте: не кладите логи в публичный /upload/ без защиты - добавьте .htaccess с deny или вынесите путь за document root на VPS, где /logs/ не отдаётся веб-сервером.
writeToFile делает print_r в файл, dumpToFile - аналог var_dump. Для замера длительности агента удобен startTimeLabel - результат попадёт в тот же лог. Workflow отладки агента: зарегистрировать агент → добавить writeToFile в начало и конец run() → проверить файл на staging → убрать вызовы перед выкладкой на prod.
Настройте Logger D7 в .settings.php для модуля
Делайте: заведите id логгера в секции loggers и вызывайте фабрику в коде модуля:
// .settings.php
'loggers' => [
'value' => [
'vendor.sync' => [
'className' => \Bitrix\Main\Diag\FileLogger::class,
'constructor' => [
$_SERVER['DOCUMENT_ROOT'] . '/upload/logs/vendor_sync.log',
1048576,
],
'level' => 'ERROR',
],
],
],
// в классе модуля
$logger = \Bitrix\Main\Diag\Logger::create('vendor.sync');
$logger->info('Order exported', ['id' => $orderId]);
Не делайте: не ставьте level DEBUG на production - FileLogger по умолчанию ротирует файл при 1 МБ, но при DEBUG диск всё равно заполнится быстрее, чем вы успеете заметить.
PSR-3 (общий стандарт логов в PHP) даёт уровни emergency…debug. SysLogger пишет в syslog хостинга, EventLogger - в b_event_log. Для модуля с интеграцией почти всегда нужен FileLogger. HttpClient использует отдельный id main.HttpClient в той же секции loggers - удобно снимать заголовки и тело ответа API только на staging. С версии ядра 25.300.0 доступен JsonLinesFormatter - он пишет построчный JSON из массива context, игнорируя текст сообщения; полезно для парсинга, но неожиданно, если ждёте строку в логе.
Разделите exception_handling и свои логи
Делайте: оставьте exception_handling для падений ядра - по умолчанию путь bitrix/modules/error.log, log_size около 1 МБ. Бизнес-отладку ("заказ на шаге 3 из 5") пишите в свой FileLogger.
Не делайте: не дублируйте настройку debug на экран из статьи про вывод ошибок в settings.php - это про экран, а не про файловые логи модуля.
Если в error.log сотни строк в минуту — типичная проблема необработанного исключения в цикле, а не нехватка writeToFile. Сначала найдите источник падения, а не добавляйте ещё один лог в hot-path. SQL-запросы ORM снимайте через getLastQuery только на staging — подробности в том же материале про exception_handling.
Замаскируйте секреты и отключите отладку после релиза
Делайте: перед записью в лог прогоняйте массив через маскирование - токены и пароли из Option API модуля не должны попадать в файл открытым текстом:
$ctx = ['token' => Option::get('vendor.sync', 'api_token')];
$ctx['token'] = '***';
Debug::writeToFile($ctx, 'auth', '/upload/logs/sync.log');
Не делайте: не логируйте полный $arOrder с платёжными полями на боевом сервере - это PII и риск утечки.
Чек-лист перед выкладкой на production:
- Убрать все временные AddMessage2Log и Debug::writeToFile из hot-path.
- Понизить уровень Logger до ERROR или WARNING.
- Проверить, что /upload/logs/ закрыт от HTTP или лог лежит вне webroot.
- Убедиться, что ротация FileLogger включена (maxLogSize не равен 0).
- Пройтись по Option-модулю: нет ли секретов в уже созданных .log на диске.
- На staging повторить сценарий агента и убедиться, что запись читаема.
Красное правило: токен в логе = утечка. Даже на тестовом стенде привыкайте маскировать - иначе тот же файл уедет на prod вместе с дампом базы.
Факт-чек: автор Максим Молков, разработка 1С-Битрикс. Технические источники: документация логгеров Bitrix Framework, справка AddMessage2Log, API Debug D7, конфигурация .settings.php (проверено 2026-07-25).
Нужна помощь с настройкой логов на staging или аудитом утечек в /upload/logs/? Обсудим проект - разберём карту инструментов под ваш модуль.
Частые вопросы
Чем AddMessage2Log отличается от Debug::writeToFile?
AddMessage2Log - legacy-функция, требует LOG_FILENAME и пишет через логгер main.Default. Debug::writeToFile - D7-метод для произвольного файла без константы, удобен в AJAX и агентах. Для нового модуля предпочтительнее Logger::create с записью в .settings.php.
Где лежит error.log в Битрикс?
По умолчанию bitrix/modules/error.log относительно корня сайта. Точный путь задаётся в секции exception_handling файла .settings.php (ключ log.file). Это не ваш кастомный лог модуля.
Как включить логирование только на dev?
Объявите константу окружения в init.php и оборачивайте вызовы: if (defined('BX_DEBUG') && BX_DEBUG) { Debug::writeToFile(...); }. На production BX_DEBUG не определяйте, уровень Logger держите ERROR.
Можно ли логировать SQL ORM без echo?
Да, на staging после запроса вызовите getLastQuery() и запишите строку через Debug::writeToFile или Logger. На production отключите - запросы могут содержать персональные данные.
Чем Logger D7 лучше file_put_contents в init.php?
Logger даёт уровни, ротацию FileLogger, единый id в .settings.php и совместимость с PSR-3. file_put_contents в init.php не ротируется, не фильтрует уровни и размазывает логику по bootstrap-файлам.
Почему AddMessage2Log не пишет в файл?
Проверьте define LOG_FILENAME до вызова, права на каталог и что main.Default в loggers не перенаправлен в несуществующий путь. Без константы функция завершается без создания файла.