Nástroj Webalizer a Google Analytics mi mnoho rokov pomáhali získať prehľad o tom, čo sa deje na webových stránkach. Teraz chápem, že poskytujú veľmi málo užitočných informácií. Mať prístup k svojmu súboru access.log, je veľmi jednoduché pochopiť štatistiku a na to stačia elementárne nástroje ako sqlite, html, jazyk sql a akýkoľvek skriptovací jazyk.
Zdrojom dát pre Webalizer je súbor access.log servera. Takto vyzerajú jeho stĺpce a čísla, z ktorých je zrejmý len celkový objem prevádzky:


Nástroje ako Google Analytics zbierajú údaje z načítanej stránky samostatne. Zobrazujú nám pár diagramov a línií, na základe ktorých je často ťažké urobiť správne závery. Možno som mal venovať viac úsilí? Neviem.
Čo som teda chcel vidieť v štatistike návštevnosti stránky?
Prevádzka používateľov a robotov
Často má prevádzka stránok obmedzenia a je potrebné vidieť, koľko užitočnej prevádzky sa využíva. Napríklad takto:

SQL dotaz na report
SELECT
1 as 'StackedArea: Prevádzka generovaná používateľmi a robotmi',
strftime('%d.%m', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Deň',
SUM(CASE WHEN USG.AGENT_BOT!='n.a.' THEN FCT.BYTES ELSE 0 END)/1000 AS 'Roboty, KB',
SUM(CASE WHEN USG.AGENT_BOT='n.a.' THEN FCT.BYTES ELSE 0 END)/1000 AS 'Používatelia, KB'
FROM
FCT_ACCESS_USER_AGENT_DD FCT,
DIM_USER_AGENT USG
WHERE FCT.DIM_USER_AGENT_ID=USG.DIM_USER_AGENT_ID
AND datetime(FCT.EVENT_DT, 'unixepoch') >= date('now', '-14 day')
GROUP BY strftime('%d.%m', datetime(FCT.EVENT_DT, 'unixepoch'))
ORDER BY FCT.EVENT_DTZ grafu je zrejmá stála aktivita robotov. Bolo by zaujímavé podrobne preskúmať najaktívnejších predstaviteľov.
Dotieraví roboti
Klasifikujeme roboty na základe informácií používateľského agenta. Dodatočná štatistika o dennej prevádzke, počte úspešných a neúspešných požiadaviek poskytuje dobrý prehľad o aktivite robotov.

SQL dotaz na report
SELECT
1 AS 'Tabuľka: Dotieraví Roboti',
MAX(USG.AGENT_BOT) AS 'Robot',
ROUND(SUM(FCT.BYTES)/1000/14.0, 1) AS 'KB za Deň',
ROUND(SUM(FCT.IP_CNT)/14.0, 1) AS 'IP za Deň',
ROUND(SUM(CASE WHEN STS.STATUS_GROUP IN ('Chyba klienta', 'Chyba servera') THEN FCT.REQUEST_CNT/14.0 ELSE 0 END), 1) AS 'Požiadavky s Chybou za Deň',
ROUND(SUM(CASE WHEN STS.STATUS_GROUP IN ('Úspešné', 'Presmerovanie') THEN FCT.REQUEST_CNT/14.0 ELSE 0 END), 1) AS 'Úspešné Požiadavky za Deň',
USG.USER_AGENT_NK AS 'Agent'
FROM FCT_ACCESS_USER_AGENT_DD FCT,
DIM_USER_AGENT USG,
DIM_HTTP_STATUS STS
WHERE FCT.DIM_USER_AGENT_ID = USG.DIM_USER_AGENT_ID
AND FCT.DIM_HTTP_STATUS_ID = STS.DIM_HTTP_STATUS_ID
AND USG.AGENT_BOT != 'n.a.'
AND datetime(FCT.EVENT_DT, 'unixepoch') >= date('now', '-14 day')
GROUP BY USG.USER_AGENT_NK
ORDER BY 3 DESC
LIMIT 10V tomto prípade analýza ukázala rozhodnutie obmedziť prístup na webovú stránku pridaním do súboru robots.txt
User-agent: AhrefsBot
Disallow: /
User-agent: dotbot
Disallow: /
User-agent: bingbot
Crawl-delay: 5
Prvé dva boty zmizli z tabuľky, zatiaľ čo roboty MS sa posunuli z prvých riadkov nižšie.
Deň a čas najväčšej aktivity
V prenosoch sú viditeľné vzostupy. Pre podrobné preskúmanie je potrebné určiť čas ich vzniku, pričom nie je nutné zobraziť všetky hodiny a dni merania času. Takto bude jednoduchšie nájsť jednotlivé požiadavky v logovom súbore, ak bude potrebná podrobná analýza.

SQL dotaz na report
SELECT
1 AS 'Riadok: Deň a hodina zásahov od používateľov a botov',
strftime('%d.%m-%H', datetime(EVENT_DT, 'unixepoch')) AS 'Dátum a čas',
HIB AS 'Boty, zásahy',
HIU AS 'Používatelia, zásahy'
FROM (
SELECT
EVENT_DT,
SUM(CASE WHEN AGENT_BOT!='n.a.' THEN LINE_CNT ELSE 0 END) AS HIB,
SUM(CASE WHEN AGENT_BOT='n.a.' THEN LINE_CNT ELSE 0 END) AS HIU
FROM FCT_ACCESS_REQUEST_REF_HH
WHERE datetime(EVENT_DT, 'unixepoch') >= date('now', '-14 day')
GROUP BY EVENT_DT
ORDER BY SUM(LINE_CNT) DESC
LIMIT 10
) ORDER BY EVENT_DTPozorujeme najaktívnejšie hodiny 11, 14 a 20 prvého dňa na grafe. Na druhý deň sa boty aktivovali o 13. hodine.
Priemerná denná aktivita používateľov za týždne
S aktivitou a prenosom sme si trošku porozumeli. Ďalšou otázkou bola aktivita samotných používateľov. Pre takúto štatistiku sú žiaduce dlhšie obdobia agregácie, napríklad týždeň.

SQL dotaz na report
SELECT
1 as 'Riadok: Priemerná denná aktivita používateľov podľa týždňa',
strftime('%W týždeň', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Týždeň',
ROUND(1.0*SUM(FCT.PAGE_CNT)/SUM(FCT.IP_CNT),1) AS 'Stránky na IP na deň',
ROUND(1.0*SUM(FCT.FILE_CNT)/SUM(FCT.IP_CNT),1) AS 'Súbory na IP na deň'
FROM
FCT_ACCESS_USER_AGENT_DD FCT,
DIM_USER_AGENT USG,
DIM_HTTP_STATUS HST
WHERE FCT.DIM_USER_AGENT_ID=USG.DIM_USER_AGENT_ID
AND FCT.DIM_HTTP_STATUS_ID = HST.DIM_HTTP_STATUS_ID
AND USG.AGENT_BOT='n.a.' /* len používatelia */
AND HST.STATUS_GROUP IN ('Successful') /* dobré stránky */
AND datetime(FCT.EVENT_DT, 'unixepoch') > date('now', '-3 month')
GROUP BY strftime('%W týždeň', datetime(FCT.EVENT_DT, 'unixepoch'))
ORDER BY FCT.EVENT_DTŠtatistika za týždeň ukazuje, že v priemere jeden používateľ otvorí 1,6 stránky za deň. Počet požadovaných súborov na jedného používateľa v tomto prípade závisí od pridávania nových súborov na web.
Všetky požiadavky a ich stavy
Webalizer vždy zobrazoval konkrétne kódy stránok a vždy by som chcel vidieť len počet úspešných požiadaviek a chýb.

SQL dotaz na report
VYBER
1 ako 'Riadok: Všetky žiadosti podľa stavu',
strftime('%d.%m', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Deň',
SUM(CASE WHEN STS.STATUS_GROUP='Úspešné' THEN FCT.REQUEST_CNT ELSE 0 END) AS 'Úspech',
SUM(CASE WHEN STS.STATUS_GROUP='Presmerovanie' THEN FCT.REQUEST_CNT ELSE 0 END) AS 'Presmerovanie',
SUM(CASE WHEN STS.STATUS_GROUP='Chyba klienta' THEN FCT.REQUEST_CNT ELSE 0 END) AS 'Chyba zákazníka',
SUM(CASE WHEN STS.STATUS_GROUP='Chyba servera' THEN FCT.REQUEST_CNT ELSE 0 END) AS 'Chyba servera'
Z
FCT_ACCESS_USER_AGENT_DD FCT,
DIM_HTTP_STATUS STS
KDE FCT.DIM_HTTP_STATUS_ID=STS.DIM_HTTP_STATUS_ID
A datetime(FCT.EVENT_DT, 'unixepoch') >= date('now', '-14 day')
GROUP BY strftime('%d.%m', datetime(FCT.EVENT_DT, 'unixepoch'))
ORDER BY FCT.EVENT_DTSpráva zobrazuje žiadosti, a nie kliknutia (hity), na rozdiel od metriky LINE_CNT sa REQUEST_CNT počíta ako COUNT(DISTINCT STG.REQUEST_NK). Cieľom je ukázať efektívne udalosti, napríklad boti MS opakovane kontrolujú súbor robots.txt dvakrát denne a v tomto prípade budú takéto kontroly počítané len raz. Týmto sa vyhladzujú skoky na grafe.
Z grafu je možné vidieť množstvo chýb — neexistujúce stránky. Výsledkom analýzy bolo pridanie presmerovaní zo zrušených stránok.
Chybné požiadavky
Pre podrobnejšie preskúmanie žiadostí je možné zobraziť podrobnú štatistiku.

SQL dotaz na report
VYBER
1 AS 'Tabuľa: Najčastejšie chybné požiadavky',
REQ.REQUEST_NK AS 'Žiadosť',
'Chyba' AS 'Stav požiadavky',
ROUND(SUM(FCT.LINE_CNT) / 14.0, 1) AS 'Hity za deň',
ROUND(SUM(FCT.IP_CNT) / 14.0, 1) AS 'IP za deň',
ROUND(SUM(FCT.BYTES)/1000 / 14.0, 1) AS 'KB za deň'
Z
FCT_ACCESS_REQUEST_REF_HH FCT,
DIM_REQUEST_V_ACT REQ
KDE FCT.DIM_REQUEST_ID = REQ.DIM_REQUEST_ID
A FCT.STATUS_GROUP IN ('Chyba klienta', 'Chyba servera')
A datetime(FCT.EVENT_DT, 'unixepoch') >= date('now', '-14 day')
GROUP BY REQ.REQUEST_NK
ORDER BY 4 DESC
LIMIT 20V tomto zozname budú zahrnuté aj všetky dopyty, napríklad požiadavka na /wp-login.php. Úpravou pravidiel pre prepisovanie požiadaviek serverom je možné upraviť reakciu servera na takéto požiadavky a presmerovať ich na domovskú stránku.
Takže niekoľko jednoduchých správ na základe súboru logu servera poskytuje dostatočne komplexný obraz toho, čo sa na stránke deje.
Ako získať informácie?
Databázy sqlite sú úplne dostatočné. Vytvoríme tabuľky: pomocnú pre logovanie procesov ETL.

Stage tabuľka, kam budeme zapisovať log súbory pomocou PHP. Dve tabuľky agregátov. Vytvoríme dennú tabuľku so štatistikami podľa používateľských agentov a stavov požiadaviek. Hodinovú so štatistikami podľa požiadaviek, skupín stavov a agentov. Štyri tabuľky príslušných dimenzií.
Výsledkom je nasledujúci relačný model:
Model dát
Skript na vytvorenie objektu v databáze sqlite:
DDL vytvorenie objektu
DROP TABLE IF EXISTS DIM_USER_AGENT;
CREATE TABLE DIM_USER_AGENT (
DIM_USER_AGENT_ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
USER_AGENT_NK TEXT NOT NULL DEFAULT 'n.a.',
AGENT_OS TEXT NOT NULL DEFAULT 'n.a.',
AGENT_ENGINE TEXT NOT NULL DEFAULT 'n.a.',
AGENT_DEVICE TEXT NOT NULL DEFAULT 'n.a.',
AGENT_BOT TEXT NOT NULL DEFAULT 'n.a.',
UPDATE_DT INTEGER NOT NULL DEFAULT 0,
UNIQUE (USER_AGENT_NK)
);
INSERT INTO DIM_USER_AGENT (DIM_USER_AGENT_ID) VALUES (-1);Stage
V prípade súboru access.log je potrebné prečítať, analyzovať a zapísať do databázy všetky požiadavky. To je možné urobiť buď priamo pomocou skriptovacieho jazyka, alebo pomocou nástrojov sqlite.
Formát log súboru:
//67.221.59.195 - - [28/Dec/2012:01:47:47 +0100] "GET /files/default.css HTTP/1.1" 200 1512 "https://project.edu/" "Mozilla/4.0"
//host ident auth time method request_nk protocol status bytes ref browser
$log_pattern = '/^([^ ]+) ([^ ]+) ([^ ]+) ([[^]]+]) "(.*) (.*) (.*)" ([0-9-]+) ([0-9-]+) "(.*)" "(.*)"$/';
Propagácia kľúčov
Keď sú surové dáta v databáze, je potrebné zapísať do tabuliek dimenzií kľúče, ktoré tam nie sú. Potom bude možné vytvoriť odkaz na dimenzie. Napríklad v tabuľke DIM_REFERRER je kľúčom kombinácia troch polí.
SQL dopyt na propagáciu kľúčov
/* Propagate the referrer from access log */
INSERT INTO DIM_REFERRER (HOST_NK, PATH_NK, QUERY_NK, UPDATE_DT)
SELECT
CLS.HOST_NK,
CLS.PATH_NK,
CLS.QUERY_NK,
STRFTIME('%s','now') AS UPDATE_DT
FROM (
SELECT DISTINCT
REFERRER_HOST AS HOST_NK,
REFERRER_PATH AS PATH_NK,
CASE WHEN INSTR(REFERRER_QUERY,'&sid')>0 THEN SUBSTR(REFERRER_QUERY, 1, INSTR(REFERRER_QUERY,'&sid')-1) /* отрезаем sid - специфика цмс */
ELSE REFERRER_QUERY END AS QUERY_NK
FROM STG_ACCESS_LOG
) CLS
LEFT OUTER JOIN DIM_REFERRER TRG
ON (CLS.HOST_NK = TRG.HOST_NK AND CLS.PATH_NK = TRG.PATH_NK AND CLS.QUERY_NK = TRG.QUERY_NK)
WHERE TRG.DIM_REFERRER_ID IS NULLPropagácia do tabuľky používateľských agentov môže obsahovať logiku botov, napríklad úryvok sql:
CASE
WHEN INSTR(LOWER(CLS.BROWSER),'yandex.com')>0
THEN 'yandex'
WHEN INSTR(LOWER(CLS.BROWSER),'googlebot')>0
THEN 'google'
WHEN INSTR(LOWER(CLS.BROWSER),'bingbot')>0
THEN 'microsoft'
WHEN INSTR(LOWER(CLS.BROWSER),'ahrefsbot')>0
THEN 'ahrefs'
WHEN INSTR(LOWER(CLS.BROWSER),'mj12bot')>0
THEN 'majestic-12'
WHEN INSTR(LOWER(CLS.BROWSER),'compatible')>0 OR INSTR(LOWER(CLS.BROWSER),'http')>0
OR INSTR(LOWER(CLS.BROWSER),'libwww')>0 OR INSTR(LOWER(CLS.BROWSER),'spider')>0
OR INSTR(LOWER(CLS.BROWSER),'java')>0 OR INSTR(LOWER(CLS.BROWSER),'python')>0
OR INSTR(LOWER(CLS.BROWSER),'robot')>0 OR INSTR(LOWER(CLS.BROWSER),'curl')>0
OR INSTR(LOWER(CLS.BROWSER),'wget')>0
THEN 'other'
ELSE 'n.a.' END AS AGENT_BOTTabuľky agregátov
Na záver nahráme tabuľky agregátov, napríklad denná tabuľka sa môže nahrávať nasledujúcim spôsobom:
SQL dopyt na načítanie agregátu
/* Load fact from access log */
INSERT INTO FCT_ACCESS_USER_AGENT_DD (EVENT_DT, DIM_USER_AGENT_ID, DIM_HTTP_STATUS_ID, PAGE_CNT, FILE_CNT, REQUEST_CNT, LINE_CNT, IP_CNT, BYTES)
WITH STG AS (
SELECT
STRFTIME( '%s', SUBSTR(TIME_NK,9,4) || '-' ||
CASE SUBSTR(TIME_NK,5,3)
WHEN 'Jan' THEN '01' WHEN 'Feb' THEN '02' WHEN 'Mar' THEN '03' WHEN 'Apr' THEN '04' WHEN 'May' THEN '05' WHEN 'Jun' THEN '06'
WHEN 'Jul' THEN '07' WHEN 'Aug' THEN '08' WHEN 'Sep' THEN '09' WHEN 'Oct' THEN '10' WHEN 'Nov' THEN '11'
ELSE '12' END || '-' || SUBSTR(TIME_NK,2,2) || ' 00:00:00' ) AS EVENT_DT,
BROWSER AS USER_AGENT_NK,
REQUEST_NK,
IP_NR,
STATUS,
LINE_NK,
BYTES
FROM STG_ACCESS_LOG
)
SELECT
CAST(STG.EVENT_DT AS INTEGER) AS EVENT_DT,
USG.DIM_USER_AGENT_ID,
HST.DIM_HTTP_STATUS_ID,
COUNT(DISTINCT (CASE WHEN INSTR(STG.REQUEST_NK,'.')=0 THEN STG.REQUEST_NK END) ) AS PAGE_CNT,
COUNT(DISTINCT (CASE WHEN INSTR(STG.REQUEST_NK,'.')>0 THEN STG.REQUEST_NK END) ) AS FILE_CNT,
COUNT(DISTINCT STG.REQUEST_NK) AS REQUEST_CNT,
COUNT(DISTINCT STG.LINE_NK) AS LINE_CNT,
COUNT(DISTINCT STG.IP_NR) AS IP_CNT,
SUM(BYTES) AS BYTES
FROM STG,
DIM_HTTP_STATUS HST,
DIM_USER_AGENT USG
WHERE STG.STATUS = HST.STATUS_NK
AND STG.USER_AGENT_NK = USG.USER_AGENT_NK
AND CAST(STG.EVENT_DT AS INTEGER) > $param_epoch_from /* load epoch date */
AND CAST(STG.EVENT_DT AS INTEGER) < strftime('%s', date('now', 'start of day'))
GROUP BY STG.EVENT_DT, HST.DIM_HTTP_STATUS_ID, USG.DIM_USER_AGENT_IDDatabáza sqlite umožňuje písať zložité dopyty. WITH obsahuje prípravu dát a kľúčov. Hlavný dopyt zbiera všetky odkazy na dimenzie.
Podmienka nedovolí znova načítať históriu: CAST(STG.EVENT_DT AS INTEGER) > $param_epoch_from, kde parameter je výsledkom dotazu
SELECT COALESCE(MAX(EVENT_DT), '3600') AS LAST_EVENT_EPOCH FROM FCT_ACCESS_USER_AGENT_DD
Podmienka načíta iba celý deň: CAST(STG.EVENT_DT AS INTEGER) < strftime('%s', date('now', 'start of day'))
Počítanie stránok alebo súborov sa vykonáva primitívnym spôsobom, hľadaním bodu.
Správy
V zložitých vizualizačných systémoch je možné vytvárať meta-model na základe objektov databázy, dynamicky spravovať filtre a pravidlá agregácie. V konečnom dôsledku všetky slušné nástroje generujú SQL dopyt.
V tomto príklade vytvoríme už hotové SQL dotazy a uložíme ich vo forme view do databázy — to sú reporty.
Vizualizácia
Na vizualizáciu sa použil nástroj Bluff: Beautiful graphs in JavaScript
Na to bolo potrebné pomocou PHP prechádzať všetkými reportmi a vygenerovať html súbor s tabuľkami.
$sqls = array(
'SELECT * FROM RPT_ACCESS_USER_VS_BOT',
'SELECT * FROM RPT_ACCESS_ANNOYING_BOT',
'SELECT * FROM RPT_ACCESS_TOP_HOUR_HIT',
'SELECT * FROM RPT_ACCESS_USER_ACTIVE',
'SELECT * FROM RPT_ACCESS_REQUEST_STATUS',
'SELECT * FROM RPT_ACCESS_TOP_REQUEST_PAGE',
'SELECT * FROM RPT_ACCESS_TOP_REQUEST_REFERRER',
'SELECT * FROM RPT_ACCESS_NEW_REQUEST',
'SELECT * FROM RPT_ACCESS_TOP_REQUEST_SUCCESS',
'SELECT * FROM RPT_ACCESS_TOP_REQUEST_ERROR'
);Nástroj jednoducho vizualizuje tabuľky výsledkov.
Záver
Na príklade webovej analýzy článok opisuje mechanizmy potrebné na vytvorenie dátových skladov. Ako je vidieť z výsledkov, na hlbokú analýzu a vizualizáciu dát sú potrebné iba najjednoduchšie nástroje.
V ďalšom kroku, na príklade tohto skladu, sa pokúsime implementovať štruktúry ako pomaly sa meniacich dimenzií, metadát, úrovne agregácie a integráciu dát z rôznych zdrojov.
Taktiež si podrobnejšie prejdeme najjednoduchší nástroj na riadenie ETL procesov založený na jednej tabuľke.
Vrátime sa k téme merania kvality dát a automatizácie tohto procesu.
Preskúmame problémy technického prostredia a údržby datových skladov, na čo zrealizujeme server skladu s minimálnymi zdrojmi, napríklad na báze Raspberry Pi.
Zdroj: habr.com
