Logowanie w standardzie PSR-3

Prawie wszędzie spotykamy się z problemem logowania zdarzeń. Przed PSR-3 musieliśmy sami wymyślać sposób działania loggera. Dziś, dzięki PSR-3 mamy jeden standard logowania. Możemy pisać aplikację niezależną od wybranego loggera lub zmieniać go bez problemu.

Standard PSR-3 oferuje nam uniwersalny interfejs logowania wszystkiego w naszej aplikacji. Koniec z pisaniem własnego loggera. Nie musimy już zastanawiać się jakie przyjąć poziomy komunikatów. Mamy jeden wspólny sposób działania wszystkich narzędzi logujących.

Po co potrzebujemy standardu?

Logowanie pojawia się w każdej aplikacji. Zapisujemy komunikaty o błędach, informacje debugujące i wszystko, co tylko potrzeujemy.

Dotychczas każdy z nas tworzył własną klasę logującą. Czasem korzystaliśmy z gotowców typu monolog czy analog. Efekt – logowanie w każdym systemie wyglądało inaczej. Czasem funckcja nazywała się log(), czasem SaveLog(), error(), warning() czy jakkolwiek inaczej. System miał jeden lub kilka poziomów logowania. Ogólnie panował bałagan.

Wstrzykiwanie zależności było koszmarem. Mieliśmy klasę logującą w każdym systemie ale nie można było ich wymienić. Jeśli wstrzyknęliśmy zły logger, cały system nie działał.

Jak wygląda standard PSR-3

Standard definiuje 8 poziomów logowania. Wystarczy dla każdego systemu. Poszczególne poziomy to:

  • Emergency – system nie działa
  • Alert – należy natychmiast podjąć naprawę
  • Critical – krytyczny błąd
  • Error – zwykły błąd
  • Warning – ostrzeżenie o możliwym błędzie
  • Notice – nie błąd, ale znaczące zdarzenie w systemie
  • Informational – informacja o działaniu systemu
  • Debug – informacja do debugowania

W ramach tych poziomów możemy logować praktycznie wszystko. Od informacji o poprawnym działaniu (Informational lub Notice) po rytyczne błędy lub całkowitą awarię. Dzięki spójnej liście błędów możemy je łatwo sortować. Na tej podstawie programiści mogą np. przydzielać priorytety swoim zadaniom.

W ramach standardu mamy interfejs, który definiuje działanie loggera:

interface LoggerInterface
{
    public function emergency($message, array $context = array());
    public function alert($message, array $context = array());
    public function critical($message, array $context = array());
    public function error($message, array $context = array());
    public function warning($message, array $context = array());
    public function notice($message, array $context = array());
    public function info($message, array $context = array());
    public function debug($message, array $context = array());

    public function log($level, $message, array $context = array());
}

Jak widać mamy 8 metod, które odpowiadają każdemu z poziomów. Każda metoda przyjmuje dwa parametry:

Wiadomość o błędzie podajemy w message. Może to być string lub obiekt, który daje się przekształcić na string. W treści możemy umieszczać placeholdery w nawiasach klamrowych. Możemy np. napisać „Użytkownik {user} wylogował się”.

Context to dodatkowa tablica z informacjami. Jeśli korzystamy z placeholderów to klucze tej tablicy powinny dawać nam odczytać ich wartość. Możemy tu wpisać dowolne dane i obiekty. Obiekt Exception również. Według standardu powinien on być pod kluczem „exception”.

Ostatnia, dziewiąta metoda log() działa tak samo jak poprzednie tylko jako pierwszy argument przyjmuje poziom logowania. W praktyce to ta metoda przeważnie zapisuje logi. Pozostałe przeważnie tylko ją wywołują.

Jak skorzystać z PSR-3

Najprościej wykorzystać composera aby pobrać klasy zdefiniowane w PSR-3:

{
   "require": {
      "psr/log": "dev-master"
   }
}

Możemy teraz napisać własną klasę logującą zdarzenia. Jest łatwo – nie musimy tworzyć klasy obsługującej cały interfejs. Wystarczy, że odziedziczymy po specjalnej klasie AbstractLogger. Klasa ta przekierowuje wszystkie metody na metodę abstrakcyjną log(). Wystarczy ją napisać. Cały kod:

require_once('vendor/autoload.php');

// nasza klasa logująca wrzuca samą wiadomość do pliku
class myLogger extends Psr\Log\AbstractLogger {
   
   // jedyna metoda, którą musimy obsłużyć
   public function log($level, $message, array $context = Array())
   {
      file_put_contents("my.log",$message,FILE_APPEND);
   }
}

Teraz możemy w dowolnym kodzie utworzyć obiekt logujący i możemy go wywoływać:

// tworzymy obiekt
$logger = new myLogger();
   
// ... i logujemy
$logger->warning("Uwaga, jedzie łamaga");

Taki obiekt możemy wrzucić np. do wstrzykiwania zależności w dowolnym frameworku, który korzysta z PSR-3.

Krytyka standardu

Jak każdy standard i ten spotkał się z krytyką. Na uwagę zasługuje jeden argument.

Poziomy logowania są nazwane stałymi w klasie Psr\Log\LogLevel. Ich nazwy bazują na małej części RFC 5424. O ile nazwy stałych są jasne i przejrzyste to nie dają możliwości posortowania poziomów. Nie możemy więc łatwo logować zdarzeń o poziomie wyższym niż X. Inaczej jest w specyfikacji RFC, gdzie każdemu poziomowi jest przypisana wartość nueryczna.

Żeby móc logować poziomy od-do lub ważniejsze niż wybrany poziom musielibyśmy sami stworzyć tablicę definiującą numeryczne wartości dla stałych.

Co dalej

Standard jest opisany na stronie PHP-FIG. Na githubie mamy dostępne repozytorióm, z którego skorzystaliśmy w composerze.

Warto korzystać z jednego standardu logowania. Pomimo wspomnianej wyżej wady jeden sposób logowania ułatwia nam powtórne użycie kodu. Możemy też skorzystać z gotowych bibliotek. Packagist na hasło PSR-3 daje nam 30 stron wyników. Na pewno w gotowych rozwiązaniach znajdziemy coś dla siebie.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *