Webbplatsstatistik och sin egen lilla lagring

Verktyget Webalizer och Google Analytics har hjÀlpt mig i mÄnga Är att fÄ en uppfattning om vad som hÀnder pÄ webbplatser. Nu inser jag att de ger mycket lite anvÀndbar information. Med tillgÄng till min access.log-fil Àr det mycket enkelt att förstÄ statistiken, och för att genomföra detta behövs bara grundlÀggande verktyg som sqlite, html, sql-sprÄk och vilket skriptsprÄk som helst.

DatakÀllan för Webalizer Àr access.log-filen servern. SÄ hÀr ser dess kolumner och siffror ut, som bara ger en översikt över den totala trafiken:

Webbplatsstatistik och sin egen lilla lagring
Webbplatsstatistik och sin egen lilla lagring
SÄdana verktyg som Google Analytics samlar in data frÄn den laddade sidan pÄ egen hand. De visar oss nÄgra diagram och linjer, utifrÄn vilka det ofta Àr svÄrt att dra korrekta slutsatser. Kanske borde ytterligare insatser ha gjorts? Jag vet inte.

SÄ vad ville jag se i webbplatsens besöksstatistik?

AnvÀndartjÀnsternas och botternas trafik

Ofta har webbplatser trafikbegrÀnsningar och det Àr nödvÀndigt att se hur mycket anvÀndbar trafik som utnyttjas. Till exempel sÄ hÀr:

Webbplatsstatistik och sin egen lilla lagring

SQL-frÄgan för rapporten

SELECT
1 AS 'StackedArea: Trafik genererad av AnvÀndare och Bots',
strftime('%d.%m', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Dag',
SUM(CASE WHEN USG.AGENT_BOT != 'n.a.' THEN FCT.BYTES ELSE 0 END) / 1000 AS 'Bots, KB',
SUM(CASE WHEN USG.AGENT_BOT = 'n.a.' THEN FCT.BYTES ELSE 0 END) / 1000 AS 'AnvÀndare, 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_DT

Grafen visar konstant aktivitet frÄn bots. Det skulle vara intressant att noggrant studera de mest aktiva representanterna.

PÄtrÀngande bots

Vi klassificerar bots baserat pÄ information frÄn anvÀndaragenten. Ytterligare statistik över daglig trafik, antal lyckade och misslyckade förfrÄgningar ger en bra kÀnsla för botarnas aktivitet.

Webbplatsstatistik och sin egen lilla lagring

SQL-frÄgan för rapporten

SELECT 
1 AS 'Tabell: PÄtrÀngande Bots',
MAX(USG.AGENT_BOT) AS 'Bot',
ROUND(SUM(FCT.BYTES) / 1000 / 14.0, 1) AS 'KB per Dag',
ROUND(SUM(FCT.IP_CNT) / 14.0, 1) AS 'IPs per Dag',
ROUND(SUM(CASE WHEN STS.STATUS_GROUP IN ('Klientfel', 'Serverfel') THEN FCT.REQUEST_CNT / 14.0 ELSE 0 END), 1) AS 'Fel förfrÄgningar per Dag',
ROUND(SUM(CASE WHEN STS.STATUS_GROUP IN ('Lyckad', 'Omdirigering') THEN FCT.REQUEST_CNT / 14.0 ELSE 0 END), 1) AS 'Lyckade förfrÄgningar per Dag',
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 10

I detta fall resulterade analysen i ett beslut att begrÀnsa Ätkomsten till webbplatsen genom att lÀgga till det i filen robots.txt

AnvÀndaragent: AhrefsBot
Som inte tillÄts: /
AnvÀndaragent: dotbot
Som inte tillÄts: /
AnvÀndaragent: bingbot
KrÀv fördröjning: 5

De tvÄ första botarna försvann frÄn tabellen, medan MS-botarna flyttades nerÄt.

Tidpunkt för högst aktivitet

I trafiken syns toppar. För att detaljstudera dem Àr det nödvÀndigt att sÀrskilja tiden för deras uppkomst, samtidigt som det inte Àr nödvÀndigt att visa alla timmar och dagar för tidsmÀtningen. Det blir enklare att hitta specifika förfrÄgningar i loggfilen vid behov av detaljerad analys.

Webbplatsstatistik och sin egen lilla lagring

SQL-frÄgan för rapporten

SELECT
1 AS 'Rad: Dag och Timme för Besök frÄn AnvÀndare och Botar',
strftime('%d.%m-%H', datetime(EVENT_DT, 'unixepoch')) AS 'Datum Tid',
HIB AS 'Botar, Besök',
HIU AS 'AnvÀndare, Besök'
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_DT

Vi observerar de mest aktiva timmarna 11, 14 och 20 den första dagen pÄ grafen. NÀsta dag vid 13-tiden var botarna aktiva.

Genomsnittlig daglig aktivitet för anvÀndare per vecka

Vi har fÄtt grepp om aktiviteten och trafiken. NÀsta frÄga gÀllde aktivitet hos anvÀndarna sjÀlva. För sÄdan statistik Àr lÀngres perioder av aggregering önskvÀrda, exempelvis en vecka.

Webbplatsstatistik och sin egen lilla lagring

SQL-frÄgan för rapporten

SELECT
1 as 'Rad: Genomsnittlig Daglig AnvÀndaraktivitet per Vecka',
strftime('%W vecka', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Vecka',
ROUND(1.0*SUM(FCT.PAGE_CNT)/SUM(FCT.IP_CNT),1) AS 'Sidor per IP per Dag',
ROUND(1.0*SUM(FCT.FILE_CNT)/SUM(FCT.IP_CNT),1) AS 'Filer per IP per Dag'
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.' /* endast anvÀndare */
  AND HST.STATUS_GROUP IN ('Successful') /* bra sidor */
  AND datetime(FCT.EVENT_DT, 'unixepoch') > date('now', '-3 month')
GROUP BY strftime('%W vecka', datetime(FCT.EVENT_DT, 'unixepoch'))
ORDER BY FCT.EVENT_DT

Statistik för veckan visar att i genomsnitt öppnar en anvÀndare 1,6 sidor per dag. Antalet begÀrda filer per anvÀndare beror i detta fall pÄ tillÀgg av nya filer pÄ webbplatsen.

Alla förfrÄgningar och deras statusar

Webalizer visade alltid specifika koder för sidor och det var alltid önskvÀrt att bara se mÀngden lyckade förfrÄgningar och fel.

Webbplatsstatistik och sin egen lilla lagring

SQL-frÄgan för rapporten

VÄLJ
1 som 'Rad: Alla förfrÄgningar efter status',
strftime('%d.%m', datetime(FCT.EVENT_DT, 'unixepoch')) SOM 'Dag',
SUM(CASE NÄR STS.STATUS_GROUP='FramgĂ„ngsrik' DÅ FCT.REQUEST_CNT ANNARS 0 SLUT) SOM 'FramgĂ„ng',
SUM(CASE NÄR STS.STATUS_GROUP='Omdirigering' DÅ FCT.REQUEST_CNT ANNARS 0 SLUT) SOM 'Omdirigera',
SUM(CASE NÄR STS.STATUS_GROUP='Klientfel' DÅ FCT.REQUEST_CNT ANNARS 0 SLUT) SOM 'Klientfel',
SUM(CASE NÄR STS.STATUS_GROUP='Serverfel' DÅ FCT.REQUEST_CNT ANNARS 0 SLUT) SOM 'Serverfel'
FRÅN
  FCT_ACCESS_USER_AGENT_DD FCT,
  DIM_HTTP_STATUS STS
VARFÖR FCT.DIM_HTTP_STATUS_ID=STS.DIM_HTTP_STATUS_ID
  OCH datetime(FCT.EVENT_DT, 'unixepoch') >= date('nu', '-14 dagar')
GRUPPERA EFTER strftime('%d.%m', datetime(FCT.EVENT_DT, 'unixepoch'))
ORDNA EFTER FCT.EVENT_DT

Rapporten visar förfrÄgningar, inte klick (trÀffar), till skillnad frÄn LINE_CNT mÀts REQUEST_CNT som COUNT(DISTINCT STG.REQUEST_NK). MÄlet Àr att visa effektiva hÀndelser, till exempel, MS-botar som flera gÄnger per dag frÄgar filen robots.txt och i det hÀr fallet kommer sÄdana frÄgor att rÀknas som en gÄng. Detta hjÀlper till att jÀmna ut toppar pÄ grafen.

FrĂ„n grafen kan man se mĂ„nga fel — det Ă€r icke-existerande sidor. Resultatet av analysen var att lĂ€gga till omdirigeringar frĂ„n borttagna sidor.

Felaktiga förfrÄgningar

För att noggrant granska förfrÄgningarna kan detaljerad statistik visas.

Webbplatsstatistik och sin egen lilla lagring

SQL-frÄgan för rapporten

VÄLJ
  1 SOM 'Tabell: Topp-fel-förfrÄgningar',
  REQ.REQUEST_NK SOM 'FörfrÄgan',
  'Fel' SOM 'FörfrÄgningsstatus',
  AVGÄR(SUM(FCT.LINE_CNT) / 14.0, 1) SOM 'TrĂ€ffar per dag',
  AVGÄR(SUM(FCT.IP_CNT) / 14.0, 1) SOM 'IP-adresser per dag',
  AVGÄR(SUM(FCT.BYTES)/1000 / 14.0, 1) SOM 'KB per dag'
FRÅN
  FCT_ACCESS_REQUEST_REF_HH FCT,
  DIM_REQUEST_V_ACT REQ
VARFÖR FCT.DIM_REQUEST_ID = REQ.DIM_REQUEST_ID
  OCH FCT.STATUS_GROUP I ('Klientfel', 'Serverfel')
  OCH datetime(FCT.EVENT_DT, 'unixepoch') >= date('nu', '-14 dagar')
GRUPPERA EFTER REQ.REQUEST_NK
ORDNA EFTER 4 DESC
BEGRÄNSA 20

I den hÀr listan kommer ocksÄ alla samtal att finnas, till exempel, förfrÄgan till /wp-login.php Genom att justera reglerna för omskrivning av förfrÄgningar servern kan vi justera serverns respons pÄ sÄdana förfrÄgningar och skicka dem till startsidan.

SÄ, nÄgra enkla rapporter baserade pÄ serverloggfilen ger en tillrÀckligt fullstÀndig bild av vad som hÀnder pÄ webbplatsen.

Hur fÄr man information?

SQLite-databaser Àr helt tillrÀckliga. Vi skapar tabeller: en hjÀlptabell för loggning av ETL-processer.

Webbplatsstatistik och sin egen lilla lagring

Staging-tabell dÀr vi kommer att skriva loggfiler med hjÀlp av PHP. TvÄ aggregator-tabeller. Vi skapar en daglig tabell med statistik över anvÀndaragenter och förfrÄgningsstatusar. En timmevis med statistik över förfrÄgningar, statusgrupper och agenter. Fyra tabeller för de relevanta mÀtningarna.

Som ett resultat blev den följande relationella modellen:

DatamodellWebbplatsstatistik och sin egen lilla lagring

Script för att skapa objekt i SQLite-databasen:

DDL skapa objekt

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);

Staging

För access.log-filen mÄste alla förfrÄgningar lÀsas, parsa och skrivas till databasen. Detta kan göras antingen direkt med skriptsprÄket eller med hjÀlp av sqlite.

Loggfilsformat:

//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-]+) "(.*)" "(.*)"$/';

NyckelpÄdragning

NÀr rÄdata finns i databasen mÄste nycklarna för dimensionerna, som inte finns dÀr, skrivas till tabellerna. DÄ blir det möjligt att bygga kopplingar till dimensionerna. Till exempel Àr nyckeln i tabellen DIM_REFERRER en kombination av tre fÀlt.

SQL-frÄga för nyckelpÄdragning

/* 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 NULL

NyckelpÄdragning i tabellen för anvÀndaragenter kan innehÄlla bot-logik, till exempel en SQL-snutt:


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_BOT

Aggregattabeller

Sist men inte minst ska vi ladda aggregattabeller, till exempel kan den dagliga tabellen laddas pÄ följande sÀtt:

SQL-frÄga för laddning av aggregat

/* 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_ID

SQLite-databasen tillÄter skrivning av komplexa frÄgor. WITH innehÄller förberedelse av data och nycklar. HuvudfrÄgan samlar alla kopplingar till dimensionerna.

Villkoret hindrar att historik laddas in igen: CAST(STG.EVENT_DT AS INTEGER) > $param_epoch_from, dÀr parametern Àr resultatet av frÄgan
‘SELECT COALESCE(MAX(EVENT_DT), ‘3600’) AS LAST_EVENT_EPOCH FROM FCT_ACCESS_USER_AGENT_DD’

Villkoret laddar endast hela dagar: CAST(STG.EVENT_DT AS INTEGER) < strftime(‘%s’, date(‘now’, ‘start of day’))

Antal sidor eller filer rÀknas pÄ ett primitivt sÀtt genom att söka efter punkter.

Rapporter

I komplexa visualiseringssystem finns möjlighet att skapa en metamodell baserad pÄ databasen, dynamiskt hantera filter och aggregeringsregler. I slutÀndan genererar alla seriösa verktyg SQL-frÄgor.

I detta exempel kommer vi att skapa fĂ€rdiga SQL-frĂ„gor och spara dem som vyer i databasen — det Ă€r rapporter.

Visualisering

För visualisering av verktyg anvÀndes Bluff: Beautiful graphs in JavaScript

För detta krÀvdes det att anvÀnda PHP för att gÄ igenom alla rapporter och generera en html-fil med tabeller.

$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'
);

Verktyget visualiserar helt enkelt resultatens tabeller.

Utdata

I exemplet med webbanalys beskriver artikeln de mekanismer som behövs för att bygga datalager. Som resultaten visar, rÀcker de enklaste verktygen för djup analys och visualisering av data.

I framtiden, med hjÀlp av detta datalager, kommer vi att försöka implementera strukturer som lÄngsam förÀndring av dimensioner, metadata, aggregeringsnivÄer och dataintegration frÄn olika kÀllor.

Vi kommer ocksÄ att titta pÄ det enklaste verktyget för att hantera ETL-processer baserat pÄ en enda tabell.

LÄt oss ÄtervÀnda till temat om mÀtning av datakvalitet och automatiseringen av denna process.

Vi kommer att studera problemen med den tekniska miljön och underhÄllet av datalager, för vilket vi implementerar en lagringsserver med minimala resurser, exempelvis baserat pÄ Raspberry Pi.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster