Overview
Tempest provides a logging implementation built on top of Monolog that follows PSR-3 and the RFC 5424 specification. This gives you access to eight standard log levels and the ability to send log messages to multiple destinations simultaneously.
The system supports file logging, Slack integration, system logs, and custom channels. You can configure different loggers for different parts of your application using Tempest's tagged singletons feature.
Writing logs
To start logging messages, you may inject the Logger interface in any class. By default, log messages will be written to a daily rotating log file stored in .tempest/logs. This may be customized by providing a different logging configuration.
use Tempest\Log\Logger; final readonly class UserService { public function __construct( private Logger $logger, ) {} }
Tempest supports all eight levels described in the RFC 5424 specification. It is possible to configure channels to only log messages at or above a certain level.
$logger->emergency('System is unusable'); $logger->alert('Action required immediately'); $logger->critical('Important, unexpected error'); $logger->error('Runtime error that should be monitored'); $logger->warning('Exceptional occurrence that is not an error'); $logger->notice('Uncommon event'); $logger->info('Miscellaneous event'); $logger->debug('Detailed debug information');
Providing context
All log methods accept an optional context array for additional information. This data is formatted as JSON and included with your log message:
$logger->error('Order processing failed', context: [ 'user_id' => $order->userId, 'order_id' => $order->id, 'total_amount' => $order->total, 'payment_method' => $order->paymentMethod, 'error_code' => $exception->getCode(), 'error_message' => $exception->getMessage(), ]);
Configuration
By default, Tempest uses a daily rotating log configuration that creates a new log file each day and retains up to 31 files:
use Tempest\Log\Config\DailyLogConfig; use Tempest; return new DailyLogConfig( path: Tempest\internal_storage_path('logs', 'tempest.log'), maxFiles: Tempest\env('LOG_MAX_FILES', default: 31) );
To configure a different logging channel, you may create a logging.config.php file anywhere and return one of the available configuration classes.
Specifying a minimum log level
Every configuration class and log channel accept a minimumLogLevel property, which defines the lowest severity level that will be logged. Messages below this level will be ignored.
use Tempest\Log\Config\MultipleChannelsLogConfig; use Tempest\Log\Channels\DailyLogChannel; use Tempest\Log\Channels\SlackLogChannel; use Tempest; return new MultipleChannelsLogConfig( channels: [ new DailyLogChannel( path: Tempest\internal_storage_path('logs', 'tempest.log'), maxFiles: Tempest\env('LOG_MAX_FILES', default: 31), minimumLogLevel: LogLevel::DEBUG, ), new SlackLogChannel( webhookUrl: Tempest\env('SLACK_LOGGING_WEBHOOK_URL'), channelId: '#alerts', minimumLogLevel: LogLevel::CRITICAL, ), ], prefix: null, );
Using multiple loggers
In situations where you would like to log different types of information to different places, you may create multiple tagged configurations to create separate loggers for different purposes.
For instance, you could have a logger dedicated to critical alerts, while each of your application's module have its own logger:
use Tempest\Log\Config\SlackLogConfig; use Modules\Monitoring\Logging; use Tempest; return new SlackLogConfig( webhookUrl: Tempest\env('SLACK_LOGGING_WEBHOOK_URL'), channelId: '#alerts', minimumLogLevel: LogLevel::CRITICAL, tag: Logging::SLACK, );
use Tempest\Log\Config\DailyLogConfig; use Modules\Monitoring\Logging; use Tempest; return new DailyLogConfig( path: Tempest\internal_storage_path('logs', 'orders.log'), tag: Logging::ORDERS, );
Using this approach, you can inject the appropriate logger using tagged singletons. This gives you the flexibility to customize logging behavior in different parts of your application.
use Tempest\Log\Logger; final readonly class ProcessOrder { public function __construct( #[Tag(Logging::ORDERS)] private Logger $logger, ) {} public function __invoke(Order $order): void { $this->logger->info('Processing new order', ['order' => $order]); // ... } }
Available configurations and channels
Tempest provides a few log channels that correspond to common logging needs:
AppendLogChannel— append all messages to a single file without rotation,DailyLogChannel— create a new file each day and remove old files automatically,WeeklyLogChannel— create a new file each week and remove old files automatically,SlackLogChannel— send messages to a Slack channel via webhook,SysLogChannel— write messages to the system log.
As a convenient abstraction, a configuration class for each channel is provided:
These configuration classes also accept a channels property, which allows for providing multiple channels for a single logger. Alternatively, you may use the MultipleChannelsLogConfig configuration class to achieve the same result more explicitly.
Debugging
Tempest includes several global functions for debugging. Typically, these functions are for quick debugging and should not be committed to production.
ll()— writes values to the debug log without displaying them,lw()(alsodump()) — logs values and displays them,ld()(alsodd()) — logs values, displays them, and stops execution,le()— logs values and emits anItemsDebuggedevent.
Tailing debug logs
Debug logs are written with console formatting, so they can be tailed with syntax highlighting. You may use ./tempest tail:debug to monitor the debug log in real time.
By default, debug logs are cleared every time the tail:debug command is run. If you want to keep previous log entries, you may pass the --no-clear flag.
Configuring the debug log
By default, the debug log is written to .tempest/debug.log. This is configurable by creating a debug.config.php file that returns a DebugConfig with a different path:
use Tempest\Debug\DebugConfig; use Tempest; return new DebugConfig( logPath: Tempest\internal_storage_path('logs', 'debug.log') );