Hibanapló Zend Framework alkalmazásokhoz

A korábbi cikkben megnéztük, hogyan lehet Zend Framework alkalmazásunkat rávenni hozzáférési napló írására. Ezúttal vegyük rá a rendszert a hibák naplózására!
A sorozatban megjelent
- Hozzáférési napló írása Zend Framework alkalmazásokhoz
- Hibanapló Zend Framework alkalmazásokhoz
Mit is kellene a naplóba írni? Rögzítsük
- az aktuális felhasználónk adatait,
- milyen IP-ről jött a kérés, ami hibához vezetett,
- az URL-t ahol a hiba keletkezett
- – természetesen kell időbélyeg is –,
- valamint maga a hiba.
- És a biztonság kedvéért a kérés lehető legtöbb adata.
A naplót most is SQLite adatbázisba írjuk. A hozzáférési naplóval ellentétben ezúttal csak egy naplót fogunk írni. A Zend Framework szállít már egy hibakezelő bővítményt (Zend_Controller_Plugin_ErrorHandler
). Mivel ennek a működését szeretnénk megtartani, származtatjuk azt, majd ezt a leszármazott osztályt fogjuk használni az alkalmazásunkban.
- Zend_Loader::loadClass('Zend_Controller_Plugin_ErrorHandler');
- /**
- * ErrorHandler plugin
- *
- * @author 4image#dev, Vince Tikász
- */
- class FI_Controller_Plugin_ErrorHandler extends Zend_Controller_Plugin_ErrorHandler {
- /**
- * @var Zend_Db_Adapter_Pdo_Sqlite
- */
- protected $db;
- /**
- * @var String
- */
- protected $dbName = 'error-log.sqlite3.db';
- /**
- * @var String
- */
- protected $tblName = 'error_log';
- protected function initLogDb() {
- $logPath = APPLICATION_PATH . '/log/' . $this->dbName;
- $this->db = new Zend_Db_Adapter_Pdo_Sqlite(array('dbname' => $logPath));
- $desc = $this->db->describeTable($this->tblName);
- if (count($desc) == 0) {
- // Ha nem létezik, létrehozzok a táblát
- $this->db->query('CREATE TABLE IF NOT EXISTS ' . $this->tblName . ' (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- session_id char(32),
- user_id INTEGER default NULL,
- user_datas text,
- ip_address varchar(45) default NULL,
- request_uri text,
- path text,
- url text,
- hostname varchar(100) default NULL,
- timestamp timestamp NOT NULL default CURRENT_TIMESTAMP,
- method varchar(45) default NULL,
- request blob,
- exceptions blob
- )');
- }
- }
- /**
- * Filter sensitive params
- * @param Array $array
- * @retunr Array
- */
- protected function filterParams($array = array()) {
- $filtered = array();
- foreach((array) $array as $k => $v) {
- if (is_array($v)) {
- $filtered[$k] = $this->filterParams($v);
- } else {
- switch($k) {
- // soroljuk fel ide a jelszómezőknél használt neveket
- case 'password':
- case 'passwrd':
- $filtered[$k] = '[keep-in-secret]';
- break;
- // itt pedig a jelszó megerősítő mezők neveit
- case 'password-confirm':
- $same = $v === $array['password'];
- $filtered[$k] = '[keep-in-secret(' . ($same ? '' : 'not-') . 'match)]';
- break;
- }
- }
- }
- return $filtered;
- }
- protected function writeLog() {
- $this->intiLogDb();
- $user_id = Zend_Auth::getInstance()->hasIdentity() ? Zend_Auth::getInstance()->getIdentity()->id : 0;
- $this->db->insert($this->tblName, array(
- 'session_id' => Zend_Session::getId(),
- 'user_id' => $user_id,
- 'user_datas' => serialize(Zend_Auth::getInstance()->getIdentity()),
- 'ip_address' => $this->_request->getServer('REMOTE_ADDR'),
- 'request_uri' => $this->_request->getServer('REQUEST_URI'),
- 'path' =>
- $this->_request->getModuleName() . '@' .
- $this->_request->getControllerName() . '/' .
- $this->_request->getActionName(),
- 'url' =>
- $this->_request->getScheme() . '://' .
- $this->_request->getHttpHost() .
- $this->_request->getRequestUri(),
- 'hostname' => $this->_request->getServer('HTTP_HOST'),
- 'timestamp' => date('Y-m-d H:i:s'),
- 'method' => $this->_request->getMethod(),
- 'request' => serialize( array(
- 'SERVER' => $this->_request->getServer(),
- 'POST' => $this->filterParams($this->_request->getPost()),
- 'COOKIE' => $this->filterParams($this->_request->getCookie()),
- 'params' => $this->filterParams($this->_request->getParams()),
- 'request' => $this->filterParams($this->getRequest())
- )),
- 'exceptions' => serialize($this->_response->getException()),
- ));
- $this->db->closeConnection();
- }
- /**
- * Post dispatch
- * @param Zend_Controller_Request_Abstract $request
- */
- public function postDispatch(Zend_Controller_Request_Abstract $request) {
- parent::postDispatch();
- // csak abban az esetben kell naplót írni, ha hiba van
- if ( $this->getResponse()->isException() ) {
- $this->writeLog();
- }
- }
A plugin betöltése most is a szokásos módon, a Bootstrap
osztályban történik.
- class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {
- protected function _initPlugins() {
- Zend_Controller_Front::getInstance()
- ->registerPlugin(new FI_Controller_Plugin_ErrorHandler());
- }
- }
A hozzáférési napló írását a lehető legkorábban futtatjuk, a hibanaplóét pedig a lehető legkésőbb, ezért ezúttal a postDispatch()
pontra kerül. Első lépésben hagyjuk, hogy az ősosztály kezelje a hibát. Akit érdekel, megvizsgálhatja, pontosan hogyan is szedi össze, és adja át a hiba adatait az alkalmazásunk ErrorController
-jének. A beépített mechanizmus futását követően létrehozzuk a naplóként használt SQLite adatbázist és benne a szükséges táblát. Záró lépésként pedig elkészítjük a naplóbejegyzést.
Nagyjából ide lehet érdemes beépíteni egy e-mail küldést is, amiben értesíthetjük magunkat az új hibákról. Az ErrorController
-hez tartozó sablonban az aktuális felhasználó jogaittól függően akár meg is jeleníthetjük a hiba részleteit.
Az előző cikk hozzászólásai közt Tyrael felhívta a figyelmet az érzékeny adatok védelmének fontosságára. A legegyszerűbb megoldás, hogy a kérés azon részeit, amelyek érzékenynek minősülnek, egyszerűen helyettesítjük valamilyen szöveggel. Azért nem töröljük a tömbből, mert lehetséges, hogy a paraméter megléte vagy hiánya ugyanolyan fontos tényező a hiba kiváltásában. Jelen példában csak a jelszó, valamint a jelszó megerősítő mezőt szűröm meg, de a filterParams()
metódus tovább bővíthető saját igényeknek megfelelően. Nyílván lehetne fokozni a védelmet, illetve a szűrendő mezők megadása történhetne például konfigurációs állománnyal, de itt a cél az alapvető logika prezentálása.
A következő cikkben megmutatom, hogyan tudunk egy olyan felületet készíteni, amin keresztül a naplókat lehet vizsgálni.
■
tiku I tikaszvince
Tikász Vince végzettsége szerint könyvtáros, a szemantikus éshozzáférhető web elkötelezett híve. 2005 óta hivatásszerűen
foglalkozik webes fejlesztéssel. Jelenleg a Full Market Zrt frontend fejlesztője, valamint a 4image Kft „bedolgozó” fejlesztője. Korábban a WebShop-Experts Kft. vezető fejlesztője.
Na most vagy az van, hogy
mea culpa
A kódban szereplő postDispatch függvény helyesen a következően néz ki
Javítottam a cikkben.
Kérdés: A lazy init miatt nem