PHP
#bitrix#logging#php#file#json#rotation#helper

Логирование в файл: безопасный путь, JSON context, ротация

User helper для логирования в файл с безопасными путями, JSON-контекстом и простой ротацией логов по размеру или дате.

Как использовать

  1. Скопируйте нужный фрагмент кода.
  2. Вставьте в свой проект и при необходимости измените под задачу.
  3. Проверьте зависимости и окружение (версии, переменные).

User helper для безопасного логирования в файл с JSON-контекстом и ротацией. Используйте для отладки, мониторинга ошибок, аудита действий пользователей.

use Bitrix\Main\IO\File;
use Bitrix\Main\Application;

/**
 * Класс для логирования с ротацией
 */
class SafeFileLogger {
    private $logDir;
    private $logFile;
    private $maxFileSize; // в байтах
    private $maxFiles; // количество файлов для хранения
    
    public function __construct($logFile = 'app.log', $maxFileSize = 10485760, $maxFiles = 5) {
        // Безопасный путь: только внутри /local/logs/
        $this->logDir = $_SERVER['DOCUMENT_ROOT'] . '/local/logs/';
        $this->logFile = $this->logDir . basename($logFile); // basename для безопасности
        $this->maxFileSize = $maxFileSize; // 10MB по умолчанию
        $this->maxFiles = $maxFiles;
        
        // Создаём директорию если нет
        if (!is_dir($this->logDir)) {
            @mkdir($this->logDir, 0755, true);
        }
    }
    
    /**
     * Логирование с JSON-контекстом
     * @param string $level info|warning|error|debug
     * @param string $message
     * @param array $context Дополнительные данные
     */
    public function log($level, $message, array $context = []) {
        // Проверяем размер файла и ротируем если нужно
        $this->rotateIfNeeded();
        
        $data = [
            'timestamp' => date('Y-m-d H:i:s'),
            'level' => strtoupper($level),
            'message' => $message,
            'context' => $context,
            'request_uri' => $_SERVER['REQUEST_URI'] ?? '',
            'user_id' => isset($GLOBALS['USER']) ? $GLOBALS['USER']->GetID() : null,
            'ip' => $_SERVER['REMOTE_ADDR'] ?? ''
        ];
        
        $logLine = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
        
        File::putFileContents($this->logFile, $logLine, \Bitrix\Main\IO\File::APPEND);
    }
    
    /**
     * Ротация логов по размеру
     */
    private function rotateIfNeeded() {
        if (!file_exists($this->logFile)) {
            return;
        }
        
        $fileSize = filesize($this->logFile);
        if ($fileSize < $this->maxFileSize) {
            return;
        }
        
        // Переименовываем существующие файлы
        for ($i = $this->maxFiles - 1; $i >= 1; $i--) {
            $oldFile = $this->logFile . '.' . $i;
            $newFile = $this->logFile . '.' . ($i + 1);
            
            if (file_exists($oldFile)) {
                if ($i + 1 > $this->maxFiles) {
                    @unlink($oldFile); // Удаляем старый файл
                } else {
                    @rename($oldFile, $newFile);
                }
            }
        }
        
        // Текущий файл становится .1
        @rename($this->logFile, $this->logFile . '.1');
    }
    
    public function info($message, array $context = []) {
        $this->log('info', $message, $context);
    }
    
    public function error($message, array $context = []) {
        $this->log('error', $message, $context);
    }
    
    public function warning($message, array $context = []) {
        $this->log('warning', $message, $context);
    }
    
    public function debug($message, array $context = []) {
        if (defined('BX_DEBUG') && BX_DEBUG) {
            $this->log('debug', $message, $context);
        }
    }
}

Usage:

// Создать логгер
$logger = new SafeFileLogger('custom.log', 10485760, 5); // 10MB, 5 файлов

// Логирование
$logger->info('Order created', ['order_id' => 12345, 'user_id' => 10]);
$logger->error('Payment failed', ['order_id' => 12345, 'error' => 'Insufficient funds']);

// С контекстом
$logger->warning('Low stock', [
    'product_id' => 100,
    'current_quantity' => 5,
    'min_quantity' => 10
]);

Notes:

⚠️ Логи сохраняются в /local/logs/ для безопасности. Используйте .htaccess или nginx для ограничения доступа к этой папке. Ротация происходит автоматически при достижении максимального размера. Не логируйте пароли, токены и персональные данные. JSON формат удобен для парсинга логов инструментами типа ELK, Splunk.