Spiral offers spiral/logger
component that is compliant with the PSR-3 standard.
This component can be utilized to log various types of information, such as errors, warnings, and debugging messages,
which can assist in identifying and resolving issues within the application.
By default, the framework does not provide its own implementation, however, the spiral/monolog-bridge
component is
available, which fully integrates with the Seldaek/monolog package and offers
support for various powerful log handlers.
The framework makes it simple to configure these handlers, allowing for customization of log handling through the use of different channels.
To configure this component, it can be set up to your preference via a config file or a bootloader. The config file for
this component is typically located in app/config/monolog.php
. Through this file, you can select a default handler,
set a global logging level, and customize handlers and processors to meet your specific needs.
Here is an example of a config file:
use Monolog\Handler\ErrorLogHandler;
use Monolog\Handler\SyslogHandler;
use Monolog\Logger;
use Monolog\Processor\PsrLogMessageProcessor;
return [
/**
* -------------------------------------------------------------------------
* Default Monolog handler
* -------------------------------------------------------------------------
*/
'default' => env('MONOLOG_DEFAULT_CHANNEL', 'default'),
/**
* -------------------------------------------------------------------------
* Global logging level
* -------------------------------------------------------------------------
*
* Monolog supports the logging levels described by RFC 5424.
*
* @see https://seldaek.github.io/monolog/doc/01-usage.html#log-levels
*/
'globalLevel' => Logger::toMonologLevel(
env('MONOLOG_DEFAULT_LEVEL', \Monolog\Logger::DEBUG)
),
/**
* -------------------------------------------------------------------------
* Handlers
* -------------------------------------------------------------------------
*
* @see https://seldaek.github.io/monolog/doc/02-handlers-formatters-processors.html#handlers
*/
'handlers' => [
'default' => [
[
'class' => 'log.rotate',
'options' => [
'filename' => directory('runtime') . 'logs/app.log',
'level' => \Monolog\Logger::DEBUG,
],
],
],
'stderr' => [
ErrorLogHandler::class,
],
'stdout' => [
[
'class' => SyslogHandler::class,
'options' => [
'ident' => 'app',
'facility' => LOG_USER,
],
],
],
],
/**
* -------------------------------------------------------------------------
* Processors
* -------------------------------------------------------------------------
*
* Processors allows adding extra data for all records.
*
* @see https://seldaek.github.io/monolog/doc/02-handlers-formatters-processors.html#processors
*/
'processors' => [
'default' => [
[
'class' => PsrLogMessageProcessor::class,
'options' => [
'dateFormat' => 'Y-m-d\TH:i:s.uP',
],
],
],
],
];
Note
UseMONOLOG_DEFAULT_CHANNEL
env variable to specify the default handler that should be used in your application.
By default, the handler will format the log message using the following
structure [%datetime%] %level_name%: %message% %context%\n
.
If you want to change the log message structure, you can set up the MONOLOG_FORMAT
evn variable like in the example
below:
MONOLOG_FORMAT=[%datetime%] %level_name%: %message% %context%\n
See more
Read more about the available placeholders in the Monolog documentation.
In addition to configuring the logging component through a config file or bootloader, you can also register handlers via
the Spiral\Monolog\Bootloader\MonologBootloader
class.
namespace App\Application\Bootloader;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Monolog\Bootloader\MonologBootloader;
final class LoggingBootloader extends Bootloader
{
public function boot(MonologBootloader $monolog): void
{
$monolog->addHandler(
'my-channel',
$monolog->logRotate(directory('runtime') . 'logs/my-channel.log')
);
}
}
Warning
Don't forget to add this bootloader to the top of bootloaders list inapp/src/Application/Kernel.php
:
public function defineBootloaders(): array
{
return [
// ...
\App\Application\Bootloader\LoggingBootloader::class,
// ...
];
}
Read more about bootloaders in the Framework — Bootloaders section.
The RoadRunner bridge package provides Spiral\RoadRunnerBridge\Logger\Handler
handler for sending logs to
the RoadRunner app logger.
You just need to add Spiral\RoadRunnerBridge\Bootloader\LoggerBootloader
to the top of bootloaders list:
public function defineBootloaders(): array
{
return [
// ...
\Spiral\RoadRunnerBridge\Bootloader\LoggerBootloader::class,
// ...
];
}
Read more about bootloaders in the Framework — Bootloaders section.
Warning
Make sure that you have the spiral/roadrunner-bridge package installed. This package provides the necessary classes to integrate RoadRunner with Monolog.
And change the default channel to roadrunner
:
MONOLOG_DEFAULT_CHANNEL=roadrunner
In order to use the logging component, the framework utilizes the Psr\Log\LoggerInterface
class, which can be used to
log messages to the default channel.
use Psr\Log\LoggerInterface;
final class UserService
{
public function __construct(
private readonly LoggerInterface $logger
) {}
public function register(string $email, string $password): void
{
// Register user ...
$this->logger->info('User has been registered', ['email' => $email]);
}
}
There are a few ways to get a logger instance with a specific channel:
Spiral\Logger\LogsInterface
.Spiral\Logger\Attribute\LoggerChannel
on a Psr\Log\LoggerInterface
parameter during autowiring.use Psr\Log\LoggerInterface;
use Spiral\Logger\LogsInterface;
final class UserService
{
private readonly LoggerInterface $logger;
public function __construct(LogsInterface $logs)
{
$this->logger = $logs->channel('my-channel');
}
public function register(string $email, string $password): void
{
// Register user ...
$this->logger->info('User has been registered', ['email' => $email]);
}
}
Spiral provides a convenient way to quickly assign a Logger to any class through the use of the
Spiral\Logger\Traits\LoggerTrait
trait. By simply including this trait in a class you can easily access a Logger
instance and log messages.
Warning
The channel name used for logging will be the class name by default. This trait enables you to log messages from any class without the need to explicitly inject the Logger in the constructor.
use Spiral\Logger\Traits\LoggerTrait;
final class UserService
{
use LoggerTrait;
public function register(string $email, string $password): void
{
// Register user ...
$this->getLogger()->info('User has been registered', ['email' => $email]);
}
}
And assign a logger to it:
namespace App\Application\Bootloader;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Monolog\Bootloader\MonologBootloader;
final class LoggingBootloader extends Bootloader
{
// ...
public function boot(MonologBootloader $monolog): void
{
$monolog->addHandler(
UserService::class,
$monolog->logRotate(directory('runtime') . 'logs/user-dervice.log')
);
}
}
Warning
LoggerTrait only works inside the global IoC scope.
In some cases, you may want to log only specific log levels. For example, you may want to aggregate only application errors into a single log file.
To do this, you can subscribe to the default channel:
namespace App\Application\Bootloader;
use Monolog\Logger;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Monolog\Bootloader\MonologBootloader;
final class LoggingBootloader extends Bootloader
{
// ...
public function boot(MonologBootloader $monolog): void
{
$monolog->addHandler(
'default',
$monolog->logRotate(directory('runtime') . 'logs/errors.log', Logger::ERROR) // only ERROR and above
);
}
}