Apache-Log mit Python und Plotly auswerten
Mit der Zeit wird es langweilig die Pipeline für die Logauswertung manuell auszuführen. Ein Aufruf müsste genügen um die Analyse durchzuführen.
pd.read_csv() zum lesen des Logfiles
Den Lösungsansatz habe ich bei R gefunden: read.table. Die Suche bei Pandas führte zu einer ähnlichen Funktion, aber die war schon mal deprecated...
Anscheinend ist sie jetzt nicht mehr deprecated, aber ein ungutes Gefühl erzeugt das schon. In der Beschreibung dieser Funktion gibt es eine Referenz auf read_csv. Auf diese Idee wäre ich nie selber gekommen, aber ein bisschen pröbeln mit den Optionen ergab recht schnell einen Dataframe mit den Logdaten.
Das Löschen aller unerwünschten Einträge ist nicht so einfach wie beim zeilenweisen Lesen, aber auch nicht besonders schwierig.
Kein Bar-Chart mit einer Zeitachse
Weil die Zugriffe für jeden Tag kumuliert werden, erschien mir ein Bar-Chart passend für die Darstellung. Das macht aber erstaunliche Probleme, sowohl bei Matplotlib als auch bei Plotly. Zeitachsen für Bar-Charts sind schlicht nicht vorgesehen!
Dutzende von Versuchen mit Matplotlib brachten nur ziemlich unverständliche Fehlermeldungen. Schlussendlich gelang es mit dem Datum als String der Library eine kategorische Achse vorzutäuschen. Das ist nicht so abstrus wie es auf den ersten Blick aussieht, weil jeder Tag im Log vorkommt. Wenn es Löcher gäbe, müssten die fehlenden Tage mit einem Count von 0 aufgeführt werden...
Weil es so viele Balken gibt, sind sie sehr schmal und nicht so einfach den Angaben auf den Achsen zuzuordnen. Plotly dagegen kann Tooltips mit definierbaren Angaben anzeigen, und das bringt eine viel bessere Orientierung.
Probleme mit Plotly
Ab und zu brachte Plotly einen Chart, aber meistens blieb der Browser hängen bis zum Timeout. Das Script lief bis zum Timeout und wurde dann ohne jegliche Meldung beendet!
Es gibt keinerlei Anhaltspunkte was schief läuft und deshalb auch keine Möglichkeit irgendwas zu debuggen :-(
Stack Overflow kam mir dann zu Hilfe mit der Zeile: fig.write_html( "tmp.html", auto_open=True )
Das schreibt den Plot in ein File, und ruft den Browser damit auf. So funktioniert es seither absolut problemlos.
Bars einfärben
Die nächste Ueberraschung tauchte auf als ich die Wochenenden farbig markieren wollte. Plotly meint dass die gleich eingefärbten Balken zusammengehören, und ordnet alle Samstage und Sonntage rechts im Chart an!
Es blieb nichts anderes übrig als die Position aller Balken fest vorzugeben, mit der Option category_orders.
All diese Erfahrungen haben viel Zeit gekostet, aber das Ziel wurde erreicht:
Die farbigen Balken zeigen deutlich dass der Wochentag auf diesem Server keinerlei Einfluss auf die Zugriffszahlen hat. Das ist schon erstaunlich.
Eine kleine Unschönheit gibt es übrigens noch: im Tooltip wird das Weekend-Flag aufgeführt. Das habe ich bisher nicht wegbekommen.
Das Python-Sript für die Auswertung
# Import the libraries import plotly.express as px import pandas as pd import datetime print( "Reading the apache log" ) # Read the logfile df = pd.read_csv( "access.log", header=None, sep=" ", quotechar="\"", escapechar="\\" ) df.head() print( "Cleaning the log" ) # Drop unused columns df.drop( [1,2,4,8], axis="columns", inplace=True ) # Remove all errors df = df[df[6] < 400] # Filter our own requests df = df[df[0]!="81.6.49.243"] # Filter IPs with suspicious access patterns suspicious = [ "82.80.249.137", "82.80.249.159", "82.80.249.249", "146.4.22.190", "212.227.250.21", "95.217.74.38" ] df = df[df[0].apply( lambda ip: ip not in suspicious )] # IP is no longer useful, drop it df.drop( [0], axis="columns", inplace=True ) # Preparation for the bot filter df[9] = df[9].str.lower() df.head() # Filter the bots bots = [ "adsbot", "adscanner", "ahrefsbot", "alphabot", "alphaseobot", "applebot", "aspiegelbot", "bingbot", "blexbot", "borneobot", "bot@linkfluence", "bot@tracemyfile", "brands-bot", "ccbot", "clarabot", "cliqzbot", "coccocbot", "discordbot", "dnsresearchbot", "domainstatsbot", "dotbot", "duckduckbot", "exabot", "facebot", "frobots", "gigabot", "googlebot", "internet-structure-research-project-bot", "jobboersebot", "kazbtbot", "keybot", "linguee bot", "mauibot", "mj12bot", "msnbot", "nimbostratus-bot", "niuebot", "obot", "our-bot", "adbeat_bot", "petalbot", "pinterestbot", "pooplebot", "ru_bot", "scraperbot", "semrushbot", "seobilitybot", "seokicks", "serpstatbot", "seznambot", "sidetrade indexer bot", "smtbot", "statvoobot", "surdotlybot", "tigerbot", "tmmbot", "triplecheckerrobot", "twitterbot", "vebidoobot", "webtechbot", "wiederfreibot", "x28-job-bot", "yacybot", "yandexbot", "zoominfobot", "spider@seocompany.store", "barkrowler", "website-datenbank.de", "crawler_eb_germany", "searchatlas.com", "adstxtcrawler", "backlinkcrawler", "cipacrawler", "domaincrawler", "grapeshotcrawler", "mbcrawler", "webcrawler", "crawler4j", "sslyze", "localsearch", "winhttprequest", "webdatastats" ] for bot in bots: df = df[df[9].str.contains(bot)==False] # Keep just two columns df.drop( [5,7,9], axis="columns", inplace=True ) print( "Group by days" ) # Convert the string to a date df[3] = df[3].apply( lambda d: datetime.datetime.strptime(d[1:12],"%d/%b/%Y") ) # Group and count the requests grp = df.groupby( [3] ).count().reset_index() print( "Add computed columns" ) # Rename the columns grp.columns = ["date","count"] # Add a column for the weekday grp["weekday"] = grp["date"].apply( lambda d: d.strftime("%a") ) # Add a column for the weekend grp["weekend"] = grp["date"].apply( lambda d: "no" if d.weekday() < 5 else "yes" ) # Convert the date back to a string! grp["date"] = grp["date"].apply( lambda d: d.strftime("%d.%m.%y") ) print( "Generate the plot" ) # Plot the data fig = px.bar( grp, x="date", y="count", color="weekend", color_discrete_map={ "yes":"red", "no":"blue" }, hover_data=["date","count","weekday"], category_orders={ "date": grp["date"].tolist() } ) fig.update_yaxes( title="", visible=True, showticklabels=True ) fig.update_layout( showlegend=False ) # Write plot to file and show it fig.write_html( "tmp.html", auto_open=True )
Die Print-Statements sind nur dazu da um dem User zu zeigen zu geben was das Script gerade macht. Es läuft eigentlich nicht lange, aber heute wollen die User sofortige Reaktionen.
Bei den Bots sind ein paar dazugekommen, die vermehren sich anscheinend wöchentlich.
Wenn Sie selber mit dem Script herumspielen wollen, dann laden Sie es hier herunter.