2009
W pierwszej części Log class – klasa loggera zdarzeń 1 rozpoczęliśmy prace nad klasą logowania zdarzeń. Utworzyliśmy interfejs klasy, który będzie implementowany. W tej części zajmiemy się implementacją tego interfejsu.
Z racji użycia interfejsu, klasy naszego loggera muszą zawierać 3 publiczne, statyczne metody: error()
, warn()
, info()
. Najłatwiejszym, a zarazem dość przenośnym rodzajem klasy logowania do napisania jest logowanie do pliku. Zatem stwórzmy taką klasę:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php class logFile implements log_log { public static function error($err){ echo 'logFile - error |'.$err; } public static function warn($err){ echo 'logFile - warn |'.$err; } public static function info($err){ echo 'logFile - info |'.$err; } } ?> |
Taki zarys musi mieć (ze względu na implementację interfejsu) nasza klasa logFile
– logująca do pliku. Jednak to co mamy do tej pory nie jest zbyt użyteczne 😉 Zatem zajmijmy się wypełnieniem metod:
Wszystkie z trzech metod mają za zadanie zapisać informację do logu, z tą różnicą, że są to informacją z inną flagą (błąd, ostrzeżenie, informacja). Zatem możemy je połączyć w jedną write()
, która będzie zapisywać log do pliku.
<?php /** * Zapis pojedynczego komunikatu * @param string $logMsg Pojedynczy komunikat */ private function write($logMsg){ file_put_contents(self::$file_name,$logMsg,FILE_APPEND | LOCK_EX); } ?> |
Zapis do logu jak widzimy możemy przeprowadzić za pomocą file_put_contents 2 , z ustawionymi flagami FILE_APPEND | LOCK_EX
– dopisywaniem do pliku, jeśli istnieje oraz blokowaniem dostępu do pliku na czas zapisu. Dzięki temu logów w naszym pliku będzie przybywało, nie będą zastępowane, nadpisywane.
Po dodaniu metody write trzy wcześniej utworzone powinny ją wywoływać. Zatem w ciele metod dodajemy wywołanie metody write:
<?php /** * Dodanie komunikatu informacyjnego do logów * @see log_log#info($msg) */ public static function info($msg){ self::write(self::logPrefix('INFO').$msg); } ?> |
Pojawiła się tu nowa rzecz: self::logPrefix('INFO');
. Otóż jest to wywołanie kolejnej metody, która ma za zadania do linii logu dodać informację o rodzaju zdarzenia, które jest logowane. Jak ta metoda wygląda w przypadku logowania do pliku? Następująco:
<?php /** * Dodanie przedrostka do linijki logu * @param string $flag Flaga określająca rodzaj zdarzenia * @return string */ private function logPrefix($flag){ return "\n".date('Y-m-d H:i:s')."\t|".$flag."\t|\t"; } ?> |
Przyjęty format pliku logu wygląda następująco:
TIMESTAMP |TYPE | MESSAGE |
Przykładowo:
2009-09-02 10:50:07 |INFO | Pomyślnie zakończono akcję 123 2009-09-02 10:50:07 |WARN | Brak aktywnych zadań 2009-09-02 10:50:33 |ERROR | Połączenie zerwane |
Wykańczając naszą klasę loggera dogramy kilka szczegółów: wybór pliku logu, ustawienie domyślnego logu w przypadku jego braku. Możemy to zrobić na kilka sposobów, jeden z nich:
<?php /** * Plik logu * @var string */ private static $file_name; /** * Ustawienie nazwy pliku logu z danego dnia */ public function setFile($logFile){ self::$file_name = $logFile; } ?> |
Zbierając to w całość otrzymujemy taką klasę logFile
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | <?php /** * Klasa logowania do pliku, implementująca interfejs log_log * @author Vokiel * @package log */ class logFile implements log_log { /** * Plik logu * @var string */ private static $file_name; /** * Ustawienie domyślnego pliku logu * @return unknown_type */ public function __construct($logFile=''){ $file_name = 'logs/'.date('Y-m').'_default.log'; if ($logFile){ $file_name = $logFile; } self::setFile($file_name); } /** * Ustawienie nazwy pliku logu z danego dnia */ public function setFile($logFile){ self::$file_name = $logFile; } /** * Dodanie błędu do logów * @see logInterface#error($err) */ public static function error($msg){ self::write(self::logPrefix('ERROR').$msg); } /** * Dodanie ostrzeżenia do logów * @see logInterface#warning() */ public static function warn($msg){ self::write(self::logPrefix('WARN').$msg); } /** * Dodanie komunikatu informacyjnego do logów * @see log_log#info($msg) */ public static function info($msg){ self::write(self::logPrefix('INFO').$msg); } /** * Zapis pojedynczego komunikatu * @param string $logMsg Pojedynczy komunikat */ private function write($logMsg){ file_put_contents(self::$file_name,$logMsg,FILE_APPEND | LOCK_EX); } /** * Dodanie przedrostka do linijki logu * @param string $flag Flaga określająca rodzaj zdarzenia * @return string */ private function logPrefix($flag){ return "\n".date('Y-m-d H:i:s')."\t|".$flag."\t|\t"; } }// end of logFile ?> |
Użycie jest banalnie proste, tak jak zakładaliśmy w pierwszym wpisie: Log class – klasa loggera zdarzeń 1:
<?php logFile::info('Wszystko ok'); logFile::error('Wszystko jak krew w piach!'); ?> |
Teraz, w przypadku chęci zmiany sposobu logowania, na np.: bazę danych, wystarczy napisać klasę logDB
implementującą interfejs log_log
. Oczywiście dobrym rozwiązaniem byłoby dodanie metody write()
, tak aby usprawnić proces zapisu do logów, jednak nie jest to konieczne, ani wymagane.
Co jeśli zechcielibyśmy płynnie zmieniać sposób logowania zdarzeń? Np logując część zdarzeń do pliku a część do bazy? Sprawa banalnie prosta:
<?php logFile::info('To zapisujemy do pliku'); logDB::error('A tu zapiszemy do bazy'); ?> |
W trakcie używania takich klas może pojawić się sytuacja, w której okaże się, że zechcemy zmienić sposób logowania dla całego pliku, części aplikacji etc. (sam miałem taką sytuację – początkowo logowanie do pliku było wystarczające, później okazało się, że jednak przejdę na bazę). Zmiana z logFile
na logDB
w całym projekcie nie jest mądrym rozwiązaniem. Ale i z tym sobie poradzimy – w trzeciej, ostatniej, części opisującej loggera zdarzeń.
Źródła
- http://blog.vokiel.com/log-class-klasa-loggera-zdarzen [↩] [↩]
- http://pl.php.net/manual/pl/function.file-put-contents.php [↩]
[…] klasy Log, ustaliliśmy strukturę, zasady funkcjonowania klasy oraz napisaliśmy interfejs. W drugiej części rozwinęliśmy nasz projekt o klasę logFile implementującą wcześniej utworzony interfejs, […]