MemoryProcessor.php 0000644 00000003414 15152100545 0010425 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
/**
* Some methods that are common for all memory processors
*
* @author Rob Jensen
*/
abstract class MemoryProcessor implements ProcessorInterface
{
/**
* @var bool If true, get the real size of memory allocated from system. Else, only the memory used by emalloc() is reported.
*/
protected bool $realUsage;
/**
* @var bool If true, then format memory size to human readable string (MB, KB, B depending on size)
*/
protected bool $useFormatting;
/**
* @param bool $realUsage Set this to true to get the real size of memory allocated from system.
* @param bool $useFormatting If true, then format memory size to human readable string (MB, KB, B depending on size)
*/
public function __construct(bool $realUsage = true, bool $useFormatting = true)
{
$this->realUsage = $realUsage;
$this->useFormatting = $useFormatting;
}
/**
* Formats bytes into a human readable string if $this->useFormatting is true, otherwise return $bytes as is
*
* @return string|int Formatted string if $this->useFormatting is true, otherwise return $bytes as int
*/
protected function formatBytes(int $bytes)
{
if (!$this->useFormatting) {
return $bytes;
}
if ($bytes > 1024 * 1024) {
return round($bytes / 1024 / 1024, 2).' MB';
} elseif ($bytes > 1024) {
return round($bytes / 1024, 2).' KB';
}
return $bytes . ' B';
}
}
ClosureContextProcessor.php 0000644 00000002750 15152100545 0012140 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
use Monolog\LogRecord;
/**
* Generates a context from a Closure if the Closure is the only value
* in the context
*
* It helps reduce the performance impact of debug logs if they do
* need to create lots of context information. If this processor is added
* on the correct handler the context data will only be generated
* when the logs are actually logged to that handler, which is useful when
* using FingersCrossedHandler or other filtering handlers to conditionally
* log records.
*/
class ClosureContextProcessor implements ProcessorInterface
{
public function __invoke(LogRecord $record): LogRecord
{
$context = $record->context;
if (isset($context[0]) && 1 === \count($context) && $context[0] instanceof \Closure) {
try {
$context = $context[0]();
} catch (\Throwable $e) {
$context = [
'error_on_context_generation' => $e->getMessage(),
'exception' => $e,
];
}
if (!\is_array($context)) {
$context = [$context];
}
$record = $record->with(context: $context);
}
return $record;
}
}
TagProcessor.php 0000644 00000002256 15152100545 0007673 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
use Monolog\LogRecord;
/**
* Adds a tags array into record
*
* @author Martijn Riemers
*/
class TagProcessor implements ProcessorInterface
{
/** @var string[] */
private array $tags;
/**
* @param string[] $tags
*/
public function __construct(array $tags = [])
{
$this->setTags($tags);
}
/**
* @param string[] $tags
* @return $this
*/
public function addTags(array $tags = []): self
{
$this->tags = array_merge($this->tags, $tags);
return $this;
}
/**
* @param string[] $tags
* @return $this
*/
public function setTags(array $tags = []): self
{
$this->tags = $tags;
return $this;
}
/**
* @inheritDoc
*/
public function __invoke(LogRecord $record): LogRecord
{
$record->extra['tags'] = $this->tags;
return $record;
}
}
MercurialProcessor.php 0000644 00000003574 15152100545 0011107 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
use Monolog\Level;
use Monolog\Logger;
use Psr\Log\LogLevel;
use Monolog\LogRecord;
/**
* Injects Hg branch and Hg revision number in all records
*
* @author Jonathan A. Schweder <jonathanschweder@gmail.com>
*/
class MercurialProcessor implements ProcessorInterface
{
private Level $level;
/** @var array{branch: string, revision: string}|array<never>|null */
private static $cache = null;
/**
* @param int|string|Level $level The minimum logging level at which this Processor will be triggered
*
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
*/
public function __construct(int|string|Level $level = Level::Debug)
{
$this->level = Logger::toMonologLevel($level);
}
/**
* @inheritDoc
*/
public function __invoke(LogRecord $record): LogRecord
{
// return if the level is not high enough
if ($record->level->isLowerThan($this->level)) {
return $record;
}
$record->extra['hg'] = self::getMercurialInfo();
return $record;
}
/**
* @return array{branch: string, revision: string}|array<never>
*/
private static function getMercurialInfo(): array
{
if (self::$cache !== null) {
return self::$cache;
}
$result = explode(' ', trim((string) shell_exec('hg id -nb')));
if (count($result) >= 3) {
return self::$cache = [
'branch' => $result[1],
'revision' => $result[2],
];
}
return self::$cache = [];
}
}
LoadAverageProcessor.php 0000644 00000003135 15152100545 0011327 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
use Monolog\LogRecord;
/**
* Injects sys_getloadavg in all records @see https://www.php.net/manual/en/function.sys-getloadavg.php
*
* @author Johan Vlaar <johan.vlaar.1994@gmail.com>
*/
class LoadAverageProcessor implements ProcessorInterface
{
public const LOAD_1_MINUTE = 0;
public const LOAD_5_MINUTE = 1;
public const LOAD_15_MINUTE = 2;
private const AVAILABLE_LOAD = [
self::LOAD_1_MINUTE,
self::LOAD_5_MINUTE,
self::LOAD_15_MINUTE,
];
/**
* @var int
*/
protected $avgSystemLoad;
/**
* @param self::LOAD_* $avgSystemLoad
*/
public function __construct(int $avgSystemLoad = self::LOAD_1_MINUTE)
{
if (!in_array($avgSystemLoad, self::AVAILABLE_LOAD, true)) {
throw new \InvalidArgumentException(sprintf('Invalid average system load: `%s`', $avgSystemLoad));
}
$this->avgSystemLoad = $avgSystemLoad;
}
/**
* {@inheritDoc}
*/
public function __invoke(LogRecord $record): LogRecord
{
if (!function_exists('sys_getloadavg')) {
return $record;
}
$usage = sys_getloadavg();
if (false === $usage) {
return $record;
}
$record->extra['load_average'] = $usage[$this->avgSystemLoad];
return $record;
}
}
UidProcessor.php 0000644 00000002724 15152100545 0007701 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
use Monolog\ResettableInterface;
use Monolog\LogRecord;
/**
* Adds a unique identifier into records
*
* @author Simon Mönch <sm@webfactory.de>
*/
class UidProcessor implements ProcessorInterface, ResettableInterface
{
/** @var non-empty-string */
private string $uid;
/**
* @param int<1, 32> $length
*/
public function __construct(int $length = 7)
{
if ($length > 32 || $length < 1) {
throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32');
}
$this->uid = $this->generateUid($length);
}
/**
* @inheritDoc
*/
public function __invoke(LogRecord $record): LogRecord
{
$record->extra['uid'] = $this->uid;
return $record;
}
public function getUid(): string
{
return $this->uid;
}
public function reset(): void
{
$this->uid = $this->generateUid(strlen($this->uid));
}
/**
* @param positive-int $length
* @return non-empty-string
*/
private function generateUid(int $length): string
{
return substr(bin2hex(random_bytes((int) ceil($length / 2))), 0, $length);
}
}
ProcessorInterface.php 0000644 00000001100 15152100545 0011043 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
use Monolog\LogRecord;
/**
* An optional interface to allow labelling Monolog processors.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
interface ProcessorInterface
{
/**
* @return LogRecord The processed record
*/
public function __invoke(LogRecord $record);
}
MemoryPeakUsageProcessor.php 0000644 00000001540 15152100545 0012211 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
use Monolog\LogRecord;
/**
* Injects memory_get_peak_usage in all records
*
* @see Monolog\Processor\MemoryProcessor::__construct() for options
* @author Rob Jensen
*/
class MemoryPeakUsageProcessor extends MemoryProcessor
{
/**
* @inheritDoc
*/
public function __invoke(LogRecord $record): LogRecord
{
$usage = memory_get_peak_usage($this->realUsage);
if ($this->useFormatting) {
$usage = $this->formatBytes($usage);
}
$record->extra['memory_peak_usage'] = $usage;
return $record;
}
}
IntrospectionProcessor.php 0000644 00000007205 15152100545 0012017 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
use Monolog\Level;
use Monolog\Logger;
use Psr\Log\LogLevel;
use Monolog\LogRecord;
/**
* Injects line/file:class/function where the log message came from
*
* Warning: This only works if the handler processes the logs directly.
* If you put the processor on a handler that is behind a FingersCrossedHandler
* for example, the processor will only be called once the trigger level is reached,
* and all the log records will have the same file/line/.. data from the call that
* triggered the FingersCrossedHandler.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class IntrospectionProcessor implements ProcessorInterface
{
private Level $level;
/** @var string[] */
private array $skipClassesPartials;
private int $skipStackFramesCount;
private const SKIP_FUNCTIONS = [
'call_user_func',
'call_user_func_array',
];
/**
* @param string|int|Level $level The minimum logging level at which this Processor will be triggered
* @param string[] $skipClassesPartials
*
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
*/
public function __construct(int|string|Level $level = Level::Debug, array $skipClassesPartials = [], int $skipStackFramesCount = 0)
{
$this->level = Logger::toMonologLevel($level);
$this->skipClassesPartials = array_merge(['Monolog\\'], $skipClassesPartials);
$this->skipStackFramesCount = $skipStackFramesCount;
}
/**
* @inheritDoc
*/
public function __invoke(LogRecord $record): LogRecord
{
// return if the level is not high enough
if ($record->level->isLowerThan($this->level)) {
return $record;
}
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// skip first since it's always the current method
array_shift($trace);
// the call_user_func call is also skipped
array_shift($trace);
$i = 0;
while ($this->isTraceClassOrSkippedFunction($trace, $i)) {
if (isset($trace[$i]['class'])) {
foreach ($this->skipClassesPartials as $part) {
if (strpos($trace[$i]['class'], $part) !== false) {
$i++;
continue 2;
}
}
} elseif (in_array($trace[$i]['function'], self::SKIP_FUNCTIONS, true)) {
$i++;
continue;
}
break;
}
$i += $this->skipStackFramesCount;
// we should have the call source now
$record->extra = array_merge(
$record->extra,
[
'file' => $trace[$i - 1]['file'] ?? null,
'line' => $trace[$i - 1]['line'] ?? null,
'class' => $trace[$i]['class'] ?? null,
'callType' => $trace[$i]['type'] ?? null,
'function' => $trace[$i]['function'] ?? null,
]
);
return $record;
}
/**
* @param array<mixed> $trace
*/
private function isTraceClassOrSkippedFunction(array $trace, int $index): bool
{
if (!isset($trace[$index])) {
return false;
}
return isset($trace[$index]['class']) || in_array($trace[$index]['function'], self::SKIP_FUNCTIONS, true);
}
}
ProcessIdProcessor.php 0000644 00000001173 15152100545 0011050 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
use Monolog\LogRecord;
/**
* Adds value of getmypid into records
*
* @author Andreas Hörnicke
*/
class ProcessIdProcessor implements ProcessorInterface
{
/**
* @inheritDoc
*/
public function __invoke(LogRecord $record): LogRecord
{
$record->extra['process_id'] = getmypid();
return $record;
}
}
GitProcessor.php 0000644 00000003677 15152100545 0007713 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
use Monolog\Level;
use Monolog\Logger;
use Psr\Log\LogLevel;
use Monolog\LogRecord;
/**
* Injects Git branch and Git commit SHA in all records
*
* @author Nick Otter
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class GitProcessor implements ProcessorInterface
{
private Level $level;
/** @var array{branch: string, commit: string}|array<never>|null */
private static $cache = null;
/**
* @param int|string|Level|LogLevel::* $level The minimum logging level at which this Processor will be triggered
*
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
*/
public function __construct(int|string|Level $level = Level::Debug)
{
$this->level = Logger::toMonologLevel($level);
}
/**
* @inheritDoc
*/
public function __invoke(LogRecord $record): LogRecord
{
// return if the level is not high enough
if ($record->level->isLowerThan($this->level)) {
return $record;
}
$record->extra['git'] = self::getGitInfo();
return $record;
}
/**
* @return array{branch: string, commit: string}|array<never>
*/
private static function getGitInfo(): array
{
if (self::$cache !== null) {
return self::$cache;
}
$branches = shell_exec('git branch -v --no-abbrev');
if (is_string($branches) && 1 === preg_match('{^\* (.+?)\s+([a-f0-9]{40})(?:\s|$)}m', $branches, $matches)) {
return self::$cache = [
'branch' => $matches[1],
'commit' => $matches[2],
];
}
return self::$cache = [];
}
}
HostnameProcessor.php 0000644 00000001340 15152100545 0010727 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
use Monolog\LogRecord;
/**
* Injects value of gethostname in all records
*/
class HostnameProcessor implements ProcessorInterface
{
private static string $host;
public function __construct()
{
self::$host = (string) gethostname();
}
/**
* @inheritDoc
*/
public function __invoke(LogRecord $record): LogRecord
{
$record->extra['hostname'] = self::$host;
return $record;
}
}
PsrLogMessageProcessor.php 0000644 00000005760 15152100545 0011676 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
use Monolog\Utils;
use Monolog\LogRecord;
/**
* Processes a record's message according to PSR-3 rules
*
* It replaces {foo} with the value from $context['foo']
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class PsrLogMessageProcessor implements ProcessorInterface
{
public const SIMPLE_DATE = "Y-m-d\TH:i:s.uP";
private ?string $dateFormat;
private bool $removeUsedContextFields;
/**
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
* @param bool $removeUsedContextFields If set to true the fields interpolated into message gets unset
*/
public function __construct(?string $dateFormat = null, bool $removeUsedContextFields = false)
{
$this->dateFormat = $dateFormat;
$this->removeUsedContextFields = $removeUsedContextFields;
}
/**
* @inheritDoc
*/
public function __invoke(LogRecord $record): LogRecord
{
if (false === strpos($record->message, '{')) {
return $record;
}
$replacements = [];
$context = $record->context;
foreach ($context as $key => $val) {
$placeholder = '{' . $key . '}';
if (strpos($record->message, $placeholder) === false) {
continue;
}
if (null === $val || is_scalar($val) || (is_object($val) && method_exists($val, "__toString"))) {
$replacements[$placeholder] = $val;
} elseif ($val instanceof \DateTimeInterface) {
if (null === $this->dateFormat && $val instanceof \Monolog\DateTimeImmutable) {
// handle monolog dates using __toString if no specific dateFormat was asked for
// so that it follows the useMicroseconds flag
$replacements[$placeholder] = (string) $val;
} else {
$replacements[$placeholder] = $val->format($this->dateFormat ?? static::SIMPLE_DATE);
}
} elseif ($val instanceof \UnitEnum) {
$replacements[$placeholder] = $val instanceof \BackedEnum ? $val->value : $val->name;
} elseif (is_object($val)) {
$replacements[$placeholder] = '[object '.Utils::getClass($val).']';
} elseif (is_array($val)) {
$replacements[$placeholder] = 'array'.Utils::jsonEncode($val, null, true);
} else {
$replacements[$placeholder] = '['.gettype($val).']';
}
if ($this->removeUsedContextFields) {
unset($context[$key]);
}
}
return $record->with(message: strtr($record->message, $replacements), context: $context);
}
}
WebProcessor.php 0000644 00000006616 15152100545 0007701 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
use ArrayAccess;
use Monolog\LogRecord;
/**
* Injects url/method and remote IP of the current web request in all records
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class WebProcessor implements ProcessorInterface
{
/**
* @var array<string, mixed>|ArrayAccess<string, mixed>
*/
protected array|ArrayAccess $serverData;
/**
* Default fields
*
* Array is structured as [key in record.extra => key in $serverData]
*
* @var array<string, string>
*/
protected array $extraFields = [
'url' => 'REQUEST_URI',
'ip' => 'REMOTE_ADDR',
'http_method' => 'REQUEST_METHOD',
'server' => 'SERVER_NAME',
'referrer' => 'HTTP_REFERER',
'user_agent' => 'HTTP_USER_AGENT',
];
/**
* @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>|array<string>|null $extraFields Field names and the related key inside $serverData to be added (or just a list of field names to use the default configured $serverData mapping). If not provided it defaults to: [url, ip, http_method, server, referrer] + unique_id if present in server data
*/
public function __construct(array|ArrayAccess|null $serverData = null, array|null $extraFields = null)
{
if (null === $serverData) {
$this->serverData = &$_SERVER;
} else {
$this->serverData = $serverData;
}
$defaultEnabled = ['url', 'ip', 'http_method', 'server', 'referrer'];
if (isset($this->serverData['UNIQUE_ID'])) {
$this->extraFields['unique_id'] = 'UNIQUE_ID';
$defaultEnabled[] = 'unique_id';
}
if (null === $extraFields) {
$extraFields = $defaultEnabled;
}
if (isset($extraFields[0])) {
foreach (array_keys($this->extraFields) as $fieldName) {
if (!in_array($fieldName, $extraFields, true)) {
unset($this->extraFields[$fieldName]);
}
}
} else {
$this->extraFields = $extraFields;
}
}
/**
* @inheritDoc
*/
public function __invoke(LogRecord $record): LogRecord
{
// skip processing if for some reason request data
// is not present (CLI or wonky SAPIs)
if (!isset($this->serverData['REQUEST_URI'])) {
return $record;
}
$record->extra = $this->appendExtraFields($record->extra);
return $record;
}
/**
* @return $this
*/
public function addExtraField(string $extraName, string $serverName): self
{
$this->extraFields[$extraName] = $serverName;
return $this;
}
/**
* @param mixed[] $extra
* @return mixed[]
*/
private function appendExtraFields(array $extra): array
{
foreach ($this->extraFields as $extraName => $serverName) {
$extra[$extraName] = $this->serverData[$serverName] ?? null;
}
return $extra;
}
}
MemoryUsageProcessor.php 0000644 00000001515 15152100545 0011412 0 ustar 00 <?php declare(strict_types=1);
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Processor;
use Monolog\LogRecord;
/**
* Injects memory_get_usage in all records
*
* @see Monolog\Processor\MemoryProcessor::__construct() for options
* @author Rob Jensen
*/
class MemoryUsageProcessor extends MemoryProcessor
{
/**
* @inheritDoc
*/
public function __invoke(LogRecord $record): LogRecord
{
$usage = memory_get_usage($this->realUsage);
if ($this->useFormatting) {
$usage = $this->formatBytes($usage);
}
$record->extra['memory_usage'] = $usage;
return $record;
}
}