menu

Logging and Debugging

for Neos 5 and 7

Needs technical review

This part needs a technical review to be sure that all content is correct.

Logging

Be aware, that 'Logging' is programmatically a task of the underlying Flow framework ...

System Log

A logger instance for the default System Log can be injected and used for logging quite easily:

use Psr\Log\LoggerInterface;

/**
 * @var LoggerInterface
 */
protected $logger;

/**
 * @param LoggerInterface $logger
 * @return void
 */
public function injectLogger(LoggerInterface $logger)
{
        $this->logger = $logger;
}

The various methods can then be used to log according to the level:

$message = 'Whatever you want to log';
$context = [
	'anything' => 'you might need',
	'but can be' => 'left empty as well'
];

$this->logger->debug(string $message, array $context);
$this->logger->info(string $message, array $context);
$this->logger->notice(string $message, array $context);
$this->logger->warning(string $message, array $context);
$this->logger->error(string $message, array $context);
$this->logger->critical(string $message, array $context);
$this->logger->alert(string $message, array $context);

In other to be able to distinguish log entries, it is wise to send the some metadata along with the log entry:

use Neos\Flow\Log\Utility\LogEnvironment;

$logger->debug(
	'Some log message',
	LogEnvironment::fromMethodName(__METHOD__)
);

Other logs

While logging to the System Log is easily done, and probably the most common case, you might want to log to the Security Log.

For Neos 5.x (based on Flow 6.x) this can be done using the PsrSecurityLogerInterface – but this is deprecated as of Flow 6.0 already:

/**
 * @var \Neos\Flow\Log\PsrSecurityLoggerInterface
 */
protected $securityLogger;

/**
 * @param \Neos\Flow\Log\PsrSecurityLoggerInterface $securityLogger
 * @return void
 */
public function injectSecurityLogger(\Neos\Flow\Log\PsrSecurityLoggerInterface $securityLogger)
{
        $this->securityLogger = $securityLogger;
}

The proper, non-deprecated method for this would be the use of the logger factory:

/**
 * @var \Neos\Flow\Log\PsrLoggerFactoryInterface
 */
protected $loggerFactory;

/**
 * @var \Neos\Flow\Log\PsrSecurityLoggerInterface
 */
protected $securityLogger;

/**
 * @param \Neos\Flow\Log\PsrLoggerFactoryInterface $loggerFactory
 * @return void
 */
public function injectLoggerFactory(\Neos\Flow\Log\PsrLoggerFactoryInterface $loggerFactory)
{
        $this->securityLogger = $loggerFactory->get('securityLogger');
}

Flow comes with the following loggers defined by default:

  • systemLogger
  • securityLogger
  • sqlLogger
  • i18nLogger

With Neos 7.0 (based on Flow 7.0) the PsrSystemLoggerInterface and PsrSecurityLoggerInterface (that were deprecated with Flow 6.0) are removed, so either use the logger factory as explained above or use virtual objects to access loggers other than for the system log:

use Neos\Flow\Annotations as Flow;
use Psr\Log\LoggerInterface;

/**
 * @Flow\Inject(name="Neos.Flow:SystemLogger")
 * @var LoggerInterface
 */
protected $systemLogger;

Flow comes with the following virtual logger objects defined by default:

  • Neos.Flow:SystemLogger
  • Neos.Flow:SecurityLogger
  • Neos.Flow:SqlLogger
  • Neos.Flow:I18nLogger

Defining a custom logger

While logging to the available logs is usually fine, it might be desirable to have a separate log for your application. In order to be able to log to a separate file, a custom logger must be defined.

<?php
namespace Acme\Demo\Log;

use Psr\Log\LoggerInterface;

/**
 * Marker interface for my custom logger.
 *
 */
interface MyLoggerInterface extends LoggerInterface
{

}

The actual logger can extend Flow's Logger and must implement MyLoggerInterface

<?php
namespace Acme\Demo\Log;

class Logger extends \Neos\Flow\Log\Psr\Logger implements MyLoggerInterface
{

}

The settings are used when the logger is injected

Acme:
  Demo:
    log:
      myLogger:
        logger: Acme\Demo\Log\Logger
        backend: Neos\Flow\Log\Backend\FileBackend
        backendOptions:
          logFileURL: '%FLOW_PATH_DATA%Logs/My.log'
          createParentDirectories: TRUE
          severityThreshold: '%LOG_INFO%'
          maximumLogFileSize: 1048576
          logFilesToKeep: 1

In order to be able to inject to logger to your class, the constructor properties must be defined in Objects.yaml using the settings configured above

Acme\Demo\Log\MyLoggerInterface:
  scope: singleton
  factoryObjectName: Neos\Flow\Log\PsrLoggerFactory
  factoryMethodName: get
  arguments:
    1:
      value: 'MyLogger'
    2:
      setting: Acme.Demo.log.myLogger.logger
    3:
      setting: Acme.Demo.log.myLogger.backend
    4:
      setting: Acme.Demo.log.myLogger.backendOptions

Now, the logger can be injected to your class

/**
 * @var \Acme\Demo\Log\MyLoggerInterface
 */
protected $logger;

/**
 * @param \Acme\Demo\Log\MyLoggerInterface $myLogger
 * @return void
 */
public function injectMyLogger(\Acme\Demo\Log\MyLoggerInterface $myLogger)
{
    $this->logger = $myLogger;
}

Is it then available for use

$this->logger->info('My first log entry.');

The log file can be found in Data/Logs/My.log