Logger - viames/pair GitHub Wiki
Pair framework: Logger class
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.
Getting the logger instance
use Pair\Core\Logger;
$logger = Logger::getInstance();
Logger::getInstance() always returns the same shared instance.
Log levels
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.
What happens when you log
Always: LogBar event
Every call ends up in LogBar:
debug,info,noticeโLogBar::event(..., 'notice')warningโLogBar::event(..., 'warning')error,critical,alert,emergencyโLogBar::event(..., 'error')
Only for WARNING or worse: processing (DB + notifications)
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).
Configuration via .env
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:[email protected],[email protected] -
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
[email protected],[email protected]
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://[email protected]/1
Configuring email transport
Email is sent only if:
- a mail transport has been configured (
$logger->useTransport(...)); emailRecipientsare set;level <= emailThreshold.
Example: SMTP transport
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' => ['[email protected]' => 'Pair'],
]);
$logger->setEmailRecipients(['[email protected]']);
$logger->setEmailThreshold(Logger::ERROR);
Example: Amazon SES transport
$logger->useTransport('ses', [
// Passed as-is to AmazonSes transport
'region' => 'eu-west-1',
'accessKeyId' => '...',
'secretAccessKey' => '...',
'from' => ['[email protected]' => 'Pair'],
]);
Example: Sendmail transport
$logger->useTransport('sendmail', [
// Passed as-is to Sendmail transport
'from' => ['[email protected]' => 'Pair'],
]);
If email transport is not configured, logs still work (LogBar + DB snapshot + Telegram if configured).
Configuring Telegram notifications
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);
Registering global handlers
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'])
Quick usage examples
Basic messages
$logger = Logger::getInstance();
$logger->info('User logged in');
$logger->notice('Profile updated');
$logger->warning('Slow query detected');
$logger->error('Payment gateway failure');
Context placeholders (PSR-3 style)
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.
Manual severity (string or numeric)
$logger->log('error', 'Something went wrong');
$logger->log(Logger::ERROR, 'Something went wrong');
Method reference
public static function getInstance(): self
Returns the singleton instance.
$logger = Logger::getInstance();
public static function registerHandlers(): void
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.
public static function exceptionHandler(Throwable $e): void
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.
public static function shutdownHandler(): void
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.
public function disable(): void
Disables the logger (no LogBar, no DB snapshot, no notifications).
Logger::getInstance()->disable();
public function log(mixed $level, Stringable|string $message, array $context = []): void
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).
Convenience PSR-3 methods
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]);
public function useTransport(string $type, array $config): void
Configures the email transport used for error notifications.
Supported types:
smtpโSmtpMailersesโAmazonSessendmailโSendmail
$logger->useTransport('smtp', [/* transport config */]);
If the type is unknown, an InvalidArgumentException is thrown.
public function setEmailRecipients(array $recipients): void
Sets the email recipients list. Values are normalized and filtered to valid emails.
$logger->setEmailRecipients(['[email protected]', '[email protected]']);
public function setEmailThreshold(int $level): void
Sets the email threshold level (1..8).
- If the level is invalid, the logger emits an internal configuration error.
$logger->setEmailThreshold(Logger::ERROR);
public function setTelegramBotToken(string $token): void
Sets the Telegram bot token used by TelegramBotClient.
$logger->setTelegramBotToken('123456:ABCDEF...');
public function setTelegramChatIds(array $chatIds): void
Sets the Telegram chat IDs list. Values are normalized to integers.
$logger->setTelegramChatIds([123456789, 987654321]);
public function setTelegramThreshold(int $level): void
Sets the Telegram threshold level (1..8).
$logger->setTelegramThreshold(Logger::CRITICAL);
Internal methods (implementation details)
These methods are part of the internal implementation and are listed here for completeness.
private function interpolate(string $message, array $context): string
Replaces {key} tokens with scalar or stringable values found in $context.
Non-scalar values are ignored.
private function snapshot(string $description, ?int $level = null): void
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).
private function ensureMailer(): void
Verifies mailer configuration and throws/logs on misconfiguration.
Called before sending email notifications.
private function setSmtpMailerConfig(array $config): void
private function setSesConfig(array $config): void
private function setSendmailConfig(array $config): void
Transport-specific builders. Each one instantiates the corresponding transport object with $config.
Notes
- 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.