-
Notifications
You must be signed in to change notification settings - Fork 2
Logger
The Logger class is a singleton implementing the PSR-3 LoggerInterface.
It provides a unified way to:
- log messages to LogBar (runtime log viewer);
- persist warnings and errors into the database (
ErrorLog); - send email and Telegram notifications for severe events;
- register custom error / exception / shutdown handlers;
- optionally forward errors to Sentry and Insight Hub (Bugsnag).
The logger is designed to be safe in production and helpful in development, while keeping the integration small and predictable.
use Pair\Core\Logger;
$logger = Logger::getInstance();Logger::getInstance() always returns the same shared instance.
Pair uses numeric levels compatible with PSR-3 severity ordering:
| Constant | Value | Name |
|---|---|---|
Logger::EMERGENCY |
1 | emergency |
Logger::ALERT |
2 | alert |
Logger::CRITICAL |
3 | critical |
Logger::ERROR |
4 | error |
Logger::WARNING |
5 | warning |
Logger::NOTICE |
6 | notice |
Logger::INFO |
7 | info |
Logger::DEBUG |
8 | debug |
Lower number = more severe.
The Logger::LEVEL_NAMES constant maps numeric levels to their string names.
Every call ends up in LogBar:
-
debug,info,notice→LogBar::event(..., 'notice') -
warning→LogBar::event(..., 'warning') -
error,critical,alert,emergency→LogBar::event(..., 'error')
If the level is WARNING or worse (<= Logger::WARNING), the logger also:
- optionally snapshots the request into the DB (
ErrorLog); - optionally sends email notifications (if configured);
- optionally sends Telegram notifications (if configured).
Logger reads these values from Env during construction:
-
PAIR_LOGGER_DISABLED
If set totrue, the logger is disabled. -
PAIR_LOGGER_EMAIL_RECIPIENTS
Comma-separated emails. Example:ops@example.com,dev@example.com -
PAIR_LOGGER_EMAIL_THRESHOLD
Numeric level from 1 to 8. Default is4(error). -
TELEGRAM_BOT_TOKEN
Bot token used to send Telegram messages. -
PAIR_LOGGER_TELEGRAM_CHAT_IDS
Comma-separated chat IDs. Example:123456789,987654321 -
PAIR_LOGGER_TELEGRAM_THRESHOLD
Numeric level from 1 to 8. Default is4(error).
Additionally:
-
APP_DEBUGcontrols handler behavior (seeerrorHandler()). -
SENTRY_DSNenables forwarding errors to Sentry (when SDK functions exist).
Example .env:
APP_DEBUG=false
PAIR_LOGGER_DISABLED=false
PAIR_LOGGER_EMAIL_RECIPIENTS=ops@example.com,dev@example.com
PAIR_LOGGER_EMAIL_THRESHOLD=4
TELEGRAM_BOT_TOKEN=123456:ABCDEF_your_token_here
PAIR_LOGGER_TELEGRAM_CHAT_IDS=123456789,987654321
PAIR_LOGGER_TELEGRAM_THRESHOLD=3
SENTRY_DSN=https://public@sentry.example/1Email is sent only if:
- a mail transport has been configured (
$logger->useTransport(...)); -
emailRecipientsare set; -
level <= emailThreshold.
use Pair\Core\Logger;
$logger = Logger::getInstance();
$logger->useTransport('smtp', [
// The config array is passed to the transport constructor.
// Keys depend on the transport implementation.
'host' => 'smtp.example.com',
'port' => 587,
'username' => 'user',
'password' => 'secret',
'encryption' => 'tls',
'from' => ['no-reply@example.com' => 'Pair'],
]);
$logger->setEmailRecipients(['ops@example.com']);
$logger->setEmailThreshold(Logger::ERROR);$logger->useTransport('ses', [
// Passed as-is to AmazonSes transport
'region' => 'eu-west-1',
'accessKeyId' => '...',
'secretAccessKey' => '...',
'from' => ['no-reply@example.com' => 'Pair'],
]);$logger->useTransport('sendmail', [
// Passed as-is to Sendmail transport
'from' => ['no-reply@example.com' => 'Pair'],
]);If email transport is not configured, logs still work (LogBar + DB snapshot + Telegram if configured).
Telegram is sent only if:
-
telegramBotTokenis set; -
telegramChatIdscontains at least one chat ID; -
telegramThreshold >= level(meaning: send on severe events only).
use Pair\Core\Logger;
$logger = Logger::getInstance();
$logger->setTelegramBotToken(Env::get('TELEGRAM_BOT_TOKEN'));
$logger->setTelegramChatIds([123456789, 987654321]);
$logger->setTelegramThreshold(Logger::CRITICAL);Pair can route PHP errors, uncaught exceptions, and fatal shutdown errors to the logger.
use Pair\Core\Logger;
Logger::registerHandlers();This sets:
set_error_handler([Logger::class, 'errorHandler'])set_exception_handler([Logger::class, 'exceptionHandler'])register_shutdown_function([Logger::class, 'shutdownHandler'])
$logger = Logger::getInstance();
$logger->info('User logged in');
$logger->notice('Profile updated');
$logger->warning('Slow query detected');
$logger->error('Payment gateway failure');Logger replaces {placeholders} with scalar / stringable values from $context.
$logger->error(
'Order {orderId} failed for user {userId}',
['orderId' => 1001, 'userId' => 55]
);This is done safely: non-scalar values are ignored.
$logger->log('error', 'Something went wrong');
$logger->log(Logger::ERROR, 'Something went wrong');Returns the singleton instance.
$logger = Logger::getInstance();Registers the error, exception and shutdown handlers.
Logger::registerHandlers();public static function errorHandler(int $errno, string $errstr, ?string $errfile = null, ?int $errline = null): bool
Custom PHP error handler.
- Logs the error internally (as
errorlevel). - If
SENTRY_DSNis set, forwards to Sentry. - Forwards to Insight Hub handler.
- If
APP_DEBUGistrue, returnsfalseso PHP can also display the error. - Otherwise returns
trueto suppress output in production.
Returning
falseallows the default PHP handler to run, which is useful during development.
Custom uncaught exception handler.
- Forwards to Sentry (if enabled and SDK exists).
- Forwards the exception to Insight Hub.
- Logs the exception message and location internally.
Shutdown handler for fatal errors.
- Reads
error_get_last(). - If the last error is fatal (
E_ERROR,E_CORE_ERROR,E_COMPILE_ERROR,E_USER_ERROR):- logs it;
- forwards to Sentry (if enabled);
- forwards to Insight Hub.
Disables the logger (no LogBar, no DB snapshot, no notifications).
Logger::getInstance()->disable();The main PSR-3 log entrypoint.
- Accepts:
- numeric level
1..8, or - string level name (
"error","warning", etc).
- numeric level
- Renders
{placeholders}using the provided context. - Logs to LogBar.
- For WARNING or worse, triggers processing (DB + notifications).
Each of these calls log() with the corresponding level:
emergency(Stringable|string $message, array $context = [])alert(Stringable|string $message, array $context = [])critical(Stringable|string $message, array $context = [])error(Stringable|string $message, array $context = [])warning(Stringable|string $message, array $context = [])notice(Stringable|string $message, array $context = [])info(Stringable|string $message, array $context = [])debug(Stringable|string $message, array $context = [])
Example:
$logger->critical('Database is not responding', ['errorCode' => 123]);Configures the email transport used for error notifications.
Supported types:
-
smtp→SmtpMailer -
ses→AmazonSes -
sendmail→Sendmail
$logger->useTransport('smtp', [/* transport config */]);If the type is unknown, an InvalidArgumentException is thrown.
Sets the email recipients list. Values are normalized and filtered to valid emails.
$logger->setEmailRecipients(['ops@example.com', 'dev@example.com']);Sets the email threshold level (1..8).
- If the level is invalid, the logger emits an internal configuration error.
$logger->setEmailThreshold(Logger::ERROR);Sets the Telegram bot token used by TelegramBotClient.
$logger->setTelegramBotToken('123456:ABCDEF...');Sets the Telegram chat IDs list. Values are normalized to integers.
$logger->setTelegramChatIds([123456789, 987654321]);Sets the Telegram threshold level (1..8).
$logger->setTelegramThreshold(Logger::CRITICAL);These methods are part of the internal implementation and are listed here for completeness.
Replaces {key} tokens with scalar or stringable values found in $context.
Non-scalar values are ignored.
Stores a snapshot into the database (via ErrorLog), including:
- level, user id, router path
-
$_GET,$_POST,$_FILES(cookies intentionally not stored) - the first 255 characters of the description
- referer (with
BASE_HREFremoved when possible) - queued application notifications (user messages)
The snapshot is performed only when the DB is connected and the error is not a DB connection error.
private function process(int $level, string $description, array $context = [], ?int $errorCode = null): void
Runs the “severe log” pipeline for WARNING or worse:
- snapshots to DB (when allowed);
- sends email notification (if configured);
- sends Telegram notification (if configured).
Verifies mailer configuration and throws/logs on misconfiguration.
Called before sending email notifications.
Transport-specific builders. Each one instantiates the corresponding transport object with $config.
- You can pass an
errorCodein$context(as['errorCode' => ...]) to help categorise errors or avoid specific pipelines. - DB snapshot is skipped for known DB connection failures to prevent recursion during outages.
- Sentry integration requires
sentry/sdk. Insight Hub integration requiresbugsnag/bugsnag.
See also: Log, LogBar, ErrorLog, Env, Application.