Developing a PSR-3 Logging Module for 1C-Bitrix
Investigating a production incident without proper logs is painful. The standard \Bitrix\Main\Diag\Debug::writeToFile() writes to a flat file with no structure, no levels, and no rotation. After a week of operation, the file reaches 2 GB, making analysis impossible and finding the right record a nightmare. We offer a logging module that solves this problem systematically. It writes structured records to the database, uses severity levels, tags, context, rotation, and search via the admin interface. This reduces error search time from hours to minutes. Get a consultation on implementing the logging module in your project.
Why the standard Bitrix logger is not suitable
Debug::writeToFile() mixes all events—debug, informational, and critical—into one file. No rotation—the file grows to tens of gigabytes. No search—you have to use grep on a huge file. No levels—you cannot filter only errors. This turns debugging into guesswork. Our module solves these shortcomings out of the box: structured records in ORM, PSR-3 levels, channel separation, rotation, and search.
How the logging module speeds up debugging
The module implements PSR-3, the industry standard for logging. You get a unified interface for all system components. The time to find a specific error is reduced tenfold compared to a flat file. Alerts in Telegram/Slack for critical errors allow instant reaction.
Architecture
The module vendor.logger implements the PSR-3 interface (Psr\Log\LoggerInterface), allowing it to connect to any libraries that support PSR-3: Guzzle, Doctrine, Symfony.
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): similar 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 synchronization: product not found', ['xml_id' => 'ABC-123']); Handlers
Database writes are 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, max N files)
- SlackHandler — sends critical/emergency to Slack channel via Webhook
- TelegramHandler — sends alerts to a Telegram bot for 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"} ] } | Handler | Purpose | Minimum level |
|---|---|---|
| DatabaseHandler | Long-term storage | debug |
| FileHandler | Quick access to recent logs | debug |
| SlackHandler | Critical error alerts | critical |
| TelegramHandler | Instant notifications | error |
Rotation and archiving
The rotation agent runs once a day:
// Records older than 30 days — move to b_vendor_log_archive // Records older than 90 days in archive — delete // Parameters configurable in module admin interface \Vendor\Logger\RotationAgent::run(); Transfer is done in batches of 1000 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; // Standard Bitrix handler also 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(), ]); }); Admin interface
Log search is the primary debugging tool:
- Filter by level, channel, date, IP, user, message text
- Context search (JSON field via PostgreSQL
@>or LIKE on serialized) - Detailed record view: full context, stack trace, request headers
- Statistics: top errors per period, dynamics by level, breakdown by channel
- Channel and handler configuration management
What is included in module development?
- Designing ORM table structures for your tasks
- Implementing a PSR-3-compatible logger
- Configuring handlers (DB, file, Slack, Telegram)
- Developing rotation and archiving agents
- Integrating global PHP error interception
- Creating an admin interface with search and statistics
- Integrating with existing code (replacing
AddMessage2Logcalls) - Operation documentation and developer instructions
- Testing on a staging environment before production deployment
We guarantee the module works on a stable codebase without surprises. With over 10 years of Bitrix development experience and 40+ successfully delivered logging modules, we build solutions that perform predictably. In one recent project for an online retailer, replacing their flat-file logging with our module cut the average time to investigate critical errors from 2 hours to under 15 minutes. Learn more about PSR-3.
Common implementation mistakes
- Forgetting to configure rotation—archive tables can grow uncontrollably.
- Setting too high a minimum level for channels (e.g., only error)—losing debugging information.
- Not integrating Telegram/Slack alerts—finding out about errors only from clients.
- Using a single channel for all events—losing context.
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 |
| Admin interface, search | 2 days |
| Testing | 1 day |
Total: from 8 business days. Order a turnkey logging module—and forget about manual log parsing. Contact us for a project assessment.

