1
0
mirror of https://github.com/Seldaek/monolog.git synced 2025-07-28 17:00:17 +02:00

Add Record/Level/LevelName type aliases and improve phpstan type coverage to level 6

This commit is contained in:
Jordi Boggiano
2021-05-28 10:29:33 +02:00
parent 5c9d9bf43e
commit 01d104aa78
88 changed files with 791 additions and 180 deletions

View File

@@ -1,5 +1,5 @@
parameters:
level: 5
level: 6
treatPhpDocTypesAsCertain: false
reportUnmatchedIgnoredErrors: false
@@ -18,3 +18,8 @@ parameters:
- message: '#Method Monolog\\Handler\\LogglyHandler::loadCurlHandle\(\) never returns resource so it can be removed from the return typehint.#'
paths:
- src/Monolog/Handler/LogglyHandler.php
# blocked by https://github.com/phpstan/phpstan/issues/5091
- '#has unknown class Monolog\\Handler\\Record#'
- '#::processRecord#'
- '#is incompatible with native type array.#'

View File

@@ -25,19 +25,30 @@ use Psr\Log\LogLevel;
*/
class ErrorHandler
{
/** @var LoggerInterface */
private $logger;
private $previousExceptionHandler;
private $uncaughtExceptionLevelMap;
/** @var ?callable */
private $previousExceptionHandler = null;
/** @var array<class-string, LogLevel::*> an array of class name to LogLevel::* constant mapping */
private $uncaughtExceptionLevelMap = [];
private $previousErrorHandler;
private $errorLevelMap;
private $handleOnlyReportedErrors;
/** @var callable|true|null */
private $previousErrorHandler = null;
/** @var array<int, LogLevel::*> an array of E_* constant to LogLevel::* constant mapping */
private $errorLevelMap = [];
/** @var bool */
private $handleOnlyReportedErrors = true;
private $hasFatalErrorHandler;
private $fatalLevel;
private $reservedMemory;
/** @var bool */
private $hasFatalErrorHandler = false;
/** @var LogLevel::* */
private $fatalLevel = LogLevel::ALERT;
/** @var ?string */
private $reservedMemory = null;
/** @var ?mixed */
private $lastFatalTrace;
/** @var int[] */
private static $fatalErrors = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
public function __construct(LoggerInterface $logger)
@@ -50,10 +61,10 @@ class ErrorHandler
*
* By default it will handle errors, exceptions and fatal errors
*
* @param LoggerInterface $logger
* @param array|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling
* @param array|false $exceptionLevelMap an array of class name to LogLevel::* constant mapping, or false to disable exception handling
* @param string|null|false $fatalLevel a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling
* @param LoggerInterface $logger
* @param array<int, LogLevel::*>|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling
* @param array<class-string, LogLevel::*>|false $exceptionLevelMap an array of class name to LogLevel::* constant mapping, or false to disable exception handling
* @param LogLevel::*|null|false $fatalLevel a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling
* @return ErrorHandler
*/
public static function register(LoggerInterface $logger, $errorLevelMap = [], $exceptionLevelMap = [], $fatalLevel = null): self
@@ -73,7 +84,11 @@ class ErrorHandler
return $handler;
}
public function registerExceptionHandler($levelMap = [], $callPrevious = true): self
/**
* @param array<class-string, LogLevel::*> $levelMap an array of class name to LogLevel::* constant mapping
* @return $this
*/
public function registerExceptionHandler(array $levelMap = [], bool $callPrevious = true): self
{
$prev = set_exception_handler(function (\Throwable $e): void {
$this->handleException($e);
@@ -91,12 +106,18 @@ class ErrorHandler
return $this;
}
public function registerErrorHandler(array $levelMap = [], $callPrevious = true, $errorTypes = -1, $handleOnlyReportedErrors = true): self
/**
* @param array<int, LogLevel::*> $levelMap an array of E_* constant to LogLevel::* constant mapping
* @return $this
*/
public function registerErrorHandler(array $levelMap = [], bool $callPrevious = true, int $errorTypes = -1, bool $handleOnlyReportedErrors = true): self
{
$prev = set_error_handler([$this, 'handleError'], $errorTypes);
$this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap);
if ($callPrevious) {
$this->previousErrorHandler = $prev ?: true;
} else {
$this->previousErrorHandler = null;
}
$this->handleOnlyReportedErrors = $handleOnlyReportedErrors;
@@ -105,20 +126,23 @@ class ErrorHandler
}
/**
* @param string|null $level a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling
* @param int $reservedMemorySize Amount of KBs to reserve in memory so that it can be freed when handling fatal errors giving Monolog some room in memory to get its job done
* @param LogLevel::*|null $level a LogLevel::* constant, null to use the default LogLevel::ALERT
* @param int $reservedMemorySize Amount of KBs to reserve in memory so that it can be freed when handling fatal errors giving Monolog some room in memory to get its job done
*/
public function registerFatalHandler($level = null, int $reservedMemorySize = 20): self
{
register_shutdown_function([$this, 'handleFatalError']);
$this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize);
$this->fatalLevel = $level;
$this->fatalLevel = null === $level ? LogLevel::ALERT : $level;
$this->hasFatalErrorHandler = true;
return $this;
}
/**
* @return array<class-string, LogLevel::*>
*/
protected function defaultExceptionLevelMap(): array
{
return [
@@ -127,6 +151,9 @@ class ErrorHandler
];
}
/**
* @return array<int, LogLevel::*>
*/
protected function defaultErrorLevelMap(): array
{
return [
@@ -148,7 +175,10 @@ class ErrorHandler
];
}
private function handleException(\Throwable $e)
/**
* @phpstan-return never
*/
private function handleException(\Throwable $e): void
{
$level = LogLevel::ERROR;
foreach ($this->uncaughtExceptionLevelMap as $class => $candidate) {
@@ -177,11 +207,13 @@ class ErrorHandler
/**
* @private
*
* @param mixed[] $context
*/
public function handleError($code, $message, $file = '', $line = 0, $context = [])
public function handleError(int $code, string $message, string $file = '', int $line = 0, array $context = []): bool
{
if ($this->handleOnlyReportedErrors && !(error_reporting() & $code)) {
return;
return false;
}
// fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries
@@ -197,7 +229,7 @@ class ErrorHandler
if ($this->previousErrorHandler === true) {
return false;
} elseif ($this->previousErrorHandler) {
return ($this->previousErrorHandler)($code, $message, $file, $line, $context);
return (bool) ($this->previousErrorHandler)($code, $message, $file, $line, $context);
}
return true;
@@ -206,14 +238,14 @@ class ErrorHandler
/**
* @private
*/
public function handleFatalError()
public function handleFatalError(): void
{
$this->reservedMemory = '';
$lastError = error_get_last();
if ($lastError && in_array($lastError['type'], self::$fatalErrors, true)) {
$this->logger->log(
$this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel,
$this->fatalLevel,
'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'],
['code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'], 'trace' => $this->lastFatalTrace]
);
@@ -226,6 +258,9 @@ class ErrorHandler
}
}
/**
* @param int $code
*/
private static function codeToString($code): string
{
switch ($code) {

View File

@@ -22,6 +22,8 @@ class ChromePHPFormatter implements FormatterInterface
{
/**
* Translates Monolog log levels to Wildfire levels.
*
* @var array<int, 'log'|'info'|'warn'|'error'>
*/
private $logLevels = [
Logger::DEBUG => 'log',

View File

@@ -17,6 +17,8 @@ use Elastica\Document;
* Format a log message into an Elastica Document
*
* @author Jelle Vink <jelle.vink@gmail.com>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class ElasticaFormatter extends NormalizerFormatter
{
@@ -68,8 +70,8 @@ class ElasticaFormatter extends NormalizerFormatter
/**
* Convert a log message into an Elastica Document
* @param array $record
* @return Document
*
* @phpstan-param Record $record
*/
protected function getDocument(array $record): Document
{

View File

@@ -11,7 +11,7 @@
namespace Monolog\Formatter;
use DateTime;
use DateTimeInterface;
/**
* Format a log message into an Elasticsearch record
@@ -37,7 +37,7 @@ class ElasticsearchFormatter extends NormalizerFormatter
public function __construct(string $index, string $type)
{
// Elasticsearch requires an ISO 8601 format date with optional millisecond precision.
parent::__construct(DateTime::ISO8601);
parent::__construct(DateTimeInterface::ISO8601);
$this->index = $index;
$this->type = $type;
@@ -76,8 +76,8 @@ class ElasticsearchFormatter extends NormalizerFormatter
/**
* Convert a log message into an Elasticsearch record
*
* @param array $record Log message
* @return array
* @param mixed[] $record Log message
* @return mixed[]
*/
protected function getDocument(array $record): array
{

View File

@@ -36,6 +36,8 @@ class FlowdockFormatter implements FormatterInterface
/**
* {@inheritdoc}
*
* @return mixed[]
*/
public function format(array $record): array
{
@@ -70,6 +72,8 @@ class FlowdockFormatter implements FormatterInterface
/**
* {@inheritdoc}
*
* @return mixed[][]
*/
public function formatBatch(array $records): array
{

View File

@@ -15,6 +15,8 @@ namespace Monolog\Formatter;
* Interface for formatters
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
interface FormatterInterface
{
@@ -23,6 +25,8 @@ interface FormatterInterface
*
* @param array $record A record to format
* @return mixed The formatted record
*
* @phpstan-param Record $record
*/
public function format(array $record);
@@ -31,6 +35,8 @@ interface FormatterInterface
*
* @param array $records A set of records to format
* @return mixed The formatted set of records
*
* @phpstan-param Record[] $records
*/
public function formatBatch(array $records);
}

View File

@@ -47,6 +47,8 @@ class GelfMessageFormatter extends NormalizerFormatter
/**
* Translates Monolog log levels to Graylog2 log priorities.
*
* @var array<int, int>
*/
private $logLevels = [
Logger::DEBUG => 7,

View File

@@ -25,6 +25,8 @@ class HtmlFormatter extends NormalizerFormatter
{
/**
* Translates Monolog log levels to html color priorities.
*
* @var array<int, string>
*/
protected $logLevels = [
Logger::DEBUG => '#CCCCCC',
@@ -79,7 +81,6 @@ class HtmlFormatter extends NormalizerFormatter
/**
* Formats a log record.
*
* @param array $record A record to format
* @return string The formatted record
*/
public function format(array $record): string
@@ -113,7 +114,6 @@ class HtmlFormatter extends NormalizerFormatter
/**
* Formats a set of log records.
*
* @param array $records A set of records to format
* @return string The formatted set of records
*/
public function formatBatch(array $records): string
@@ -126,6 +126,9 @@ class HtmlFormatter extends NormalizerFormatter
return $message;
}
/**
* @param mixed $data
*/
protected function convertToString($data): string
{
if (null === $data || is_scalar($data)) {

View File

@@ -19,21 +19,26 @@ use Throwable;
* This can be useful to log to databases or remote APIs
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class JsonFormatter extends NormalizerFormatter
{
public const BATCH_MODE_JSON = 1;
public const BATCH_MODE_NEWLINES = 2;
/** @var self::BATCH_MODE_* */
protected $batchMode;
/** @var bool */
protected $appendNewline;
/** @var bool */
protected $ignoreEmptyContextAndExtra;
/**
* @var bool
*/
/** @var bool */
protected $includeStacktraces = false;
/**
* @param self::BATCH_MODE_* $batchMode
*/
public function __construct(int $batchMode = self::BATCH_MODE_JSON, bool $appendNewline = true, bool $ignoreEmptyContextAndExtra = false)
{
$this->batchMode = $batchMode;
@@ -101,6 +106,9 @@ class JsonFormatter extends NormalizerFormatter
}
}
/**
* @return void
*/
public function includeStacktraces(bool $include = true)
{
$this->includeStacktraces = $include;
@@ -108,6 +116,8 @@ class JsonFormatter extends NormalizerFormatter
/**
* Return a JSON-encoded array of records.
*
* @phpstan-param Record[] $records
*/
protected function formatBatchJson(array $records): string
{
@@ -117,6 +127,8 @@ class JsonFormatter extends NormalizerFormatter
/**
* Use new lines to separate records instead of a
* JSON-encoded array.
*
* @phpstan-param Record[] $records
*/
protected function formatBatchNewlines(array $records): string
{
@@ -175,6 +187,8 @@ class JsonFormatter extends NormalizerFormatter
/**
* Normalizes given exception with or without its own stack trace based on
* `includeStacktraces` property.
*
* {@inheritDoc}
*/
protected function normalizeException(Throwable $e, int $depth = 0): array
{

View File

@@ -25,9 +25,13 @@ class LineFormatter extends NormalizerFormatter
{
public const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
/** @var string */
protected $format;
/** @var bool */
protected $allowInlineLineBreaks;
/** @var bool */
protected $ignoreEmptyContextAndExtra;
/** @var bool */
protected $includeStacktraces;
/**
@@ -44,7 +48,7 @@ class LineFormatter extends NormalizerFormatter
parent::__construct($dateFormat);
}
public function includeStacktraces(bool $include = true)
public function includeStacktraces(bool $include = true): void
{
$this->includeStacktraces = $include;
if ($this->includeStacktraces) {
@@ -52,12 +56,12 @@ class LineFormatter extends NormalizerFormatter
}
}
public function allowInlineLineBreaks(bool $allow = true)
public function allowInlineLineBreaks(bool $allow = true): void
{
$this->allowInlineLineBreaks = $allow;
}
public function ignoreEmptyContextAndExtra(bool $ignore = true)
public function ignoreEmptyContextAndExtra(bool $ignore = true): void
{
$this->ignoreEmptyContextAndExtra = $ignore;
}
@@ -121,6 +125,9 @@ class LineFormatter extends NormalizerFormatter
return $message;
}
/**
* @param mixed $value
*/
public function stringify($value): string
{
return $this->replaceNewlines($this->convertToString($value));
@@ -139,6 +146,9 @@ class LineFormatter extends NormalizerFormatter
return $str;
}
/**
* @param mixed $data
*/
protected function convertToString($data): string
{
if (null === $data || is_bool($data)) {

View File

@@ -21,8 +21,11 @@ use Monolog\Utils;
*/
class MongoDBFormatter implements FormatterInterface
{
/** @var bool */
private $exceptionTraceAsString;
/** @var int */
private $maxNestingLevel;
/** @var bool */
private $isLegacyMongoExt;
/**
@@ -39,6 +42,8 @@ class MongoDBFormatter implements FormatterInterface
/**
* {@inheritDoc}
*
* @return scalar[]
*/
public function format(array $record): array
{
@@ -47,40 +52,48 @@ class MongoDBFormatter implements FormatterInterface
/**
* {@inheritDoc}
*
* @return array<scalar[]>
*/
public function formatBatch(array $records): array
{
$formatted = [];
foreach ($records as $key => $record) {
$records[$key] = $this->format($record);
$formatted[$key] = $this->format($record);
}
return $records;
return $formatted;
}
/**
* @return array|string Array except when max nesting level is reached then a string "[...]"
* @param mixed[] $array
* @return mixed[]|string Array except when max nesting level is reached then a string "[...]"
*/
protected function formatArray(array $record, int $nestingLevel = 0)
protected function formatArray(array $array, int $nestingLevel = 0)
{
if ($this->maxNestingLevel == 0 || $nestingLevel <= $this->maxNestingLevel) {
foreach ($record as $name => $value) {
if ($value instanceof \DateTimeInterface) {
$record[$name] = $this->formatDate($value, $nestingLevel + 1);
} elseif ($value instanceof \Throwable) {
$record[$name] = $this->formatException($value, $nestingLevel + 1);
} elseif (is_array($value)) {
$record[$name] = $this->formatArray($value, $nestingLevel + 1);
} elseif (is_object($value)) {
$record[$name] = $this->formatObject($value, $nestingLevel + 1);
}
}
} else {
$record = '[...]';
if ($this->maxNestingLevel > 0 && $nestingLevel > $this->maxNestingLevel) {
return '[...]';
}
return $record;
foreach ($array as $name => $value) {
if ($value instanceof \DateTimeInterface) {
$array[$name] = $this->formatDate($value, $nestingLevel + 1);
} elseif ($value instanceof \Throwable) {
$array[$name] = $this->formatException($value, $nestingLevel + 1);
} elseif (is_array($value)) {
$array[$name] = $this->formatArray($value, $nestingLevel + 1);
} elseif (is_object($value)) {
$array[$name] = $this->formatObject($value, $nestingLevel + 1);
}
}
return $array;
}
/**
* @param mixed $value
* @return mixed[]|string
*/
protected function formatObject($value, int $nestingLevel)
{
$objectVars = get_object_vars($value);
@@ -89,6 +102,9 @@ class MongoDBFormatter implements FormatterInterface
return $this->formatArray($objectVars, $nestingLevel);
}
/**
* @return mixed[]|string
*/
protected function formatException(\Throwable $exception, int $nestingLevel)
{
$formattedException = [

View File

@@ -123,7 +123,7 @@ class NormalizerFormatter implements FormatterInterface
/**
* @param mixed $data
* @return int|bool|string|null|array
* @return scalar|array<scalar>
*/
protected function normalize($data, int $depth = 0)
{
@@ -189,7 +189,7 @@ class NormalizerFormatter implements FormatterInterface
}
/**
* @return array
* @return mixed[]
*/
protected function normalizeException(Throwable $e, int $depth = 0)
{
@@ -248,6 +248,9 @@ class NormalizerFormatter implements FormatterInterface
return Utils::jsonEncode($data, $this->jsonEncodeOptions, $ignoreErrors);
}
/**
* @return string
*/
protected function formatDate(\DateTimeInterface $date)
{
// in case the date format isn't custom then we defer to the custom DateTimeImmutable
@@ -259,12 +262,12 @@ class NormalizerFormatter implements FormatterInterface
return $date->format($this->dateFormat);
}
public function addJsonEncodeOption(int $option)
public function addJsonEncodeOption(int $option): void
{
$this->jsonEncodeOptions |= $option;
}
public function removeJsonEncodeOption(int $option)
public function removeJsonEncodeOption(int $option): void
{
$this->jsonEncodeOptions &= ~$option;
}

View File

@@ -21,6 +21,8 @@ class ScalarFormatter extends NormalizerFormatter
{
/**
* {@inheritdoc}
*
* @phpstan-return scalar[] $record
*/
public function format(array $record): array
{

View File

@@ -19,11 +19,15 @@ use Monolog\Logger;
* @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
* @author Christophe Coevoet <stof@notk.org>
* @author Kirill chEbba Chebunin <iam@chebba.org>
*
* @phpstan-import-type Level from \Monolog\Logger
*/
class WildfireFormatter extends NormalizerFormatter
{
/**
* Translates Monolog log levels to Wildfire levels.
*
* @var array<Level, string>
*/
private $logLevels = [
Logger::DEBUG => 'LOG',
@@ -49,6 +53,8 @@ class WildfireFormatter extends NormalizerFormatter
/**
* {@inheritdoc}
*
* @return string
*/
public function format(array $record): string
{
@@ -108,6 +114,8 @@ class WildfireFormatter extends NormalizerFormatter
/**
* {@inheritdoc}
*
* @phpstan-return never
*/
public function formatBatch(array $records)
{
@@ -116,7 +124,8 @@ class WildfireFormatter extends NormalizerFormatter
/**
* {@inheritdoc}
* @return int|bool|string|null|array|object
*
* @return scalar|array<scalar>|object
*/
protected function normalize($data, int $depth = 0)
{

View File

@@ -18,10 +18,17 @@ use Monolog\ResettableInterface;
* Base Handler class providing basic level/bubble support
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Level from \Monolog\Logger
*/
abstract class AbstractHandler extends Handler implements ResettableInterface
{
/**
* @var int
* @phpstan-var Level
*/
protected $level = Logger::DEBUG;
/** @var bool */
protected $bubble = true;
/**
@@ -59,6 +66,8 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
* Gets minimum logging level at which this handler will be triggered.
*
* @return int
*
* @phpstan-return Level
*/
public function getLevel(): int
{
@@ -90,6 +99,9 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
return $this->bubble;
}
/**
* {@inheritDoc}
*/
public function reset()
{
}

View File

@@ -18,6 +18,10 @@ namespace Monolog\Handler;
*
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Christophe Coevoet <stof@notk.org>
*
* @phpstan-import-type LevelName from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-type FormattedRecord array{message: string, context: mixed[], level: Level, level_name: LevelName, channel: string, datetime: \DateTimeImmutable, extra: mixed[], formatted: mixed}
*/
abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
{
@@ -46,9 +50,14 @@ abstract class AbstractProcessingHandler extends AbstractHandler implements Proc
/**
* Writes the record down to the log of the implementing handler
*
* @phpstan-param FormattedRecord $record
*/
abstract protected function write(array $record): void;
/**
* @return void
*/
public function reset()
{
parent::reset();

View File

@@ -17,13 +17,18 @@ use Monolog\Formatter\LineFormatter;
/**
* Common syslog functionality
*
* @phpstan-import-type Level from \Monolog\Logger
*/
abstract class AbstractSyslogHandler extends AbstractProcessingHandler
{
/** @var int */
protected $facility;
/**
* Translates Monolog log levels to syslog log priorities.
* @var array
* @phpstan-var array<Level, int>
*/
protected $logLevels = [
Logger::DEBUG => LOG_DEBUG,
@@ -38,6 +43,7 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler
/**
* List of valid log facility names.
* @var array<string, int>
*/
protected $facilities = [
'auth' => LOG_AUTH,

View File

@@ -18,6 +18,9 @@ use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Channel\AMQPChannel;
use AMQPExchange;
/**
* @phpstan-import-type Record from \Monolog\Logger
*/
class AmqpHandler extends AbstractProcessingHandler
{
/**
@@ -108,6 +111,8 @@ class AmqpHandler extends AbstractProcessingHandler
/**
* Gets the routing key for the AMQP exchange
*
* @phpstan-param Record $record
*/
protected function getRoutingKey(array $record): string
{

View File

@@ -19,10 +19,14 @@ use Monolog\Utils;
* Handler sending logs to browser's javascript console with no browser extension required
*
* @author Olivier Poitrey <rs@dailymotion.com>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/
class BrowserConsoleHandler extends AbstractProcessingHandler
{
/** @var bool */
protected static $initialized = false;
/** @var FormattedRecord[] */
protected static $records = [];
/**
@@ -165,6 +169,9 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);";
}
/**
* @return string[]
*/
private static function handleStyles(string $formatted): array
{
$args = [];
@@ -205,6 +212,10 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
}, $style);
}
/**
* @param mixed[] $dict
* @return mixed[]
*/
private static function dump(string $title, array $dict): array
{
$script = [];
@@ -229,13 +240,22 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
return '"' . addcslashes($arg, "\"\n\\") . '"';
}
/**
* @param mixed $args
*/
private static function call(...$args): string
{
$method = array_shift($args);
if (!is_string($method)) {
throw new \UnexpectedValueException('Expected the first arg to be a string, got: '.var_export($method, true));
}
return static::call_array($method, $args);
}
/**
* @param mixed[] $args
*/
private static function call_array(string $method, array $args): string
{
return 'c.' . $method . '(' . implode(', ', $args) . ');';

View File

@@ -22,6 +22,8 @@ use Monolog\Formatter\FormatterInterface;
* sending one per log message.
*
* @author Christophe Coevoet <stof@notk.org>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class BufferHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
{
@@ -29,10 +31,15 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
/** @var HandlerInterface */
protected $handler;
/** @var int */
protected $bufferSize = 0;
/** @var int */
protected $bufferLimit;
/** @var bool */
protected $flushOnOverflow;
/** @var Record[] */
protected $buffer = [];
/** @var bool */
protected $initialized = false;
/**

View File

@@ -42,6 +42,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
*/
protected const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}';
/** @var bool */
protected static $initialized = false;
/**
@@ -53,12 +54,14 @@ class ChromePHPHandler extends AbstractProcessingHandler
*/
protected static $overflowed = false;
/** @var mixed[] */
protected static $json = [
'version' => self::VERSION,
'columns' => ['label', 'log', 'backtrace', 'type'],
'rows' => [],
];
/** @var bool */
protected static $sendHeaders = true;
/**

View File

@@ -22,8 +22,12 @@ use Monolog\Logger;
*/
class CouchDBHandler extends AbstractProcessingHandler
{
/** @var mixed[] */
private $options;
/**
* @param mixed[] $options
*/
public function __construct(array $options = [], $level = Logger::DEBUG, bool $bubble = true)
{
$this->options = array_merge([

View File

@@ -22,11 +22,17 @@ use Monolog\Utils;
*/
class CubeHandler extends AbstractProcessingHandler
{
private $udpConnection;
private $httpConnection;
/** @var resource|\Socket|null */
private $udpConnection = null;
/** @var resource|\CurlHandle|null */
private $httpConnection = null;
/** @var string */
private $scheme;
/** @var string */
private $host;
/** @var int */
private $port;
/** @var string[] */
private $acceptedSchemes = ['http', 'udp'];
/**
@@ -53,7 +59,7 @@ class CubeHandler extends AbstractProcessingHandler
$this->scheme = $urlInfo['scheme'];
$this->host = $urlInfo['host'];
$this->port = $urlInfo['port'];
$this->port = (int) $urlInfo['port'];
parent::__construct($level, $bubble);
}

View File

@@ -20,6 +20,7 @@ use CurlHandle;
*/
final class Util
{
/** @var array<int> */
private static $retriableErrorCodes = [
CURLE_COULDNT_RESOLVE_HOST,
CURLE_COULDNT_CONNECT,

View File

@@ -32,6 +32,8 @@ use Monolog\Logger;
* same way.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class DeduplicationHandler extends BufferHandler
{
@@ -100,6 +102,9 @@ class DeduplicationHandler extends BufferHandler
}
}
/**
* @phpstan-param Record $record
*/
private function isDuplicate(array $record): bool
{
if (!file_exists($this->deduplicationStore)) {
@@ -166,6 +171,9 @@ class DeduplicationHandler extends BufferHandler
$this->gc = false;
}
/**
* @phpstan-param Record $record
*/
private function appendRecord(array $record): void
{
file_put_contents($this->deduplicationStore, $record['datetime']->getTimestamp() . ':' . $record['level_name'] . ':' . preg_replace('{[\r\n].*}', '', $record['message']) . "\n", FILE_APPEND);

View File

@@ -23,6 +23,7 @@ use Doctrine\CouchDB\CouchDBClient;
*/
class DoctrineCouchDBHandler extends AbstractProcessingHandler
{
/** @var CouchDBClient */
private $client;
public function __construct(CouchDBClient $client, $level = Logger::DEBUG, bool $bubble = true)

View File

@@ -86,6 +86,10 @@ class DynamoDbHandler extends AbstractProcessingHandler
]);
}
/**
* @param mixed[] $record
* @return mixed[]
*/
protected function filterEmptyFields(array $record): array
{
return array_filter($record, function ($value) {

View File

@@ -11,6 +11,7 @@
namespace Monolog\Handler;
use Elastica\Document;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\ElasticaFormatter;
use Monolog\Logger;
@@ -41,13 +42,13 @@ class ElasticaHandler extends AbstractProcessingHandler
protected $client;
/**
* @var array Handler config options
* @var mixed[] Handler config options
*/
protected $options = [];
/**
* @param Client $client Elastica Client object
* @param array $options Handler configuration
* @param mixed[] $options Handler configuration
* @param int|string $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/
@@ -85,6 +86,9 @@ class ElasticaHandler extends AbstractProcessingHandler
throw new \InvalidArgumentException('ElasticaHandler is only compatible with ElasticaFormatter');
}
/**
* @return mixed[]
*/
public function getOptions(): array
{
return $this->options;
@@ -109,6 +113,9 @@ class ElasticaHandler extends AbstractProcessingHandler
/**
* Use Elasticsearch bulk API to send list of documents
*
* @param Document[] $documents
*
* @throws \RuntimeException
*/
protected function bulkSend(array $documents): void

View File

@@ -49,13 +49,13 @@ class ElasticsearchHandler extends AbstractProcessingHandler
protected $client;
/**
* @var array Handler config options
* @var mixed[] Handler config options
*/
protected $options = [];
/**
* @param Client $client Elasticsearch Client object
* @param array $options Handler configuration
* @param mixed[] $options Handler configuration
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/
@@ -96,7 +96,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
/**
* Getter options
*
* @return array
* @return mixed[]
*/
public function getOptions(): array
{
@@ -123,7 +123,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
/**
* Use Elasticsearch bulk API to send list of documents
*
* @param array $records
* @param array[] $records Records + _index/_type keys
* @throws \RuntimeException
*/
protected function bulkSend(array $records): void
@@ -162,7 +162,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
*
* Only the first error is converted into an exception.
*
* @param array $responses returned by $this->client->bulk()
* @param mixed[] $responses returned by $this->client->bulk()
*/
protected function createExceptionFromResponses(array $responses): ElasticsearchRuntimeException
{
@@ -178,7 +178,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
/**
* Creates elasticsearch exception from error array
*
* @param array $error
* @param mixed[] $error
*/
protected function createExceptionFromError(array $error): ElasticsearchRuntimeException
{

View File

@@ -25,7 +25,9 @@ class ErrorLogHandler extends AbstractProcessingHandler
public const OPERATING_SYSTEM = 0;
public const SAPI = 4;
/** @var int */
protected $messageType;
/** @var bool */
protected $expandNewlines;
/**
@@ -49,7 +51,7 @@ class ErrorLogHandler extends AbstractProcessingHandler
}
/**
* @return array With all available types
* @return int[] With all available types
*/
public static function getAvailableTypes(): array
{

View File

@@ -14,6 +14,7 @@ namespace Monolog\Handler;
use Monolog\Logger;
use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface;
use Psr\Log\LogLevel;
/**
* Simple handler wrapper that filters records based on a list of levels
@@ -22,6 +23,10 @@ use Monolog\Formatter\FormatterInterface;
*
* @author Hennadiy Verkh
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/
class FilterHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
{
@@ -30,7 +35,8 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
/**
* Handler or factory callable($record, $this)
*
* @var callable|\Monolog\Handler\HandlerInterface
* @var callable|HandlerInterface
* @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface
*/
protected $handler;
@@ -38,6 +44,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
* Minimum level for logs that are passed to handler
*
* @var int[]
* @phpstan-var Level[]
*/
protected $acceptedLevels;
@@ -49,12 +56,14 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
protected $bubble;
/**
* @psalm-param HandlerInterface|callable(?array, HandlerInterface): HandlerInterface $handler
* @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler
*
* @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler).
* @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided
* @param int|string $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*
* @phpstan-param Level|LevelName|LogLevel::*|array<Level|LevelName|LogLevel::*> $minLevelOrList
*/
public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, bool $bubble = true)
{
@@ -67,6 +76,9 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
}
}
/**
* @phpstan-return Level[]
*/
public function getAcceptedLevels(): array
{
return array_flip($this->acceptedLevels);
@@ -75,6 +87,8 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
/**
* @param int|string|array $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided
* @param int|string $maxLevel Maximum level or level name to accept, only used if $minLevelOrList is not an array
*
* @phpstan-param Level|LevelName|LogLevel::*|array<Level|LevelName|LogLevel::*> $minLevelOrList
*/
public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY): self
{
@@ -141,6 +155,8 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
* If the handler was provided as a factory callable, this will trigger the handler's instantiation.
*
* @return HandlerInterface
*
* @phpstan-param Record $record
*/
public function getHandler(array $record = null)
{

View File

@@ -15,11 +15,15 @@ namespace Monolog\Handler\FingersCrossed;
* Interface for activation strategies for the FingersCrossedHandler.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
interface ActivationStrategyInterface
{
/**
* Returns whether the given record activates the handler.
*
* @phpstan-param Record $record
*/
public function isHandlerActivated(array $record): bool;
}

View File

@@ -32,6 +32,9 @@ use Monolog\Logger;
* </code>
*
* @author Mike Meessen <netmikey@gmail.com>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
*/
class ChannelLevelActivationStrategy implements ActivationStrategyInterface
{
@@ -41,13 +44,15 @@ class ChannelLevelActivationStrategy implements ActivationStrategyInterface
private $defaultActionLevel;
/**
* @var array
* @var array<string, Level>
*/
private $channelToActionLevel;
/**
* @param int|string $defaultActionLevel The default action level to be used if the record's category doesn't match any
* @param array $channelToActionLevel An array that maps channel names to action levels.
* @param int|string $defaultActionLevel The default action level to be used if the record's category doesn't match any
* @param array<string, int> $channelToActionLevel An array that maps channel names to action levels.
*
* @phpstan-param array<string, Level> $channelToActionLevel
*/
public function __construct($defaultActionLevel, array $channelToActionLevel = [])
{
@@ -55,6 +60,9 @@ class ChannelLevelActivationStrategy implements ActivationStrategyInterface
$this->channelToActionLevel = array_map('Monolog\Logger::toMonologLevel', $channelToActionLevel);
}
/**
* @phpstan-param Record $record
*/
public function isHandlerActivated(array $record): bool
{
if (isset($this->channelToActionLevel[$record['channel']])) {

View File

@@ -32,23 +32,40 @@ use Monolog\Formatter\FormatterInterface;
* Monolog\Handler\FingersCrossed\ namespace.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/
class FingersCrossedHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
{
use ProcessableHandlerTrait;
/** @var HandlerInterface */
/**
* @var callable|HandlerInterface
* @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface
*/
protected $handler;
/** @var ActivationStrategyInterface */
protected $activationStrategy;
/** @var bool */
protected $buffering = true;
/** @var int */
protected $bufferSize;
/** @var Record[] */
protected $buffer = [];
/** @var bool */
protected $stopBuffering;
/**
* @var ?int
* @phpstan-var ?Level
*/
protected $passthruLevel;
/** @var bool */
protected $bubble;
/**
* @psalm-param HandlerInterface|callable(?array, FingersCrossedHandler): HandlerInterface $handler
* @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler
*
* @param callable|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler).
* @param int|string|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated
@@ -171,7 +188,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
return $record['level'] >= $level;
});
if (count($this->buffer) > 0) {
$this->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer);
$this->getHandler(end($this->buffer))->handleBatch($this->buffer);
}
}
@@ -185,6 +202,8 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
* If the handler was provided as a factory callable, this will trigger the handler's instantiation.
*
* @return HandlerInterface
*
* @phpstan-param Record $record
*/
public function getHandler(array $record = null)
{

View File

@@ -18,6 +18,8 @@ use Monolog\Formatter\FormatterInterface;
* Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol.
*
* @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/
class FirePHPHandler extends AbstractProcessingHandler
{
@@ -45,6 +47,7 @@ class FirePHPHandler extends AbstractProcessingHandler
/**
* Whether or not Wildfire vendor-specific headers have been generated & sent yet
* @var bool
*/
protected static $initialized = false;
@@ -54,14 +57,15 @@ class FirePHPHandler extends AbstractProcessingHandler
*/
protected static $messageIndex = 1;
/** @var bool */
protected static $sendHeaders = true;
/**
* Base header creation function used by init headers & record headers
*
* @param array $meta Wildfire Plugin, Protocol & Structure Indexes
* @param string $message Log message
* @return array Complete header string ready for the client as key and message as value
* @param array<int|string> $meta Wildfire Plugin, Protocol & Structure Indexes
* @param string $message Log message
* @return array<string, string> Complete header string ready for the client as key and message as value
*/
protected function createHeader(array $meta, string $message): array
{
@@ -73,7 +77,11 @@ class FirePHPHandler extends AbstractProcessingHandler
/**
* Creates message header from record
*
* @return array<string, string>
*
* @see createHeader()
*
* @phpstan-param FormattedRecord $record
*/
protected function createRecordHeader(array $record): array
{
@@ -98,6 +106,8 @@ class FirePHPHandler extends AbstractProcessingHandler
*
* @see createHeader()
* @see sendHeader()
*
* @return array<string, string>
*/
protected function getInitHeaders(): array
{
@@ -124,7 +134,6 @@ class FirePHPHandler extends AbstractProcessingHandler
*
* @see sendHeader()
* @see sendInitHeaders()
* @param array $record
*/
protected function write(array $record): void
{

View File

@@ -22,6 +22,8 @@ use Monolog\Logger;
*
* @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation
* @author Ando Roots <ando@sqroot.eu>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/
class FleepHookHandler extends SocketHandler
{
@@ -104,6 +106,8 @@ class FleepHookHandler extends SocketHandler
/**
* Builds the body of API call
*
* @phpstan-param FormattedRecord $record
*/
private function buildContent(array $record): string
{

View File

@@ -26,6 +26,8 @@ use Monolog\Formatter\FormatterInterface;
*
* @author Dominik Liebler <liebler.dominik@gmail.com>
* @see https://www.flowdock.com/api/push
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/
class FlowdockHandler extends SocketHandler
{
@@ -72,8 +74,6 @@ class FlowdockHandler extends SocketHandler
/**
* {@inheritdoc}
*
* @param array $record
*/
protected function write(array $record): void
{
@@ -94,6 +94,8 @@ class FlowdockHandler extends SocketHandler
/**
* Builds the body of API call
*
* @phpstan-param FormattedRecord $record
*/
private function buildContent(array $record): string
{

View File

@@ -25,6 +25,7 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
/** @var HandlerInterface[] */
protected $handlers;
/** @var bool */
protected $bubble;
/**

View File

@@ -15,6 +15,9 @@ namespace Monolog\Handler;
* Interface that all Monolog Handlers must implement
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
*/
interface HandlerInterface
{
@@ -30,6 +33,8 @@ interface HandlerInterface
* @param array $record Partial log record containing only a level key
*
* @return bool
*
* @phpstan-param array{level: Level} $record
*/
public function isHandling(array $record): bool;
@@ -46,6 +51,8 @@ interface HandlerInterface
* @param array $record The record to handle
* @return bool true means that this handler handled the record, and that bubbling is not permitted.
* false means the record was either not processed or that this handler allows bubbling.
*
* @phpstan-param Record $record
*/
public function handle(array $record): bool;
@@ -53,6 +60,8 @@ interface HandlerInterface
* Handles a set of records at once.
*
* @param array $records The records to handle (an array of record arrays)
*
* @phpstan-param Record[] $records
*/
public function handleBatch(array $records): void;

View File

@@ -27,7 +27,9 @@ use Monolog\Utils;
*/
class IFTTTHandler extends AbstractProcessingHandler
{
/** @var string */
private $eventName;
/** @var string */
private $secretKey;
/**

View File

@@ -37,8 +37,10 @@ class LogglyHandler extends AbstractProcessingHandler
*/
protected $curlHandlers = [];
/** @var string */
protected $token;
/** @var string[] */
protected $tag = [];
/**

View File

@@ -18,6 +18,8 @@ use Monolog\Formatter\HtmlFormatter;
* Base class for all mail handlers
*
* @author Gyula Sallai
*
* @phpstan-import-type Record from \Monolog\Logger
*/
abstract class MailHandler extends AbstractProcessingHandler
{
@@ -45,6 +47,8 @@ abstract class MailHandler extends AbstractProcessingHandler
*
* @param string $content formatted email body to be sent
* @param array $records the array of log records that formed this content
*
* @phpstan-param Record[] $records
*/
abstract protected function send(string $content, array $records): void;
@@ -56,6 +60,10 @@ abstract class MailHandler extends AbstractProcessingHandler
$this->send((string) $record['formatted'], [$record]);
}
/**
* @phpstan-param Record[] $records
* @phpstan-return Record
*/
protected function getHighestRecord(array $records): array
{
$highestRecord = null;

View File

@@ -33,8 +33,11 @@ use Monolog\Formatter\MongoDBFormatter;
*/
class MongoDBHandler extends AbstractProcessingHandler
{
/** @var \MongoDB\Collection */
private $collection;
/** @var Client|Manager */
private $manager;
/** @var string */
private $namespace;
/**

View File

@@ -24,7 +24,7 @@ class NativeMailerHandler extends MailHandler
{
/**
* The email addresses to which the message will be sent
* @var array
* @var string[]
*/
protected $to;
@@ -36,13 +36,13 @@ class NativeMailerHandler extends MailHandler
/**
* Optional headers for the message
* @var array
* @var string[]
*/
protected $headers = [];
/**
* Optional parameters for the message
* @var array
* @var string[]
*/
protected $parameters = [];
@@ -65,7 +65,7 @@ class NativeMailerHandler extends MailHandler
protected $encoding = 'utf-8';
/**
* @param string|array $to The receiver of the mail
* @param string|string[] $to The receiver of the mail
* @param string $subject The subject of the mail
* @param string $from The sender of the mail
* @param string|int $level The minimum logging level at which this handler will be triggered
@@ -84,7 +84,7 @@ class NativeMailerHandler extends MailHandler
/**
* Add headers to the message
*
* @param string|array $headers Custom added headers
* @param string|string[] $headers Custom added headers
*/
public function addHeader($headers): self
{
@@ -101,7 +101,7 @@ class NativeMailerHandler extends MailHandler
/**
* Add parameters to the message
*
* @param string|array $parameters Custom added parameters
* @param string|string[] $parameters Custom added parameters
*/
public function addParameter($parameters): self
{

View File

@@ -30,14 +30,14 @@ class NewRelicHandler extends AbstractProcessingHandler
/**
* Name of the New Relic application that will receive logs from this handler.
*
* @var string|null
* @var ?string
*/
protected $appName;
/**
* Name of the current transaction
*
* @var string|null
* @var ?string
*/
protected $transactionName;
@@ -135,6 +135,8 @@ class NewRelicHandler extends AbstractProcessingHandler
/**
* Returns the appname where this log should be sent. Each log can override the default appname, set in this
* handler's constructor, by providing the appname in it's context.
*
* @param mixed[] $context
*/
protected function getAppName(array $context): ?string
{
@@ -148,6 +150,8 @@ class NewRelicHandler extends AbstractProcessingHandler
/**
* Returns the name of the current transaction. Each log can override the default transaction name, set in this
* handler's constructor, by providing the transaction_name in it's context
*
* @param mixed[] $context
*/
protected function getTransactionName(array $context): ?string
{

View File

@@ -87,10 +87,7 @@ class OverflowHandler extends AbstractHandler implements FormattableHandlerInter
* Unless the bubbling is interrupted (by returning true), the Logger class will keep on
* calling further handlers in the stack with a given log record.
*
* @param array $record The record to handle
*
* @return Boolean true means that this handler handled the record, and that bubbling is not permitted.
* false means the record was either not processed or that this handler allows bubbling.
* {@inheritdoc}
*/
public function handle(array $record): bool
{

View File

@@ -37,9 +37,12 @@ use PhpConsole\Helper;
* PC::debug($_SERVER); // PHP Console debugger for any type of vars
*
* @author Sergey Barbushin https://www.linkedin.com/in/barbushin
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class PHPConsoleHandler extends AbstractProcessingHandler
{
/** @var array<string, mixed> */
private $options = [
'enabled' => true, // bool Is PHP Console server enabled
'classesPartialsTraceIgnore' => ['Monolog\\'], // array Hide calls of classes started with...
@@ -67,10 +70,10 @@ class PHPConsoleHandler extends AbstractProcessingHandler
private $connector;
/**
* @param array $options See \Monolog\Handler\PHPConsoleHandler::$options for more details
* @param Connector|null $connector Instance of \PhpConsole\Connector class (optional)
* @param string|int $level The minimum logging level at which this handler will be triggered.
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not.
* @param array<string, mixed> $options See \Monolog\Handler\PHPConsoleHandler::$options for more details
* @param Connector|null $connector Instance of \PhpConsole\Connector class (optional)
* @param string|int $level The minimum logging level at which this handler will be triggered.
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not.
* @throws \RuntimeException
*/
public function __construct(array $options = [], ?Connector $connector = null, $level = Logger::DEBUG, bool $bubble = true)
@@ -83,6 +86,10 @@ class PHPConsoleHandler extends AbstractProcessingHandler
$this->connector = $this->initConnector($connector);
}
/**
* @param array<string, mixed> $options
* @return array<string, mixed>
*/
private function initOptions(array $options): array
{
$wrongOptions = array_diff(array_keys($options), array_keys($this->options));
@@ -153,6 +160,9 @@ class PHPConsoleHandler extends AbstractProcessingHandler
return $this->connector;
}
/**
* @return array<string, mixed>
*/
public function getOptions(): array
{
return $this->options;
@@ -181,6 +191,9 @@ class PHPConsoleHandler extends AbstractProcessingHandler
}
}
/**
* @phpstan-param Record $record
*/
private function handleDebugRecord(array $record): void
{
$tags = $this->getRecordTags($record);
@@ -191,11 +204,17 @@ class PHPConsoleHandler extends AbstractProcessingHandler
$this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']);
}
/**
* @phpstan-param Record $record
*/
private function handleExceptionRecord(array $record): void
{
$this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']);
}
/**
* @phpstan-param Record $record
*/
private function handleErrorRecord(array $record): void
{
$context = $record['context'];
@@ -209,6 +228,10 @@ class PHPConsoleHandler extends AbstractProcessingHandler
);
}
/**
* @phpstan-param Record $record
* @return string
*/
private function getRecordTags(array &$record)
{
$tags = null;

View File

@@ -44,12 +44,12 @@ class ProcessHandler extends AbstractProcessingHandler
private $cwd;
/**
* @var array
* @var resource[]
*/
private $pipes = [];
/**
* @var array
* @var array<int, string[]>
*/
protected const DESCRIPTOR_SPEC = [
0 => ['pipe', 'r'], // STDIN is a pipe that the child will read from

View File

@@ -17,13 +17,15 @@ use Monolog\Processor\ProcessorInterface;
* Interface to describe loggers that have processors
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
interface ProcessableHandlerInterface
{
/**
* Adds a processor in the stack.
*
* @psalm-param ProcessorInterface|callable(array): array $callback
* @psalm-param ProcessorInterface|callable(Record): Record $callback
*
* @param ProcessorInterface|callable $callback
* @return HandlerInterface self
@@ -33,10 +35,10 @@ interface ProcessableHandlerInterface
/**
* Removes the processor on top of the stack and returns it.
*
* @psalm-return callable(array): array
* @psalm-return ProcessorInterface|callable(Record): Record $callback
*
* @throws \LogicException In case the processor stack is empty
* @return callable
* @return callable|ProcessorInterface
*/
public function popProcessor(): callable;
}

View File

@@ -12,16 +12,20 @@
namespace Monolog\Handler;
use Monolog\ResettableInterface;
use Monolog\Processor\ProcessorInterface;
/**
* Helper trait for implementing ProcessableInterface
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
trait ProcessableHandlerTrait
{
/**
* @var callable[]
* @phpstan-var array<ProcessorInterface|callable(Record): Record>
*/
protected $processors = [];
@@ -49,6 +53,9 @@ trait ProcessableHandlerTrait
/**
* Processes a record.
*
* @phpstan-param Record $record
* @phpstan-return Record
*/
protected function processRecord(array $record): array
{

View File

@@ -19,24 +19,35 @@ use Monolog\Utils;
*
* @author Sebastian Göttschkes <sebastian.goettschkes@googlemail.com>
* @see https://www.pushover.net/api
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/
class PushoverHandler extends SocketHandler
{
/** @var string */
private $token;
/** @var array<int|string> */
private $users;
/** @var ?string */
private $title;
private $user;
/** @var string|int|null */
private $user = null;
/** @var int */
private $retry;
/** @var int */
private $expire;
/** @var int */
private $highPriorityLevel;
/** @var int */
private $emergencyLevel;
/** @var bool */
private $useFormattedMessage = false;
/**
* All parameters that can be sent to Pushover
* @see https://pushover.net/api
* @var array
* @var array<string, bool>
*/
private $parameterNames = [
'token' => true,
@@ -57,7 +68,7 @@ class PushoverHandler extends SocketHandler
/**
* Sounds the api supports by default
* @see https://pushover.net/api#sounds
* @var array
* @var string[]
*/
private $sounds = [
'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming',
@@ -81,6 +92,8 @@ class PushoverHandler extends SocketHandler
* send the same notification to the user.
* @param int $expire The expire parameter specifies how many seconds your notification will continue
* to be retried for (every retry seconds).
*
* @phpstan-param string|array<int|string> $users
*/
public function __construct(
string $token,
@@ -113,6 +126,9 @@ class PushoverHandler extends SocketHandler
return $this->buildHeader($content) . $content;
}
/**
* @phpstan-param FormattedRecord $record
*/
private function buildContent(array $record): string
{
// Pushover has a limit of 512 characters on title and message combined.
@@ -177,6 +193,9 @@ class PushoverHandler extends SocketHandler
$this->user = null;
}
/**
* @param int|string $value
*/
public function setHighPriorityLevel($value): self
{
$this->highPriorityLevel = Logger::toMonologLevel($value);
@@ -184,6 +203,9 @@ class PushoverHandler extends SocketHandler
return $this;
}
/**
* @param int|string $value
*/
public function setEmergencyLevel($value): self
{
$this->emergencyLevel = Logger::toMonologLevel($value);

View File

@@ -25,11 +25,16 @@ use Monolog\Logger;
* $log->pushHandler($redis);
*
* @author Thomas Tourlourat <thomas@tourlourat.com>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/
class RedisHandler extends AbstractProcessingHandler
{
/** @var \Predis\Client|\Redis */
private $redisClient;
/** @var string */
private $redisKey;
/** @var int */
protected $capSize;
/**
@@ -67,6 +72,8 @@ class RedisHandler extends AbstractProcessingHandler
/**
* Write and cap the collection
* Writes the record to the redis list and caps its
*
* @phpstan-param FormattedRecord $record
*/
protected function writeCapped(array $record): void
{

View File

@@ -28,7 +28,9 @@ use Monolog\Logger;
*/
class RedisPubSubHandler extends AbstractProcessingHandler
{
/** @var \Predis\Client|\Redis */
private $redisClient;
/** @var string */
private $channelKey;
/**

View File

@@ -38,6 +38,7 @@ class RollbarHandler extends AbstractProcessingHandler
*/
protected $rollbarLogger;
/** @var string[] */
protected $levelMap = [
Logger::DEBUG => 'debug',
Logger::INFO => 'info',
@@ -56,6 +57,7 @@ class RollbarHandler extends AbstractProcessingHandler
*/
private $hasRecords = false;
/** @var bool */
protected $initialized = false;
/**

View File

@@ -30,11 +30,17 @@ class RotatingFileHandler extends StreamHandler
public const FILE_PER_MONTH = 'Y-m';
public const FILE_PER_YEAR = 'Y';
/** @var string */
protected $filename;
/** @var int */
protected $maxFiles;
/** @var bool */
protected $mustRotate;
/** @var \DateTimeImmutable */
protected $nextRotation;
/** @var string */
protected $filenameFormat;
/** @var string */
protected $dateFormat;
/**

View File

@@ -26,13 +26,17 @@ use Monolog\Formatter\FormatterInterface;
*
* @author Bryan Davis <bd808@wikimedia.org>
* @author Kunal Mehta <legoktm@gmail.com>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
*/
class SamplingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
{
use ProcessableHandlerTrait;
/**
* @var callable|HandlerInterface $handler
* @var HandlerInterface|callable
* @phpstan-var HandlerInterface|callable(Record, HandlerInterface): HandlerInterface
*/
protected $handler;
@@ -42,7 +46,7 @@ class SamplingHandler extends AbstractHandler implements ProcessableHandlerInter
protected $factor;
/**
* @psalm-param HandlerInterface|callable(array, HandlerInterface): HandlerInterface $handler
* @psalm-param HandlerInterface|callable(Record, HandlerInterface): HandlerInterface $handler
*
* @param callable|HandlerInterface $handler Handler or factory callable($record|null, $samplingHandler).
* @param int $factor Sample factor (e.g. 10 means every ~10th record is sampled)
@@ -81,6 +85,8 @@ class SamplingHandler extends AbstractHandler implements ProcessableHandlerInter
*
* If the handler was provided as a factory callable, this will trigger the handler's instantiation.
*
* @phpstan-param Record|array{level: Level}|null $record
*
* @return HandlerInterface
*/
public function getHandler(array $record = null)

View File

@@ -40,7 +40,7 @@ class SendGridHandler extends MailHandler
/**
* The email addresses to which the message will be sent
* @var array
* @var string[]
*/
protected $to;
@@ -51,13 +51,13 @@ class SendGridHandler extends MailHandler
protected $subject;
/**
* @param string $apiUser The SendGrid API User
* @param string $apiKey The SendGrid API Key
* @param string $from The sender of the email
* @param string|array $to The recipients of the email
* @param string $subject The subject of the mail
* @param int|string $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param string $apiUser The SendGrid API User
* @param string $apiKey The SendGrid API Key
* @param string $from The sender of the email
* @param string|string[] $to The recipients of the email
* @param string $subject The subject of the mail
* @param int|string $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/
public function __construct(string $apiUser, string $apiKey, string $from, $to, string $subject, $level = Logger::ERROR, bool $bubble = true)
{

View File

@@ -23,6 +23,8 @@ use Monolog\Formatter\FormatterInterface;
* @author Haralan Dobrev <hkdobrev@gmail.com>
* @see https://api.slack.com/incoming-webhooks
* @see https://api.slack.com/docs/message-attachments
*
* @phpstan-import-type FormattedRecord from \Monolog\Handler\AbstractProcessingHandler
*/
class SlackRecord
{
@@ -72,7 +74,7 @@ class SlackRecord
/**
* Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
* @var array
* @var string[]
*/
private $excludeFields;
@@ -86,6 +88,9 @@ class SlackRecord
*/
private $normalizerFormatter;
/**
* @param string[] $excludeFields
*/
public function __construct(
?string $channel = null,
?string $username = null,
@@ -114,6 +119,9 @@ class SlackRecord
/**
* Returns required data in format that Slack
* is expecting.
*
* @phpstan-param FormattedRecord $record
* @phpstan-return string[]
*/
public function getSlackData(array $record): array
{
@@ -208,6 +216,8 @@ class SlackRecord
/**
* Stringifies an array of key/value pairs to be used in attachment fields
*
* @param mixed[] $fields
*/
public function stringify(array $fields): string
{
@@ -285,6 +295,9 @@ class SlackRecord
return $this;
}
/**
* @param string[] $excludeFields
*/
public function excludeFields(array $excludeFields = []): self
{
$this->excludeFields = $excludeFields;
@@ -302,7 +315,8 @@ class SlackRecord
/**
* Generates attachment field
*
* @param string|array $value
* @param string|mixed[] $value
* @return array{title: string, value: string, short: false}
*/
private function generateAttachmentField(string $title, $value): array
{
@@ -319,6 +333,9 @@ class SlackRecord
/**
* Generates a collection of attachment fields from array
*
* @param mixed[] $data
* @return array<array{title: string, value: string, short: false}>
*/
private function generateAttachmentFields(array $data): array
{
@@ -332,6 +349,9 @@ class SlackRecord
/**
* Get a copy of record with fields excluded according to $this->excludeFields
*
* @phpstan-param FormattedRecord $record
* @return mixed[]
*/
private function removeExcludedFields(array $record): array
{

View File

@@ -21,6 +21,8 @@ use Monolog\Handler\Slack\SlackRecord;
*
* @author Greg Kedzierski <greg@gregkedzierski.com>
* @see https://api.slack.com/
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/
class SlackHandler extends SocketHandler
{
@@ -46,7 +48,7 @@ class SlackHandler extends SocketHandler
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param bool $useShortAttachment Whether the context/extra messages added to Slack as attachments are in a short style
* @param bool $includeContextAndExtra Whether the attachment should include context and extra data
* @param array $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
* @param string[] $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
* @throws MissingExtensionException If no OpenSSL PHP extension configured
*/
public function __construct(
@@ -102,6 +104,8 @@ class SlackHandler extends SocketHandler
/**
* Builds the body of API call
*
* @phpstan-param FormattedRecord $record
*/
private function buildContent(array $record): string
{
@@ -110,6 +114,10 @@ class SlackHandler extends SocketHandler
return http_build_query($dataArray);
}
/**
* @phpstan-param FormattedRecord $record
* @return string[]
*/
protected function prepareContentData(array $record): array
{
$dataArray = $this->slackRecord->getSlackData($record);
@@ -224,6 +232,9 @@ class SlackHandler extends SocketHandler
return $this;
}
/**
* @param string[] $excludeFields
*/
public function excludeFields(array $excludeFields): self
{
$this->slackRecord->excludeFields($excludeFields);

View File

@@ -46,7 +46,7 @@ class SlackWebhookHandler extends AbstractProcessingHandler
* @param bool $includeContextAndExtra Whether the attachment should include context and extra data
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param array $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
* @param string[] $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
*/
public function __construct(
string $webhookUrl,
@@ -87,8 +87,6 @@ class SlackWebhookHandler extends AbstractProcessingHandler
/**
* {@inheritdoc}
*
* @param array $record
*/
protected function write(array $record): void
{

View File

@@ -18,10 +18,15 @@ use Monolog\Logger;
*
* @author Pablo de Leon Belloc <pablolb@gmail.com>
* @see http://php.net/manual/en/function.fsockopen.php
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/
class SocketHandler extends AbstractProcessingHandler
{
/** @var string */
private $connectionString;
/** @var float */
private $connectionTimeout;
/** @var resource|null */
private $resource;
@@ -29,14 +34,18 @@ class SocketHandler extends AbstractProcessingHandler
private $timeout = 0.0;
/** @var float */
private $writingTimeout = 10.0;
/** @var ?int */
private $lastSentBytes = null;
/** @var int */
private $chunkSize = null;
/** @var bool */
private $persistent = false;
private $errno;
private $errstr;
/** @var ?int */
private $errno = null;
/** @var ?string */
private $errstr = null;
/** @var ?float */
private $lastWritingAt;
private $lastWritingAt = null;
/**
* @param string $connectionString Socket connection string
@@ -53,7 +62,7 @@ class SocketHandler extends AbstractProcessingHandler
/**
* Connect (if necessary) and write to the socket
*
* @param array $record
* {@inheritDoc}
*
* @throws \UnexpectedValueException
* @throws \RuntimeException
@@ -208,6 +217,8 @@ class SocketHandler extends AbstractProcessingHandler
/**
* Wrapper to allow mocking
*
* @return resource|bool
*/
protected function pfsockopen()
{
@@ -216,6 +227,8 @@ class SocketHandler extends AbstractProcessingHandler
/**
* Wrapper to allow mocking
*
* @return resource|bool
*/
protected function fsockopen()
{
@@ -226,6 +239,8 @@ class SocketHandler extends AbstractProcessingHandler
* Wrapper to allow mocking
*
* @see http://php.net/manual/en/function.stream-set-timeout.php
*
* @return bool
*/
protected function streamSetTimeout()
{
@@ -239,6 +254,8 @@ class SocketHandler extends AbstractProcessingHandler
* Wrapper to allow mocking
*
* @see http://php.net/manual/en/function.stream-set-chunk-size.php
*
* @return int|bool
*/
protected function streamSetChunkSize()
{
@@ -247,29 +264,32 @@ class SocketHandler extends AbstractProcessingHandler
/**
* Wrapper to allow mocking
*
* @return int|bool
*/
protected function fwrite($data)
protected function fwrite(string $data)
{
return @fwrite($this->resource, $data);
}
/**
* Wrapper to allow mocking
*
* @return mixed[]|bool
*/
protected function streamGetMetadata()
{
return stream_get_meta_data($this->resource);
}
private function validateTimeout($value)
private function validateTimeout(float $value): void
{
$ok = filter_var($value, FILTER_VALIDATE_FLOAT);
if ($ok === false || $value < 0) {
if ($value < 0) {
throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got $value)");
}
}
private function connectIfNotConnected()
private function connectIfNotConnected(): void
{
if ($this->isConnected()) {
return;
@@ -277,6 +297,9 @@ class SocketHandler extends AbstractProcessingHandler
$this->connect();
}
/**
* @phpstan-param FormattedRecord $record
*/
protected function generateDataStream(array $record): string
{
return (string) $record['formatted'];

View File

@@ -41,9 +41,7 @@ class SqsHandler extends AbstractProcessingHandler
}
/**
* Writes the record down to the log of the implementing handler.
*
* @param array $record
* {@inheritdoc}
*/
protected function write(array $record): void
{

View File

@@ -20,6 +20,8 @@ use Monolog\Utils;
* Can be used to store into php://stderr, remote and local files, etc.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/
class StreamHandler extends AbstractProcessingHandler
{
@@ -27,12 +29,16 @@ class StreamHandler extends AbstractProcessingHandler
/** @var resource|null */
protected $stream;
protected $url;
/** @var string|null */
private $errorMessage;
/** @var ?string */
protected $url = null;
/** @var ?string */
private $errorMessage = null;
/** @var ?int */
protected $filePermission;
/** @var bool */
protected $useLocking;
private $dirCreated;
/** @var true|null */
private $dirCreated = null;
/**
* @param resource|string $stream If a missing path can't be created, an UnexpectedValueException will be thrown on first write
@@ -132,13 +138,15 @@ class StreamHandler extends AbstractProcessingHandler
* Write to stream
* @param resource $stream
* @param array $record
*
* @phpstan-param FormattedRecord $record
*/
protected function streamWrite($stream, array $record): void
{
fwrite($stream, (string) $record['formatted']);
}
private function customErrorHandler($code, $msg): bool
private function customErrorHandler(int $code, string $msg): bool
{
$this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg);

View File

@@ -21,14 +21,18 @@ use Swift;
* SwiftMailerHandler uses Swift_Mailer to send the emails
*
* @author Gyula Sallai
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class SwiftMailerHandler extends MailHandler
{
/** @var \Swift_Mailer */
protected $mailer;
/** @var Swift_Message|callable(string, Record[]): Swift_Message */
private $messageTemplate;
/**
* @psalm-param Swift_Message|callable(string, array): Swift_Message $message
* @psalm-param Swift_Message|callable(string, Record[]): Swift_Message $message
*
* @param \Swift_Mailer $mailer The mailer to use
* @param callable|Swift_Message $message An example message for real messages, only the body will be replaced
@@ -67,6 +71,8 @@ class SwiftMailerHandler extends MailHandler
* @param string $content formatted email body to be sent
* @param array $records Log records that formed the content
* @return Swift_Message
*
* @phpstan-param Record[] $records
*/
protected function buildMessage(string $content, array $records): Swift_Message
{

View File

@@ -28,7 +28,9 @@ use Monolog\Logger;
*/
class SyslogHandler extends AbstractSyslogHandler
{
/** @var string */
protected $ident;
/** @var int */
protected $logopts;
/**

View File

@@ -39,6 +39,11 @@ class UdpSocket
$this->socket = socket_create($domain, SOCK_DGRAM, $protocol) ?: null;
}
/**
* @param string $line
* @param string $header
* @return void
*/
public function write($line, $header = "")
{
$this->send($this->assembleMessage($line, $header));

View File

@@ -27,14 +27,18 @@ class SyslogUdpHandler extends AbstractSyslogHandler
const RFC5424 = 1;
const RFC5424e = 2;
/** @var array<self::RFC*, string> */
private $dateFormats = array(
self::RFC3164 => 'M d H:i:s',
self::RFC5424 => \DateTime::RFC3339,
self::RFC5424e => \DateTime::RFC3339_EXTENDED,
);
/** @var UdpSocket */
protected $socket;
/** @var string */
protected $ident;
/** @var self::RFC* */
protected $rfc;
/**
@@ -45,6 +49,8 @@ class SyslogUdpHandler extends AbstractSyslogHandler
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param string $ident Program name or tag for each log message.
* @param int $rfc RFC to format the message for.
*
* @phpstan-param self::RFC* $rfc
*/
public function __construct(string $host, int $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true, string $ident = 'php', int $rfc = self::RFC5424)
{
@@ -72,6 +78,10 @@ class SyslogUdpHandler extends AbstractSyslogHandler
$this->socket->close();
}
/**
* @param string|string[] $message
* @return string[]
*/
private function splitMessageIntoLines($message): array
{
if (is_array($message)) {

View File

@@ -33,7 +33,7 @@ class TelegramBotHandler extends AbstractProcessingHandler
private const BOT_API = 'https://api.telegram.org/bot';
/**
* @var array AVAILABLE_PARSE_MODES The available values of parseMode according to the Telegram api documentation
* The available values of parseMode according to the Telegram api documentation
*/
private const AVAILABLE_PARSE_MODES = [
'HTML',
@@ -59,19 +59,19 @@ class TelegramBotHandler extends AbstractProcessingHandler
* The kind of formatting that is used for the message.
* See available options at https://core.telegram.org/bots/api#formatting-options
* or in AVAILABLE_PARSE_MODES
* @var string|null
* @var ?string
*/
private $parseMode;
/**
* Disables link previews for links in the message.
* @var bool|null
* @var ?bool
*/
private $disableWebPagePreview;
/**
* Sends the message silently. Users will receive a notification with no sound.
* @var bool|null
* @var ?bool
*/
private $disableNotification;

View File

@@ -12,6 +12,7 @@
namespace Monolog\Handler;
use Monolog\Logger;
use Psr\Log\LogLevel;
/**
* Used for testing purposes.
@@ -64,24 +65,42 @@ use Monolog\Logger;
* @method bool hasNoticeThatPasses($message)
* @method bool hasInfoThatPasses($message)
* @method bool hasDebugThatPasses($message)
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/
class TestHandler extends AbstractProcessingHandler
{
/** @var Record[] */
protected $records = [];
/** @var array<Level, Record[]> */
protected $recordsByLevel = [];
/** @var bool */
private $skipReset = false;
/**
* @return array
*
* @phpstan-return Record[]
*/
public function getRecords()
{
return $this->records;
}
/**
* @return void
*/
public function clear()
{
$this->records = [];
$this->recordsByLevel = [];
}
/**
* @return void
*/
public function reset()
{
if (!$this->skipReset) {
@@ -89,6 +108,9 @@ class TestHandler extends AbstractProcessingHandler
}
}
/**
* @return void
*/
public function setSkipReset(bool $skipReset)
{
$this->skipReset = $skipReset;
@@ -105,6 +127,9 @@ class TestHandler extends AbstractProcessingHandler
/**
* @param string|array $record Either a message string or an array containing message and optionally context keys that will be checked against all records
* @param string|int $level Logging level value or name
*
* @phpstan-param array{message: string, context?: mixed[]}|string $record
* @phpstan-param Level|LevelName|LogLevel::* $level
*/
public function hasRecord($record, $level): bool
{
@@ -136,6 +161,8 @@ class TestHandler extends AbstractProcessingHandler
/**
* @param string|int $level Logging level value or name
*
* @phpstan-param Level|LevelName|LogLevel::* $level
*/
public function hasRecordThatMatches(string $regex, $level): bool
{
@@ -145,10 +172,11 @@ class TestHandler extends AbstractProcessingHandler
}
/**
* @psalm-param callable(array, int): mixed $predicate
*
* @param string|int $level Logging level value or name
* @return bool
*
* @psalm-param callable(Record, int): mixed $predicate
* @phpstan-param Level|LevelName|LogLevel::* $level
*/
public function hasRecordThatPasses(callable $predicate, $level)
{
@@ -176,6 +204,11 @@ class TestHandler extends AbstractProcessingHandler
$this->records[] = $record;
}
/**
* @param string $method
* @param mixed[] $args
* @return bool
*/
public function __call($method, $args)
{
if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) {

View File

@@ -20,13 +20,15 @@ use Monolog\Logger;
*
* @author Christian Bergau <cbergau86@gmail.com>
* @author Jason Davis <happydude@jasondavis.net>
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
*/
class ZendMonitorHandler extends AbstractProcessingHandler
{
/**
* Monolog level / ZendMonitor Custom Event priority map
*
* @var array
* @var array<int, int>
*/
protected $levelMap = [];
@@ -75,6 +77,8 @@ class ZendMonitorHandler extends AbstractProcessingHandler
* @param string $message Text displayed in "Error String"
* @param array $formatted Displayed in Custom Variables tab
* @param int $severity Set the event severity level (-1,0,1)
*
* @phpstan-param FormattedRecord $formatted
*/
protected function writeZendMonitorCustomEvent(string $type, string $message, array $formatted, int $severity): void
{
@@ -89,6 +93,9 @@ class ZendMonitorHandler extends AbstractProcessingHandler
return new NormalizerFormatter();
}
/**
* @return array<int, int>
*/
public function getLevelMap(): array
{
return $this->levelMap;

View File

@@ -15,6 +15,7 @@ use DateTimeZone;
use Monolog\Handler\HandlerInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;
use Throwable;
/**
@@ -24,6 +25,10 @@ use Throwable;
* and uses them to store records that are added to it.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-type Level Logger::DEBUG|Logger::INFO|Logger::NOTICE|Logger::WARNING|Logger::ERROR|Logger::CRITICAL|Logger::ALERT|Logger::EMERGENCY
* @phpstan-type LevelName 'DEBUG'|'INFO'|'NOTICE'|'WARNING'|'ERROR'|'CRITICAL'|'ALERT'|'EMERGENCY'
* @phpstan-type Record array{message: string, context: mixed[], level: Level, level_name: LevelName, channel: string, datetime: \DateTimeImmutable, extra: mixed[]}
*/
class Logger implements LoggerInterface, ResettableInterface
{
@@ -91,6 +96,8 @@ class Logger implements LoggerInterface, ResettableInterface
* This is a static variable and not a constant to serve as an extension point for custom levels
*
* @var array<int, string> $levels Logging levels with the levels as key
*
* @phpstan-var array<Level, LevelName> $levels Logging levels with the levels as key
*/
protected static $levels = [
self::DEBUG => 'DEBUG',
@@ -276,6 +283,8 @@ class Logger implements LoggerInterface, ResettableInterface
* @param string $message The log message
* @param mixed[] $context The log context
* @return bool Whether the record has been processed
*
* @phpstan-param Level $level
*/
public function addRecord(int $level, string $message, array $context = []): bool
{
@@ -383,6 +392,9 @@ class Logger implements LoggerInterface, ResettableInterface
* Gets the name of the logging level.
*
* @throws \Psr\Log\InvalidArgumentException If level is not defined
*
* @phpstan-param Level $level
* @phpstan-return LevelName
*/
public static function getLevelName(int $level): string
{
@@ -398,11 +410,15 @@ class Logger implements LoggerInterface, ResettableInterface
*
* @param string|int $level Level number (monolog) or name (PSR-3)
* @throws \Psr\Log\InvalidArgumentException If level is not defined
*
* @phpstan-param Level|LevelName|LogLevel::* $level
* @phpstan-return Level
*/
public static function toMonologLevel($level): int
{
if (is_string($level)) {
if (is_numeric($level)) {
/** @phpstan-ignore-next-line */
return intval($level);
}
@@ -413,18 +429,21 @@ class Logger implements LoggerInterface, ResettableInterface
return constant(__CLASS__ . '::' . $upper);
}
throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels)));
throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels) + static::$levels));
}
if (!is_int($level)) {
throw new InvalidArgumentException('Level "'.var_export($level, true).'" is not defined, use one of: '.implode(', ', array_keys(static::$levels)));
throw new InvalidArgumentException('Level "'.var_export($level, true).'" is not defined, use one of: '.implode(', ', array_keys(static::$levels) + static::$levels));
}
/** @phpstan-ignore-next-line */
return $level;
}
/**
* Checks whether the Logger has a handler that listens on the given level
*
* @phpstan-param Level $level
*/
public function isHandling(int $level): bool
{
@@ -463,9 +482,11 @@ class Logger implements LoggerInterface, ResettableInterface
*
* This method allows for compatibility with common interfaces.
*
* @param mixed $level The log level
* @param string $message The log message
* @param mixed[] $context The log context
* @param int|string $level The log level
* @param string $message The log message
* @param mixed[] $context The log context
*
* @phpstan-param Level|LevelName|LogLevel::* $level
*/
public function log($level, $message, array $context = []): void
{
@@ -599,6 +620,9 @@ class Logger implements LoggerInterface, ResettableInterface
/**
* Delegates exception management to the custom exception handler,
* or throws the exception if no custom handler is set.
*
* @param array $record
* @phpstan-param Record $record
*/
protected function handleException(Throwable $e, array $record): void
{

View File

@@ -21,8 +21,10 @@ use Monolog\Logger;
*/
class GitProcessor implements ProcessorInterface
{
/** @var int */
private $level;
private static $cache;
/** @var array{branch: string, commit: string}|array<never>|null */
private static $cache = null;
/**
* @param string|int $level The minimum logging level at which this Processor will be triggered
@@ -32,6 +34,9 @@ class GitProcessor implements ProcessorInterface
$this->level = Logger::toMonologLevel($level);
}
/**
* {@inheritDoc}
*/
public function __invoke(array $record): array
{
// return if the level is not high enough
@@ -44,6 +49,9 @@ class GitProcessor implements ProcessorInterface
return $record;
}
/**
* @return array{branch: string, commit: string}|array<never>
*/
private static function getGitInfo(): array
{
if (self::$cache) {

View File

@@ -16,6 +16,7 @@ namespace Monolog\Processor;
*/
class HostnameProcessor implements ProcessorInterface
{
/** @var string */
private static $host;
public function __construct()
@@ -23,6 +24,9 @@ class HostnameProcessor implements ProcessorInterface
self::$host = (string) gethostname();
}
/**
* {@inheritDoc}
*/
public function __invoke(array $record): array
{
$record['extra']['hostname'] = self::$host;

View File

@@ -26,19 +26,21 @@ use Monolog\Logger;
*/
class IntrospectionProcessor implements ProcessorInterface
{
/** @var int */
private $level;
/** @var string[] */
private $skipClassesPartials;
/** @var int */
private $skipStackFramesCount;
/** @var string[] */
private $skipFunctions = [
'call_user_func',
'call_user_func_array',
];
/**
* @param string|int $level The minimum logging level at which this Processor will be triggered
* @param string|int $level The minimum logging level at which this Processor will be triggered
* @param string[] $skipClassesPartials
*/
public function __construct($level = Logger::DEBUG, array $skipClassesPartials = [], int $skipStackFramesCount = 0)
{
@@ -47,6 +49,9 @@ class IntrospectionProcessor implements ProcessorInterface
$this->skipStackFramesCount = $skipStackFramesCount;
}
/**
* {@inheritDoc}
*/
public function __invoke(array $record): array
{
// return if the level is not high enough
@@ -97,7 +102,10 @@ class IntrospectionProcessor implements ProcessorInterface
return $record;
}
private function isTraceClassOrSkippedFunction(array $trace, int $index)
/**
* @param array[] $trace
*/
private function isTraceClassOrSkippedFunction(array $trace, int $index): bool
{
if (!isset($trace[$index])) {
return false;

View File

@@ -19,6 +19,9 @@ namespace Monolog\Processor;
*/
class MemoryPeakUsageProcessor extends MemoryProcessor
{
/**
* {@inheritDoc}
*/
public function __invoke(array $record): array
{
$usage = memory_get_peak_usage($this->realUsage);

View File

@@ -19,6 +19,9 @@ namespace Monolog\Processor;
*/
class MemoryUsageProcessor extends MemoryProcessor
{
/**
* {@inheritDoc}
*/
public function __invoke(array $record): array
{
$usage = memory_get_usage($this->realUsage);

View File

@@ -20,8 +20,10 @@ use Monolog\Logger;
*/
class MercurialProcessor implements ProcessorInterface
{
/** @var int */
private $level;
private static $cache;
/** @var array{branch: string, revision: string}|array<never>|null */
private static $cache = null;
/**
* @param string|int $level The minimum logging level at which this Processor will be triggered
@@ -31,6 +33,9 @@ class MercurialProcessor implements ProcessorInterface
$this->level = Logger::toMonologLevel($level);
}
/**
* {@inheritDoc}
*/
public function __invoke(array $record): array
{
// return if the level is not high enough
@@ -43,6 +48,9 @@ class MercurialProcessor implements ProcessorInterface
return $record;
}
/**
* @return array{branch: string, revision: string}|array<never>
*/
private static function getMercurialInfo(): array
{
if (self::$cache) {

View File

@@ -18,6 +18,9 @@ namespace Monolog\Processor;
*/
class ProcessIdProcessor implements ProcessorInterface
{
/**
* {@inheritDoc}
*/
public function __invoke(array $record): array
{
$record['extra']['process_id'] = getmypid();

View File

@@ -15,11 +15,16 @@ namespace Monolog\Processor;
* An optional interface to allow labelling Monolog processors.
*
* @author Nicolas Grekas <p@tchwork.com>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
interface ProcessorInterface
{
/**
* @return array The processed record
*
* @phpstan-param Record $record
* @phpstan-return Record
*/
public function __invoke(array $record);
}

View File

@@ -41,8 +41,7 @@ class PsrLogMessageProcessor implements ProcessorInterface
}
/**
* @param array $record
* @return array
* {@inheritDoc}
*/
public function __invoke(array $record): array
{

View File

@@ -18,13 +18,20 @@ namespace Monolog\Processor;
*/
class TagProcessor implements ProcessorInterface
{
/** @var string[] */
private $tags;
/**
* @param string[] $tags
*/
public function __construct(array $tags = [])
{
$this->setTags($tags);
}
/**
* @param string[] $tags
*/
public function addTags(array $tags = []): self
{
$this->tags = array_merge($this->tags, $tags);
@@ -32,6 +39,9 @@ class TagProcessor implements ProcessorInterface
return $this;
}
/**
* @param string[] $tags
*/
public function setTags(array $tags = []): self
{
$this->tags = $tags;
@@ -39,6 +49,9 @@ class TagProcessor implements ProcessorInterface
return $this;
}
/**
* {@inheritDoc}
*/
public function __invoke(array $record): array
{
$record['extra']['tags'] = $this->tags;

View File

@@ -20,6 +20,7 @@ use Monolog\ResettableInterface;
*/
class UidProcessor implements ProcessorInterface, ResettableInterface
{
/** @var string */
private $uid;
public function __construct(int $length = 7)
@@ -31,6 +32,9 @@ class UidProcessor implements ProcessorInterface, ResettableInterface
$this->uid = $this->generateUid($length);
}
/**
* {@inheritDoc}
*/
public function __invoke(array $record): array
{
$record['extra']['uid'] = $this->uid;

View File

@@ -19,7 +19,7 @@ namespace Monolog\Processor;
class WebProcessor implements ProcessorInterface
{
/**
* @var array|\ArrayAccess
* @var array<string, mixed>|\ArrayAccess<string, mixed>
*/
protected $serverData;
@@ -28,7 +28,7 @@ class WebProcessor implements ProcessorInterface
*
* Array is structured as [key in record.extra => key in $serverData]
*
* @var array
* @var array<string, string>
*/
protected $extraFields = [
'url' => 'REQUEST_URI',
@@ -39,8 +39,8 @@ class WebProcessor implements ProcessorInterface
];
/**
* @param array|\ArrayAccess|null $serverData Array or object w/ ArrayAccess that provides access to the $_SERVER data
* @param array|null $extraFields Field names and the related key inside $serverData to be added. If not provided it defaults to: url, ip, http_method, server, referrer
* @param array<string, mixed>|\ArrayAccess<string, mixed>|null $serverData Array or object w/ ArrayAccess that provides access to the $_SERVER data
* @param array<string, string>|null $extraFields Field names and the related key inside $serverData to be added. If not provided it defaults to: url, ip, http_method, server, referrer
*/
public function __construct($serverData = null, array $extraFields = null)
{
@@ -69,6 +69,9 @@ class WebProcessor implements ProcessorInterface
}
}
/**
* {@inheritDoc}
*/
public function __invoke(array $record): array
{
// skip processing if for some reason request data
@@ -89,6 +92,10 @@ class WebProcessor implements ProcessorInterface
return $this;
}
/**
* @param mixed[] $extra
* @return mixed[]
*/
private function appendExtraFields(array $extra): array
{
foreach ($this->extraFields as $extraName => $serverName) {

View File

@@ -51,6 +51,7 @@ class Registry
* @param string|null $name Name of the logging channel ($logger->getName() by default)
* @param bool $overwrite Overwrite instance in the registry if the given name already exists?
* @throws \InvalidArgumentException If $overwrite set to false and named Logger instance already exists
* @return void
*/
public static function addLogger(Logger $logger, ?string $name = null, bool $overwrite = false)
{
@@ -122,7 +123,7 @@ class Registry
* Gets Logger instance from the registry via static method call
*
* @param string $name Name of the requested Logger instance
* @param array $arguments Arguments passed to static method call
* @param mixed[] $arguments Arguments passed to static method call
* @throws \InvalidArgumentException If named Logger instance is not in the registry
* @return Logger Requested instance of Logger
*/

View File

@@ -22,10 +22,14 @@ use ReflectionExtension;
*/
class SignalHandler
{
/** @var LoggerInterface */
private $logger;
/** @var array<int, callable|int> SIG_DFL, SIG_IGN or previous callable */
private $previousSignalHandler = [];
/** @var array<int, int> */
private $signalLevelMap = [];
/** @var array<int, bool> */
private $signalRestartSyscalls = [];
public function __construct(LoggerInterface $logger)
@@ -33,12 +37,21 @@ class SignalHandler
$this->logger = $logger;
}
public function registerSignalHandler($signo, $level = LogLevel::CRITICAL, bool $callPrevious = true, bool $restartSyscalls = true, ?bool $async = true): self
/**
* @param int|string $level Level or level name
* @param bool $callPrevious
* @param bool $restartSyscalls
* @param bool|null $async
* @return $this
*/
public function registerSignalHandler(int $signo, $level = LogLevel::CRITICAL, bool $callPrevious = true, bool $restartSyscalls = true, ?bool $async = true): self
{
if (!extension_loaded('pcntl') || !function_exists('pcntl_signal')) {
return $this;
}
$level = Logger::toMonologLevel($level);
if ($callPrevious) {
$handler = pcntl_signal_get_handler($signo);
$this->previousSignalHandler[$signo] = $handler;
@@ -57,7 +70,10 @@ class SignalHandler
return $this;
}
public function handleSignal($signo, array $siginfo = null): void
/**
* @param mixed $siginfo
*/
public function handleSignal(int $signo, $siginfo = null): void
{
static $signals = [];
@@ -80,7 +96,7 @@ class SignalHandler
return;
}
if ($this->previousSignalHandler[$signo] === true || $this->previousSignalHandler[$signo] === SIG_DFL) {
if ($this->previousSignalHandler[$signo] === SIG_DFL) {
if (extension_loaded('pcntl') && function_exists('pcntl_signal') && function_exists('pcntl_sigprocmask') && function_exists('pcntl_signal_dispatch')
&& extension_loaded('posix') && function_exists('posix_getpid') && function_exists('posix_kill')
) {

View File

@@ -17,15 +17,23 @@ use Monolog\Formatter\FormatterInterface;
/**
* Lets you easily generate log records and a dummy formatter for testing purposes
* *
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
*/
class TestCase extends \PHPUnit\Framework\TestCase
{
/**
* @param mixed[] $context
*
* @return array Record
*
* @phpstan-param Level $level
* @phpstan-return Record
*/
protected function getRecord($level = Logger::WARNING, $message = 'test', array $context = []): array
protected function getRecord(int $level = Logger::WARNING, string $message = 'test', array $context = []): array
{
return [
'message' => (string) $message,
@@ -38,6 +46,9 @@ class TestCase extends \PHPUnit\Framework\TestCase
];
}
/**
* @phpstan-return Record[]
*/
protected function getMultipleRecords(): array
{
return [

View File

@@ -54,8 +54,8 @@ class ErrorHandlerTest extends \PHPUnit\Framework\TestCase
public function fatalHandlerProvider()
{
return [
[null, 10, str_repeat(' ', 1024 * 10), null],
[E_ALL, 15, str_repeat(' ', 1024 * 15), E_ALL],
[null, 10, str_repeat(' ', 1024 * 10), LogLevel::ALERT],
[LogLevel::DEBUG, 15, str_repeat(' ', 1024 * 15), LogLevel::DEBUG],
];
}