Логирование в файл: безопасный путь, JSON context, ротация
User helper для логирования в файл с безопасными путями, JSON-контекстом и простой ротацией логов по размеру или дате.
Как использовать
- Скопируйте нужный фрагмент кода.
- Вставьте в свой проект и при необходимости измените под задачу.
- Проверьте зависимости и окружение (версии, переменные).
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.