Skip to content

Общий интерфейс логирования

Этот документ описывает общий интерфейс для библиотек логирования.

Основная цель — позволить библиотекам получать объект Psr\Log\LoggerInterface и записывать в него логи простым и универсальным способом. Фреймворки и CMS, которые имеют индивидуальные потребности, МОГУТ реализовывать интерфейс для своих собственных целей, но ДОЛЖНЫ оставаться совместимыми с ним. Это гарантирует, что сторонние библиотеки, которые использует приложение, могут записывать в централизованный лог этого приложения.

Ключевые слова «ДОЛЖЕН», «НЕ ДОЛЖЕН», «ТРЕБУЕТСЯ», «СЛЕДУЕТ», "НЕ ДОЛЖЕН", "РЕКОМЕНДУЕТСЯ", "МОЖЕТ" и "ДОПОЛНИТЕЛЬНО" в этом документе должны быть интерпретированы, как описано в RFC 2119.

Слово разработчик в этом документе должно интерпретироваться как кто-то, кто реализует интерфейс LoggerInterface в сторонней библиотеке или фреймворке. Пользователи, которые используют логгер, называются словом пользователь.

1. Спецификация

1.1 Основы

  • Интерфейс LoggerInterface предоставляет восемь методов для записи логов в восемь RFC 5424 уровней (debug (отладка), info (информация), notice (уведомление), warning (предупреждение), error ( ошибка), critical (критическая ошибка), alert (предупреждение), emergency (чрезвычайная ситуация)).

  • Девятый метод, log, принимает уровень журнала в качестве первого аргумента. Вызов этого метода с одной из констант уровня журнала ДОЛЖЕН иметь тот же результат, что и вызов специфичного для уровня метода. Вызов этого метода с уровнем, который не определен в этой спецификации, ДОЛЖЕН генерировать исключение Psr\Log\InvalidArgumentException если реализация не знает об этом уровне. Пользователи НЕ ДОЛЖНЫ использовать пользовательский уровень, не зная наверняка, что текущая реализация поддерживает этот уровень.

1.2 Сообщение

  • Каждый метод принимает в качестве сообщения строку или объект с реализованным методом __toString().
  • Разработчики МОГУТ реализовывать специальную обработку переданного объекта. Если это не так, разработчики ДОЛЖНЫ преобразовать его в строку.

  • Сообщение МОЖЕТ содержать заполнители, которые разработчики МОГУТ заменить на значения из контекстного массива.

    • Имена плейсхолдеров ДОЛЖНЫ соответствовать ключам в контекстном массиве.

    • Имена плейсхолдеров ДОЛЖНЫ быть разделены одной открывающей фигурной скобкой { и одной закрывающей фигурной скобкой }. НЕ ДОЛЖНО быть пробела между разделителями и именем плейсхолдера.

    • Имена плейсхолдеров ДОЛЖНЫ состоять только из символов A-Z, a-z, 0-9, символа подчеркивания _ и точки .. Использование других символов зарезервировано для будущих модификаций спецификации плейсхолдеров.

    • Разработчики МОГУТ использовать плейсхолдеры для реализации различных стратегий экранирования и отображения логов. Пользователям НЕ СЛЕДУЕТ предварительно экранировать значения плейсхолдеров, так как они могут не знать в каком контексте будут отображаться данные.

Ниже приведен пример реализации плейсхолдера. Только для справки:

<?php

  /**
   * Преобразование контекстных значений в значения плейсхолдера
   */
  function interpolate($message, array $context = array())
  {
      // Создаем замещающий массив с фигурными скобками вокруг контекстных ключей
      $replace = array();
      foreach ($context as $key => $val) {
          // проверяем, что значение может быть приведено к строке
          if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
              $replace['{' . $key . '}'] = $val;
          }
      }

      // заменяем и возвращаем результат
      return strtr($message, $replace);
  }

  // сообщение с плейсхолдером
  $message = "User {username} created";

  // массив для замены
  $context = array('username' => 'bolivar');

  // выводит "User bolivar created"
  echo interpolate($message, $context);

1.3 Контекст

  • Каждый метод принимает массив в качестве данных контекста. Это сделано для того, чтобы можно было бы передавать данные, которые невозможно представить в виде строки. Массив может содержать что угодно. Разработчики ДОЛЖНЫ гарантировать, что они обрабатывают данные контекста не строго. Заданное значение в контексте НЕ ДОЛЖНО бросать исключение и не должно вызывает никаких ошибок, предупреждений или уведомлений.

  • Если объект Exception передается в данных контекста, он ДОЛЖЕН быть помещен в ключ exception. Регистрация исключений является распространенным шаблоном. Это позволяет разработчикам получать трассировку стека из исключения, если это возможно. Разработчики ДОЛЖНЫ по-прежнему проверять, что значение ключа exception на самом деле является исключением (Exception), прежде чем использовать его как таковой, поскольку он МОЖЕТ содержать что-либо еще.

1.4 Вспомогательные классы и интерфейсы

  • Класс Psr\Log\AbstractLogger позволяет вам реализовать LoggerInterface очень легко, расширив его и реализовав общий метод log. Остальные восемь методов пересылают ему сообщение и контекст.

  • Аналогично, использование Psr\Log\LoggerTrait требует от вас только реализовать общий метод log. Обратите внимание, что, поскольку трейты не могут реализовывать интерфейсы, в этом случае вам все равно придется реализовать интерфейс LoggerInterface.

  • Класс Psr\Log\NullLogger предоставляется вместе с интерфейсом. Он МОЖЕТ использоваться разработчиками для реализации резервной «черной дыры», если им не предоставлен регистратор. Однако условная регистрация может быть лучшим подходом, если создание контекстных данных неприменимо.

  • Класс Psr\Log\LoggerAwareInterface содержит только метод setLogger(LoggerInterface $logger) и может использоваться фреймворками для автоматического связывания произвольных экземпляров с регистратором.

  • Трейт Psr\Log\LoggerAwareTrait можно использовать для простой реализации аналогичного интерфейса в любом классе. Такая реализация дает вам доступ к $this->logger.

  • Класс Psr\Log\LogLevel содержит константы для восьми уровней логирования.

2. Пакет

Описанные интерфейсы и классы, а также соответствующие классы исключений и набор тестов для проверки вашей реализации предоставляются как часть psr/log пакета.

3. Psr\Log\LoggerInterface

<?php

namespace Psr\Log;

/**
 * Описывает общий интерфейс логирования
 *
 * Сообщение ДОЛЖНО быть строкой или объектом, реализующим __toString().
 *
 * Сообщение МОЖЕТ содержать плейсхолдеры в форме: {foo}, где foo
  * будут заменены данными контекста из ключа "foo".
 *
 * Контекстный массив может содержать произвольные данные, единственное предположение, 
 * которое могут сделать разработчики, заключается в том, что если экземпляр Exception дается 
 * для создания трассировки стека, он ДОЛЖЕН находиться в ключе «exception».
 *
 */
interface LoggerInterface
{
    /**
     * Чрезвычайная ситуация. Система непригодна для использования.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function emergency($message, array $context = array());

    /**
     * Действия должны быть приняты немедленно.
     *
     * Пример: весь веб-сайт недоступен, база данных недоступна и т. д. Это должно вызвать SMS-уведомления и разбудить вас.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function alert($message, array $context = array());

    /**
     * Критические условия.
     *
     * Пример: компонент приложения недоступен, неожиданное исключение.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function critical($message, array $context = array());

    /**
     * Ошибки времени выполнения, которые не требуют немедленных действий, но обычно должны регистрироваться и отслеживаться.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function error($message, array $context = array());

    /**
     * Исключительные случаи, не являющиеся ошибками.
     *
     * Пример: использование устаревших API, неправильное использование API, нежелательные вещи, которые не обязательно являются неправильными.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function warning($message, array $context = array());

    /**
     * Обычные, но важные события.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function notice($message, array $context = array());

    /**
     * Интересные события.
     *
     * Пример: вход пользователя в систему, запись лога SQL.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function info($message, array $context = array());

    /**
     * Подробная отладочная информация.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function debug($message, array $context = array());

    /**
     * Логирование с заданным уровнем.
     *
     * @param mixed $level
     * @param string $message
     * @param array $context
     * @return void
     */
    public function log($level, $message, array $context = array());
}

4. Psr\Log\LoggerAwareInterface

<?php

namespace Psr\Log;

/**
 * Описывает экземпляр с поддержкой ведения лога.
 */
interface LoggerAwareInterface
{
    /**
     * Устанавливает экземпляр лога для объекта.
     *
     * @param LoggerInterface $logger
     * @return void
     */
    public function setLogger(LoggerInterface $logger);
}

5. Psr\Log\LogLevel

<?php

namespace Psr\Log;

/**
 * Описывает уровни логирования
 */
class LogLevel
{
    const EMERGENCY = 'emergency';
    const ALERT     = 'alert';
    const CRITICAL  = 'critical';
    const ERROR     = 'error';
    const WARNING   = 'warning';
    const NOTICE    = 'notice';
    const INFO      = 'info';
    const DEBUG     = 'debug';
}