diff --git a/CHANGELOG.md b/CHANGELOG.md index a185cac8..d7839c00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ `CategoryFilter::DEFAULT` in favor it (@vjik) - New #113: Add `Message::trace()` method (@vjik) - Enh #113: Remove unnecessary `unset` call in `ContextProvider` (@vjik) +- New #114: Add `Message::time()` method (@vjik) ## 2.0.0 May 22, 2022 diff --git a/src/Message.php b/src/Message.php index 8010787b..9a200b20 100644 --- a/src/Message.php +++ b/src/Message.php @@ -4,6 +4,9 @@ namespace Yiisoft\Log; +use DateTimeImmutable; +use DateTimeInterface; +use Exception; use LogicException; use Psr\Log\InvalidArgumentException; use Psr\Log\LoggerTrait; @@ -48,11 +51,16 @@ final class Message * * - category: string, message category. * - memory: int, memory usage in bytes, obtained by `memory_get_usage()`. - * - time: float, message timestamp obtained by microtime(true). + * - time: float, message timestamp obtained by `microtime(true)`. * - trace: array, debug backtrace, contains the application code call stacks. */ private array $context; + /** + * Default time to use when the time is not set in the context. + */ + private DateTimeImmutable $defaultTime; + /** * @param string $level Log message level. * @param string|Stringable $message Log message. @@ -69,6 +77,7 @@ public function __construct(string $level, string|Stringable $message, array $co $this->level = $level; $this->message = $this->parse($message, $context); $this->context = $context; + $this->defaultTime = new DateTimeImmutable(); } /** @@ -147,6 +156,43 @@ public function trace(): ?array return $trace; } + /** + * Returns the time of the log message. + * + * @return DateTimeImmutable The log message time. + */ + public function time(): DateTimeImmutable + { + $time = $this->context['time'] ?? $this->defaultTime; + + if ($time instanceof DateTimeInterface) { + return DateTimeImmutable::createFromInterface($time); + } + + if (is_int($time) || is_float($time)) { + try { + return new DateTimeImmutable('@' . $time); + } catch (Exception $e) { + throw new LogicException('Invalid time value in log context: ' . $time . '.', previous: $e); + } + } + + if (is_string($time)) { + $format = match (true) { + str_contains($time, '.') => 'U.u', + str_contains($time, ',') => 'U,u', + default => 'U', + }; + $date = DateTimeImmutable::createFromFormat($format, $time); + if ($date === false) { + throw new LogicException('Invalid time value in log context: "' . $time . '".'); + } + return $date; + } + + throw new LogicException('Invalid time value in log context. Got "' . get_debug_type($time) . '".'); + } + /** * Parses log message resolving placeholders in the form: "{foo}", * where foo will be replaced by the context data in key "foo". diff --git a/src/Message/Formatter.php b/src/Message/Formatter.php index a90afc6b..00698dbd 100644 --- a/src/Message/Formatter.php +++ b/src/Message/Formatter.php @@ -4,10 +4,6 @@ namespace Yiisoft\Log\Message; -use DateTime; -use DateTimeInterface; -use Exception; -use LogicException; use RuntimeException; use Yiisoft\Log\Message; use Yiisoft\VarDumper\VarDumper; @@ -124,51 +120,13 @@ public function format(Message $message, array $commonContext): string */ private function defaultFormat(Message $message, array $commonContext): string { - $time = $this->getTime($message); + $time = $message->time()->format($this->timestampFormat); $prefix = $this->getPrefix($message, $commonContext); $context = $this->getContext($message, $commonContext); return "{$time} {$prefix}[{$message->level()}][{$message->category()}] {$message->message()}{$context}"; } - /** - * Gets formatted timestamp for message, according to {@see Formatter::$timestampFormat}. - * - * @param Message $message The log message. - * - * @return string Formatted timestamp for message. - */ - private function getTime(Message $message): string - { - $time = $message->context('time'); - - if (is_int($time) || is_float($time)) { - try { - $date = new DateTime('@' . $time); - } catch (Exception $e) { - throw new LogicException('Invalid time value in log context: ' . $time . '.', previous: $e); - } - } elseif (is_string($time)) { - $format = match (true) { - str_contains($time, '.') => 'U.u', - str_contains($time, ',') => 'U,u', - default => 'U', - }; - $date = DateTime::createFromFormat($format, $time); - if ($date === false) { - throw new LogicException('Invalid time value in log context: "' . $time . '".'); - } - } elseif ($time instanceof DateTimeInterface) { - $date = $time; - } elseif ($time === null) { - $date = new DateTime(); - } else { - throw new LogicException('Invalid time value in log context. Got "' . get_debug_type($time) . '".'); - } - - return $date->format($this->timestampFormat); - } - /** * Gets a string to be prefixed to the given message. *