Logowanie błędów jest kluczowym elementem minitoringu aplikacji. Przekonał się o tym chociażby każdy programista, który pracuje w firmie oferującej dodatkowo dla swoich produktów terminowy support.
Dzięki temu, że błędy są logowane jesteśmy w stanie szybko i czasami transparentnie wyeliminować bugi w aplikacji, tak że klient nawet się nie dowie o ich istnieniu:)
Jeśli Zend Framework jest podstawą twojego projektu oferuję Tobie bardzo wygodny i przejrzysty sposób na logowanie błędów aplikacji.
Potrzebna będzie jedna klasa, ponieważ aż się prosi aby system logowania błędów był ładowany jako resource, przez Zend_Application. Zarządzanie zasobów zaprezentowane w Zend Framework jest moim zdaniem rewelacyjne, ponieważ bardzo łatwo możemy wydzielić zasoby, dzięki czemu mamy wprowadzoną dodatkową logikę i ład w naszej aplikacji, ponadto dowolny zasób można łatwo i sprawnie włączyć/wyłączyć.
Przykładowa klasa mogłaby wygłądać następująco:
class Zextend_Application_Resource_Log extends Zend_Application_Resource_ResourceAbstract
{
public function init()
{
/**
* Pobranie głównej konfiguracji.
*/
$config = $this->getBootstrap()->getContainer()->config;
/**
* Przygotowanie formatu zapisanej informacji.
*
* @example Postać zmiennej $config->format:
* "%timestamp% %priorityName%: %message%"
*/
$format = new Zend_Log_Formatter_Simple($config->format . PHP_EOL);
/**
* Sprawdzanie, ktory sposób logowania błędów został włączony.
*
* @example Postać zmiennej $config->log->stream:
* "../temporary/log/error.log"
*/
$logger = new Zend_Log();
if($config->log->file) {
$logger->addWriter(
$this->_getStreamWriter($format, $config->log->stream));
}
if($config->log->stdoutput) {
$logger->addWriter(
$this->_getStreamWriter($format, 'php://output'));
}
if($config->log->firebug) {
$logger->addWriter(
$this->_getFirebugWriter($format));
}
if($config->log->mail) {
$logger->addWriter($this->_getMailWriter($config));
}
/**
* Używamy tego logger wszędzie w aplikacji.
*/
Zend_Registry::set( 'log', $logger );
return $logger;
}
/**
* Zwraca obiekt zapisania informacja do Firebug.
*
* @param Zend_Log_Formatter_Simple $format
* @return Zend_Log_Writer_Firebug
*/
private function _getFirebugWriter(Zend_Log_Formatter_Simple $format)
{
$writer = new Zend_Log_Writer_Firebug();
$writer->setFormatter($format);
return $writer;
}
/**
* Zwraca obiekt zapisania informacji w postaci emaila.
*
* @param Zend_Config_Ini $config
* @return Zend_Log_Writer_Mail
*/
private function _getMailWriter(Zend_Config_Ini $config)
{
$mail = new Zend_Mail();
$mail->setDefaultTransport(new Zend_Mail_Transport_Smtp());
$mail->setFrom($config->email->from, $config->email->fromAlias);
$mail->addTo($config->app->admin->email);
$mail->addTo($config->app->webmaster->email);
$mail->setSubject($config->app->name . ' - nastapil nieoczekiwany blad!');
$writer = new Zend_Log_Writer_Mail($mail);
$writer->addFilter(Zend_Log::WARN);
return $writer;
}
/**
* Zwraca obiekt zapisania informacji do PHP stream.
*
* @param Zend_Log_Formatter_Simple $format
* @param string $stream
* @return Zend_Log_Writer_Stream
*/
private function _getStreamWriter(Zend_Log_Formatter_Simple $format, $stream = '')
{
$writer = new Zend_Log_Writer_Stream($stream);
$writer->setFormatter($format);
return $writer;
}
}
Jak widać oferuje ona możliwość logowania błędów na kilka sposobów: streaming do pliku, badź na ekran, JSON’em prosto do firebug’a (warunkiem odczytu jest zainstalowanie wtyczki FirePHP w Firefox) lub powiadomienie na email. W przykładzie część konfiguracji znajduje się w pliku /application/configs/application.ini, ktory ładowany jest poprzez Zend_Config_Ini do rejestru. Odczytany zostaje między innymi email do webmastera oraz do admina, czyli osób odpowiedzialnych za poprawne działanie aplikacji, ponadto zdefiniowane są takie parametry jak scieżka do pliku z blędami (error.log) czy też format komunikatu z błędem.
Oczywiście trzeba tę funkcjonalność dołączyć do projektu jako nowy zasób. Nic prostszego – w pliku konfiguracyjnym /application/configs/application.ini dodajemy kolejny resource do listy:
resources.types = "array()"
resources.view = "array()"
resources.navigation = "array()"
resources.log = "array()"
Dodatkowo musimy zdefniować ustawienia, które załączają odpowiednei przekazanie informacji o błędzie. Czyli znów w /application/configs/application.ini dopisujemy:
log.file = true
log.firebug = true
log.mail = true
log.stdoutput = true
Rzecz jasna korzystamy z odpowiedniego typu logowania w zależności od potrzeb. Bardzo wygodnie jest w trakcie tworzenia kodu, czy też jego testowania, otrzymywać komunikaty do konsoli FF, wykorzystując firePHP, dodatkowo wygodne jest odczytywanie błędów z pliku error.log – otwieramy konsole i dajemy polecenie:
i mamy nasz system monitorujacy, w którym dokładnie zapisane jest gdzie wystąpił nieoczekiwany błąd (kupujemy telewizor LCD, który pokazuje te komunikaty przez cały czas
oczywiście żartuje
). Jeśli aplikacja jest już w wersji produkcyjnej to bardzo fajne jest rozwiązanie z przychodzącym emailem – można wtedy szybko interweniować.
Pamiętajmy jednak, że jeśli aplikacja jest już na produkcji, należy zablokować logowanie błędów do FireBug’a!!
Logowane błędy są tylko dla nas – developerów, adminów, …, i mają stanowić wskazówkę do szybkiego ich rozwiązania. Jeśli te informacje trafią do niepowołanych rąk to niestety możemy wskazać najsłabsze ogniwo naszej aplikacji, a tym samym stworzyć okazję do włamania się do niej np. hackerowi.
Kolejną sprawą jest miejsce, w którym nasz logger, trzymany w rejestrze, może dokonać zapisu błędu. Jednym z lepszym miejsc jest kontroler ErrorController.php i w nim metoda errorAction, na którą domyślnie przekierowany jest błąd. Oczywiście gdzie będzie odbywało się logowanie błędów utożsamiony z tą linijką kodu:
if (Zend_Registry::isRegistered('log')) {
$logger = Zend_Registry::get('log');
if (!is_null($this->_error)) {
$logger->log(
$this->_error->exception->getMessage() . "\n\n" .
$this->_error->exception->getTraceAsString(),
Zend_Log::ERR
);
}
}
jest uzależnione od potrzeb aplikacji i zaprojektowania jej przez programistę.
Taki sposób logowania błędów można implementować w każdym projekcie – nie tylko opartym o Zend Framework. Po drobnych modyfikacjach, korzystając tylko z kilku komponentów (w wersji ZF 1.10.6), Log(20 plików), Wildfire (8 plików), Json (14 plików), Config (8 plików) + Exception.php, można stworzyć taki sam system logowania błędów.