ugrás a tartalomhoz

Hozzáférési napló írása Zend Framework alkalmazásokhoz

tiku I tikaszvince · 2010. Nov. 8. (H), 08.20
Hozzáférési napló írása Zend Framework alkalmazásokhoz

Próbálok nagy szavakat találni, miért érdemes egy-egy alkalmazásba naplózást építeni, de mindig a hiba felderítéshez lyukadok ki. Biztosan az olvasó is találkozott olyan felhasználóval, aki rendszeresen futott bele egy hibába, amit az elmondása alapján nem lehetett reprodukálni. Vagy olyanba, amibe csak egy-két felhasználó szaladt bele, olyan úton, amire fejlesztőként egyáltalán nem gondoltunk. Ilyenkor hatalmas segítség lehet, hogy lépésről lépésre végig tudjuk követni felhasználót.

A sorozatban megjelent

Jöhet a felvetés, hogy ott vannak nekünk a szerverlogok, használjuk azt. Igaz, vannak logok. De mi van, ha a tárhelyszolgáltató nem őrizgeti, csak korlátozott ideig? Vagy prompt megoldást kell találni egy problémára, és a szolgáltató nem elérhető, nem ér rá, nem tud vagy – egyszerűen – nem akar segíteni? Egyik opció lehet egy másik, készségesebb tárhelyszolgáltató, másik a naplózás beépítése az alkalmazásunkba.

Mindennapi munkámban Zend Framework alapokon építek webalkalmazásokat, ezért e rendszeren fogom bemutatni, hogyan naplózom a kéréseket. Egy következő cikk témája lesz a felület elkészítése, ahol végig lehet majd nézni a naplókat.

A napló írását egy egyszerű bővítmény végzi, SQLite adatbázisba, heti bontásban. Minden bejegyzés tartalmaz minden adatot, amit a felhasználóról ismer a rendszer: az aktuális munkamenet azonosítót, az IP-címet, ahonnan a kérés érkezett, a tartománynevet, a lekért URL-t, a kérés típusát, az időpontot és egy kicsit kibővített Request objektumot.

/**
 * AccessLog plugin
 *
 * @author 4image#dev, Tikász Vince
 */
class FI_Controller_Plugin_AccessLog extends Zend_Controller_Plugin_Abstract
{
    /**
     * @var Zend_Db_Adapter_Abstract
     */
    protected $db;
    protected $tableName = 'access_log';
    
    protected function initDb()
    {
        $dbName   = 'access-log-' . date('Y') . '-w' . date('W') . '.sqlite3.db';
        $logPath  = APPLICATION_PATH . '/log/' . $dbName;
        $dbExists = is_file($logPath);
        
        $this->db = new Zend_Db_Adapter_Pdo_Sqlite(array('dbname' => $logPath));
        
        if (!$dbExists) {
            // Még nem létezik az eheti adatbázis, tehát létre kell hoznunk a napló táblát.
            
            $this->db->query(
                'CREATE TABLE IF NOT EXISTS ' . $this->tableName . ' (
                   id          INTEGER PRIMARY KEY AUTOINCREMENT,
                   session_id  CHAR(32),
                   user_id     INTEGER DEFAULT NULL,
                   user_data   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
                 )');
        }
    }
    
    public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
    {
        $this->initDb();
        $userId = 0;
        
        if (Zend_Auth::getInstance()->hasIdentity()) {
            $userId = Zend_Auth::getInstance()->getIdentity()->id;
        }
        
        $this->db->insert($this->tableName, array(
            'session_id'  => Zend_Session::getId(),
            'user_id'     => $userId,
            'user_data'   => serialize(Zend_Auth::getInstance()->getIdentity()),
            'ip_address'  => $request->getServer('REMOTE_ADDR'),
            'request_uri' => $request->getServer('REQUEST_URI'),
            'path'        => $request->getModuleName() . '@' . $request->getControllerName() . '/' . $request->getActionName(),
            'url'         => $request->getScheme() . '://' . $request->getHttpHost() . $request->getRequestUri(),
            'hostname'    => $request->getServer('HTTP_HOST'),
            'timestamp'   => date('Y-m-d H:i:s'),
            'method'      => $request->getMethod(),
            
            'request' => serialize(array(
                'SERVER'  => $request->getServer(),
                'POST'    => $request->getPost(),
                'COOKIE'  => $request->getCookie(),
                'params'  => $request->getParms(),
                'request' => $this->getRequest(),
            )),
        ));
        
        // Lezárjuk az adatbázis kapcsolatot, megtörténik a tényleges írás
        $this->db->closeConnection();
    }
}

A plugin betöltése 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_AccessLog(), -10);
    }
}

A napló írása a dispatchLoopStartup() ponton történik. Ennél korábban csak a routeStartup() és a routeShutdown() fut le. Ezeken a pontokon még nem lehet pontosan tudni, hogy melyik modul, vezérlő és művelet kerül majd meghívásra, ezért sanszos, hogy nem a tényleges esemény kerül a naplóba. Ezen pontot követően pedig már a preDispatch() fut le, ahol a jogosultságok ellenőrzése és egyéb dolgokkal összefüggő átirányítások is történhetnek.

Az adatbázis inicializálásakor az első lépés a napló nevének meghatározása. Mivel heti napló készítés a cél, ezért év-hét formátumú SQLite adatbázis állományaink lesznek. Ha nem létezett korábban az adatbázis, akkor a rendszer azt automatikusan létrehozza majd. Viszont a tábla létrehozásáról nekünk kell gondoskodni. Az írás után pedig ne felejtsük el lezárni az adatbázis kapcsolatot.

A napló bejegyzésbe nem csak az egész Request objektumot mentjük le, de ki is emelünk belőle néhány részt, így pl a SERVER, POST, COOKIE tartalmát, és az útválasztás után kapott paramétereket. A külön oszlopba való kiemelés a majdani keresést könnyíti meg: így egyszerűbb lesz megírni ez egy bizonyos felhasználó követését segítő lekérdezéseket.

Már a cikk írása közben jött néhány új ötlet, hogyan is lehetne további funkciókat beépíteni: az új adatbázis állomány létrehozásakor az előző naplót archiválni: tömöríteni, másik szerverre feltölteni, e-mail-ben elküldeni egy vagy több címre, esetleg ezek közül többet is elvégezni. Vagy akár ezeknek a konfigurálására külön adminisztrációs felületet írni.

 
tiku I tikaszvince arcképe
tiku I tikaszvince
Tikász Vince végzettsége szerint könyvtáros, a szemantikus és
hozzá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.
1

Nagyon jó!

janoszen · 2010. Nov. 9. (K), 02.08
Nagyon jó! Általában veszekednem kell a fejlesztőkkel azért, hogy normális loggolást írjanak, jó látni, hogy valaki elébe megy ezeknek a dolgoknak. A jó loggolás nem csak azért fontos, hogy a felhasználót követni tudd, hanem pl. ez alapján lehet kiszűrni a biztonsági szempontból gyanús próbálkozásokat is. De hogy mást mondjak, építettem olyan loggoló utilityt, ami minden különösebb terhelés nélkül óránként 450 000 logsort aggregált napi, óránkénti és perces bontásba, és ebből gyártott adatforgalmi grafikont.
2

statisztika

tiku I tikaszvince · 2010. Nov. 9. (K), 08.00
Az értelmes statisztikák, grafikonok elkészítése egy hatalmas feladat tud lenni. Ezek értelmezése meg egy olyan terület ami nagyon messzire bírja elvinni a fejlesztőt :) Megérne egy összefoglaló cikket ez a téma is. Mit tartalmaz egy ilyen statisztika, mit jelentenek az egyes fogalmak, hogyan kellene értelmezni ezeket, hogyan lehet javítani a rossz értékeken, stb
3

Tapasztaltam

janoszen · 2010. Nov. 9. (K), 12.38
Tapasztaltam, nemrég a DotRoll support chat közel másfél éves működéséből kellett operátorra lebontott statisztikát gyártanom. Csak az első feldolgozó query 10 percig futott, pedig dual quad Xeon volt SAS-os lemezekkel. :)
4

Cikk?

Joó Ádám · 2010. Nov. 10. (Sze), 00.07
Ahogy Tiku is írja lejjebb, a konkrét megoldásból, és a statisztikai elvekből szívesen olvasnánk egy cikket!
6

Attól tartok

janoszen · 2010. Nov. 10. (Sze), 13.39
Attól tartok, sosem tartozott az erősségeim közé. Ellenben úgy tűnik, két hét múlva tartok syslog / statisztika / stb. témakörből előadást. A részletek még jövő héten tisztázódnak, a Twitter feedemen ki fogom postolni, ha van új infó.
5

Hú, ez jó!

Gixx · 2010. Nov. 10. (Sze), 10.56
A @author megjelölésével, kicsit módosítva (osztály név, db név, log path stb) beépíthetem a saját rendszerembe?
7

nem orulok neki, hogy

Tyrael · 2010. Nov. 11. (Cs), 17.58
nem orulok neki, hogy lelogolod pl. a belepesi adataimat plaintextben.
ezert nem szeretem az ilyen altalanos logolo alkalmazasokat, mert konnyu megfelejtkezni rola, aztan hiaba tarolod a jelszavakat hashelve saltolva a user tablaban, ha az access_log-ban ott van minden plainben. :/

Tyrael
8

INSERT

janoszen · 2010. Nov. 11. (Cs), 18.49
Na erre mondtam én azt, hogy igen, és az alkalmazásnak csak INSERT jogosultságot szabad adni a log táblához. :)
11

nem megoldás

Hodicska Gergely · 2010. Nov. 14. (V), 19.53
Ettől még simán ott vannak a DB-ben a szenzitív adatok, simán elérik a sysopok, vagy ha épp megtörik a rendszert, és viszik a DB-t. És ugye a legtöbb user több oldalon is ugyanazokat a bejelentkezési adatokat használja, szóval a megoldás az az, hogy ha nem mentesz le ilyen adatokat.
9

password

tiku I tikaszvince · 2010. Nov. 12. (P), 08.50
Teljesen igazad van. Nem is tárolom le ;) Ez az osztály egy nagyban egyszerűsített változata az általam használtnak. Nem emlékeztem meg a naplók védelméről, vagy a naplózott adatok körének befolyásolásáról, de itt most nem is ez volt a cél.
10

igazabol csak azert irtam le,

Tyrael · 2010. Nov. 12. (P), 12.02
igazabol csak azert irtam le, hogy azert meg legyen emlitve.
a hozzaszolasbol is latszott, hogy eleg sokan esz nelkul atvesznek mindenfele kodreszletet, ezert nem szeretem, hogyha demo kodban valamilyen sebezhetoseg van, es legalabb egy figyelmeztetes nincs kirakva melle, hogy ez es ez vele a problema.

Tyrael
12

audit log

Hodicska Gergely · 2010. Nov. 14. (V), 19.56
Loggolásra simán lehet szükség akkor is, ha pl. utológas nyomonkövethetőség, felelősségre vonás miatt van arra szükséged, hogy XY mit csinált, vagy mondjuk mit kommunikáltál third party-kal stb.