1C-Bitrix Logging Module Development
Debugging a production incident without proper logs is a painful experience. The standard \Bitrix\Main\Diag\Debug::writeToFile() writes to a flat file with no structure, no severity levels, and no rotation. After a week of operation you end up with a 2 GB file that is impossible to analyse or search. The logging module solves this systematically: structured records in the database, severity levels, tags, context, rotation, and search via the administrative interface.
Architecture
The vendor.logger module implements the PSR-3 interface (Psr\Log\LoggerInterface), making it compatible with any library that supports PSR-3 (Guzzle, Doctrine, Symfony components).
ORM tables:
-
b_vendor_log_entry— log records: id, level (debug/info/notice/warning/error/critical/alert/emergency), channel, message, context (JSON), extra (JSON), created_at, user_id, request_uri, ip -
b_vendor_log_channel— channels: id, code, name, min_level, handlers (JSON), is_active -
b_vendor_log_archive— archived records (after rotation): same structure + archived_at
PSR-3 Implementation
class BitrixLogger extends \Psr\Log\AbstractLogger
{
private string $channel;
public function __construct(string $channel = 'app')
{
$this->channel = $channel;
}
public function log($level, $message, array $context = []): void
{
if (!$this->isLevelEnabled($level)) {
return;
}
$extra = [
'user_id' => $GLOBALS['USER']?->GetID(),
'request_uri' => $_SERVER['REQUEST_URI'] ?? '',
'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
];
LogEntryTable::add([
'LEVEL' => $level,
'CHANNEL' => $this->channel,
'MESSAGE' => $this->interpolate($message, $context),
'CONTEXT' => $context,
'EXTRA' => $extra,
]);
}
private function interpolate(string $message, array $context): string
{
$replace = [];
foreach ($context as $key => $val) {
$replace['{' . $key . '}'] = is_scalar($val) ? $val : json_encode($val, JSON_UNESCAPED_UNICODE);
}
return strtr($message, $replace);
}
}
Usage in Code
$logger = new \Vendor\Logger\BitrixLogger('payment');
$logger->info('Payment initiated', ['order_id' => 42, 'sum' => 1500.00, 'gateway' => 'sberbank']);
$logger->error('Payment gateway error', ['order_id' => 42, 'response' => $gatewayResponse]);
// Global logger via facade
\Vendor\Logger\Log::channel('sync')->warning('1C sync: product not found', ['xml_id' => 'ABC-123']);
Handlers
Writing to the database is not the only handler. The module supports multiple handlers per channel:
-
DatabaseHandler — writes to
b_vendor_log_entry(primary) - FileHandler — writes to a file with automatic rotation (daily, maximum N files)
- SlackHandler — sends critical/emergency messages to a Slack channel via Webhook
- TelegramHandler — sends alerts to a Telegram bot when level >= error
Channel configuration is stored in b_vendor_log_channel in the handlers field (JSON). Example:
{
"handlers": [
{"type": "database", "min_level": "debug"},
{"type": "telegram", "min_level": "error", "chat_id": "-100123456789"}
]
}
Rotation and Archiving
The rotation agent runs once per day:
// Records older than 30 days — moved to b_vendor_log_archive
// Records older than 90 days in the archive — deleted
// Parameters are configurable in the module's administrative interface
\Vendor\Logger\RotationAgent::run();
Transfer is performed in batches of 1,000 records to avoid long table locks.
PHP Error Interception
The module can register a global PHP error handler:
set_error_handler(function($errno, $errstr, $errfile, $errline) {
$level = ErrorLevelMapper::toLogLevel($errno);
\Vendor\Logger\Log::channel('php')->log($level, $errstr, [
'file' => $errfile,
'line' => $errline,
'errno' => $errno,
]);
return false; // Bitrix standard handler still fires
});
set_exception_handler(function(\Throwable $e) {
\Vendor\Logger\Log::channel('php')->critical($e->getMessage(), [
'exception' => get_class($e),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString(),
]);
});
Administrative Interface
Log search is the primary debugging tool:
- Filtering by level, channel, date, IP, user, and message text
- Search within context (JSON field via PostgreSQL
@>or LIKE on serialized data) - Detailed record view: full context, call stack, request headers
- Statistics: top errors for a period, dynamics by level, breakdown by channel
- Channel and handler configuration management
Development Timeline
| Stage | Duration |
|---|---|
| ORM tables, PSR-3 implementation | 1 day |
| DatabaseHandler, FileHandler | 1 day |
| Slack and Telegram alerts | 1 day |
| Rotation and archiving (agent) | 1 day |
| PHP error interception | 1 day |
| Administrative interface, search | 2 days |
| Testing | 1 day |
Total: 8 working days. Integration with external monitoring systems (Sentry, Graylog) — additional 1–2 days.







