Views: 1072
Last Modified: 18.01.2022

  Introducton

Core now have available loggers, implementing the PSR-3 interface:

  • base abstract class \Bitrix\Main\Diag\Logger, implementing the PSR-3 interface;
  • file logger: \Bitrix\Main\Diag\FileLogger;
  • syslog logger: \Bitrix\Main\Diag\SysLogger.

Loggers are used by the log formatter \Bitrix\Main\Diag\LogFormatter that replaces placeholders as per PSR-3.

Note: Library is available starting from main version 21.900.0.

  Logger Interface

\Psr\Log\LoggerInterface interface is quite simple, representing a set of logging features that support levels of logging. Levels are set by constants \Psr\Log\LogLevel::*.

interface LoggerInterface
{
    public function emergency($message, array $context = array());
    public function alert($message, array $context = array());
    public function critical($message, array $context = array());
    public function error($message, array $context = array());
    public function warning($message, array $context = array());
    public function notice($message, array $context = array());
    public function info($message, array $context = array());
    public function debug($message, array $context = array());
    public function log($level, $message, array $context = array());
}

Message can contain {placeholders}, replaced by data from the $context associative array.

Also, a useful interface \Psr\Log\LoggerAwareInterface is available if you want to notify that your object is ready to accept the PSR-3 logger:

interface LoggerAwareInterface
{
    public function setLogger(LoggerInterface $logger);
}

  Loggers in Bitrix24

While implementing PSR-3, loggers in Bitrix24 have expanded functionality. Now you can:

  • set a minimum logging level, below which logger doesn't show anything,
  • set formatter.

File logger \Bitrix\Main\Diag\FileLogger can write messages into file, specified in constructor. When log size exceeds the set maximum, system performs a single-time log file rotation. Null - no rotation is needed. Default size: 1 Mb.

$logger = new Diag\FileLogger($logFile, $maxLogSize);
$logget->setLevel(\Psr\Log\LogLevel::ERROR);
// prints into log
$logger->error($message, $context);
// Doesn't print into log
$logger->debug($message, $context);

Syslog logger \Bitrix\Main\Diag\SysLogger is an addin to the function php syslog function. Constructor receives parameters, used by the function openlog.

$logger = new Diag\SysLogger('Bitrix WAF', LOG_ODELAY, $facility);
$logger->warning($message);

File logger uses the function AddMessage2Log and class \Bitrix\Main\Diag\FileExceptionHandlerLog, as well as logging in the Proactive protection module (security).

  Message formatting

Message formatter can be set into the logger. By default, uses the formatter \Bitrix\Main\Diag\LogFormatter, implementing the interface \Bitrix\Main\Diag\LogFormatterInterface:

interface LogFormatterInterface
{
	public function format($message, array $context = []): string;
}

Formatter constructor receives parameters $showArguments = false, $argMaxChars = 30 (show argument values in trace, maximum argument length).

$logger = new Main\Diag\FileLogger(LOG_FILENAME, 0);
$formatter = new Main\Diag\LogFormatter($showArgs);
$logger->setFormatter($formatter);

Formatter main task is to insert values into message placeholders from context array. Formatter can process specific placeholders:

  • {date} - current time * ;
  • {host} - HTTP host * ;
  • {exception} - exception object (\Throwable);
  • {trace} - backtrace array;
  • {delimiter} - message delimiter * .

* - optional to pass in context array, substituted automatically.

$logger->debug(
    "{date} - {host}\n{trace}{delimiter}\n", 
    [
        'trace' => Diag\Helper::getBackTrace(6, DEBUG_BACKTRACE_IGNORE_ARGS, 3)
    ]
);

Formatter formats values from context array into convenient format depending on the value type. Accepts strings, arrays, objects.

Use

Standard object format can get a logger that supports the interface \Psr\Log\LoggerAwareInterface. Can use a corresponding trait:

use Bitrix\Main\Diag;
use Psr\Log;

class MyClass implements Log\LoggerAwareInterface
{
	use Log\LoggerAwareTrait;
	
	public function doSomething()
	{
	    if ($this->logger)
	    {
	        $this->logger->error('Error!');
	    }
	}
}

$object = new MyClass();
$logger = new Diag\FileLogger("/var/log/php/error.log");
$object->setLogger($logger);
$object->doSomething();

However, it's not convenient to change the code at the operational project to pass logger a desired object. Logger class provides an individual factory for this. The factory receives a logger string ID:

use Bitrix\Main\Diag;
use Psr\Log;

class MyClass implements Log\LoggerAwareInterface
{
	use Log\LoggerAwareTrait;
	
	public function doSomething()
	{
	    if ($logger = $this->getLogger())
	    {
	        $logger->error('Error!');
	    }
	}

	protected function getLogger()
	{
		if ($this->logger === null)
		{
			$logger = Diag\Logger::create('myClassLogger', [$this]);
			$this->setLogger($logger);
		}

		return $this->logger;
	}
}

Configuration

Root section for the .settings.php file indicates loggers in the loggers key. Description syntax matches with ServiceLocator settings. The difference is that service locator is a register and this file contains the factory config.

Additional parameters can be passed to constructor closure via the Diag\Logger::create('logger.id', [$this]) factory's second parameter. Parameters allows to flexibly enable logging depending on passed parameters, including calling the methods of the object itself.

Additionally, you can indicate minimal level of logging (level) and formatter. Formatter is searched in service locator by its ID.

// /bitrix/.settings.php
return [
	//...
	'services' => [
		'value' => [
			//...
			'formatter.Arguments' => [
				'className' => '\Bitrix\Main\Diag\LogFormatter',
				'constructorParams' => [true],
			],
		],
		'readonly' => true,
	]
	'loggers' => [
		'value' => [
			//...
			'main.HttpClient' => [
//				'className' => '\\Bitrix\\Main\\Diag\\FileLogger',
//				'constructorParams' => ['/home/bitrix/www/log.txt'],
//				'constructorParams' => function () { return ['/home/bitrix/www/log.txt']; },
				'constructor' => function (\Bitrix\Main\Web\HttpClient $http, $method, $url) { 
					$http->setDebugLevel(\Bitrix\Main\Web\HttpDebug::ALL);
					return new \Bitrix\Main\Diag\FileLogger('/home/bitrix/www/log.txt');
				},
				'level' => \Psr\Log\LogLevel::DEBUG,
				'formatter' => 'formatter.Arguments',
			],
		],
		'readonly' => true,
	],
	//...
];

Upon indicating closing constructor, its preferable to use the .settings_extra.php file, to avoid loosing code when saving settings from API.

There is \Psr\Log\NullLogger that can be installed to avoid writing if($logger) each time before calling logger. However, you need to consider, if extra message and context formatting work is worth it.

  Classes

List of classes supporting logger factory:

ClassLogger IDPassed parameters
\Bitrix\Main\Web\HttpClient main.HttpClient [$this, $this->queryMethod, $this->effectiveUrl]



Courses developed by Bitrix24