[PYTHON] Analysieren Sie Apache-Zugriffsprotokolle mit Pandas und Matplotlib

Überblick

Selbst wenn Sie versuchen, Zugriffsprotokolle wie Apache und Tomcat zu analysieren, gibt es kein überraschend gutes Tool. Ich hätte gerne ein Tool, das verschiedene Ausgabeformate unterstützt, nur die erforderlichen Informationen filtert und das Verständnis und die Visualisierung erleichtert, aber ich kann es nicht leicht finden.

In der Welt der Datenanalyse habe ich versucht, Apache-Zugriffsprotokolle auf Jupyter Notebook mithilfe von Pandas und Matplotlib, die in der Welt der Datenanalyse Standard sind, einfach zu analysieren und zu visualisieren. logo.png

Zugriffsprotokoll lesen

Importieren Sie die erforderlichen Bibliotheken

Importieren Sie zunächst die minimal erforderlichen Pandas und Matplotlib.

import pandas as pd
from pandas import DataFrame, Series
import matplotlib.pyplot as plt

Umgebungseinstellung

Stellen Sie die Umgebung nach Ihren Wünschen ein.

#Grafiken usw. werden in Notebook gezeichnet
%matplotlib inline

#Die maximale Zeichenfolgenlänge von DataFrame-Spalten wurde von 50 auf 150 geändert
pd.set_option("display.max_colwidth", 150)

Laden von Zugriffsprotokollen

Informationen zum Laden des Zugriffsprotokolls habe ich auf diesen Blogeintrag verwiesen. Definieren Sie vor dem Laden des Zugriffsprotokolls die erforderlichen Typanalysefunktionen.

from datetime import datetime
import pytz

def parse_str(x):
    return x[1:-1]

def parse_datetime(x):
    dt = datetime.strptime(x[1:-7], '%d/%b/%Y:%H:%M:%S')
    dt_tz = int(x[-6:-3])*60+int(x[-3:-1])
    return dt.replace(tzinfo=pytz.FixedOffset(dt_tz))

Laden Sie dann das Zugriffsprotokoll mit "pd.read_csv ()". Das Ausgabeformat des Zugriffsprotokolls war etwas anders, daher habe ich es wie folgt geändert. Dieses Mal werden der Einfachheit halber nur einige Spalten extrahiert.

df = pd.read_csv(
    '/var/log/httpd/access_log',
    sep=r'\s(?=(?:[^"]*"[^"]*")*[^"]*$)(?![^\[]*\])',
    engine='python',
    na_values='-',
    header=None,
    usecols=[0, 4, 6, 7, 8, 9, 10],
    names=['ip', 'time', 'response_time', 'request', 'status', 'size', 'user_agent'],
    converters={'time': parse_datetime,
                'response_time': int,
                'request': parse_str,
                'status': int,
                'size': int,
                'user_agent': parse_str})

Zugriffsprotokoll analysieren

Lassen Sie uns das Zugriffsprotokoll nach dem Laden analysieren.

Bestätigen Sie ohne Daten zu verarbeiten

Überprüfen Sie zuerst die ersten und letzten 5 Zeilen der Datei.

df

Screenshot from 2020-03-24 16-47-29.png

Sie können sehen, dass es innerhalb von 1 Stunde und 20 Minuten ab 18.20 Uhr am 24. März 114.004 Anfragen gab.

Bestätigung der Gesamtantwortzeit

Lassen Sie uns die durchschnittliche und maximale Antwortzeit anzeigen.

print('Gesamtergebnis der Antwortzeit (Mikrosekunden)\n')
print('Mindestwert: {}'.format(str(df['response_time'].min()).rjust(10)))
print('Durchschnittswert: {}'.format(str(round(df['response_time'].mean())).rjust(10)))
print('Median: {}'.format(str(round(df['response_time'].median())).rjust(10)))
print('Maximalwert: {}'.format(str(df['response_time'].max()).rjust(10)))

print('\n Schlechteste Reaktionszeit 15')
df.sort_values('response_time', ascending=False).head(15)

Screenshot from 2020-03-24 14-03-41.png

Die 15 schlechtesten Antwortzeiten waren alle Anfragen an den OpenAM-Authentifizierungsdienst, und es dauerte ungefähr 15 Sekunden, um eine Antwort mit dem HTTP-Status 406 zurückzugeben. Sie können sehen, dass ein Problem mit der Anwendung aufgetreten ist, die mit OpenAM funktioniert.

Bestätigung fehlender Werte

Bei den schlechtesten 15 Antwortzeiten sind "Größe" (Antwortgröße) und "Benutzeragent" (Benutzeragent) "NaN". Lassen Sie uns überprüfen, wie viele Werte fehlen.

df.isnull().sum()

Screenshot from 2020-03-24 17-00-58.png

Wenn "Größe" "NaN" ist, handelt es sich um eine Umleitung. Außerdem sind ungefähr 30% von "user_agent" unbekannt. Da nicht nur der Browser des Endbenutzers auf OpenAM zugreift, haben wahrscheinlich viele von ihnen keinen "User-Agent" -Header. Lassen Sie uns herausfinden, welche anderen Benutzeragenten verfügbar sind.

Bestätigung des Benutzeragenten

ua_df = DataFrame(df.groupby(['user_agent']).size().index)
ua_df['count'] = df.groupby(['user_agent']).size().values
ua_df

Screenshot from 2020-03-24 17-15-03.png

Es gab 490 Typen. Dies scheint das Ergebnis zu sein, da nicht nur die Endbenutzer, sondern auch viele Anwendungen zusammenarbeiten.

Visualisieren Sie Zugriffsprotokolle

Ich habe es bisher überhaupt nicht visualisiert, daher denke ich, dass es nicht interessant war. Lassen Sie es uns als Kreisdiagramm anzeigen. Zeichnen wir zunächst den Prozentsatz des Antwortstatuscodes in ein Kreisdiagramm.

plt.figure(figsize = (5, 5))
labels = ['2xx', '3xx', '4xx', '5xx']
plt.pie(df.groupby([df['status'] // 100]).size(), autopct = '%1.1f%%', labels = labels, shadow = True, startangle = 90)
plt.axis('equal')
df.groupby([df['status'] // 100]).size()
plt.show()

Screenshot from 2020-03-24 17-29-01.png

Hmmm, die Statuscode-Beschriftungen mit niedrigem Prozentsatz überlappen sich und sehen nicht gut aus ...

Definieren Sie Dienstprogrammfunktionen für die Visualisierung

Definieren wir also eine Funktion, die eine kleine Anzahl von Elementen (1% oder weniger) zu "anderen" kombiniert. Wenn Sie die Funktion von Matplotlib verwenden, benötigen Sie möglicherweise keine solche Funktion, aber ich konnte sie nicht finden, also habe ich eine einfache erstellt.

#Für DataFrame
def replace_df_minors_with_others(df_before, column_name):
    elm_num = 1
    for index, row in df_before.sort_values([column_name], ascending=False).iterrows():
        if (row[column_name] / df_before[column_name].sum()) > 0.01:
            elm_num = elm_num + 1
    
    df_after = df_before.sort_values([column_name], ascending=False).nlargest(elm_num, columns=column_name)
    df_after.loc[len(df_after)] = ['others', df_before.drop(df_after.index)[column_name].sum()]
    return df_after

#Für Wörterbücher
def replace_dict_minors_with_others(dict_before):
    dict_after = {}
    others = 0
    total = sum(dict_before.values())
    for key in dict_before.keys():
        if (dict_before.get(key) / total) > 0.01:
            dict_after[key] = dict_before.get(key)
        else:
            others = others + dict_before.get(key)
    dict_after = {k: v for k, v in sorted(dict_after.items(), reverse=True, key=lambda item: item[1])}
    dict_after['others'] = others
    return dict_after

Visualisierung des Benutzeragenten

Verwenden Sie diese Funktion nun, um die Benutzeragententypen in einem Kreisdiagramm anzuzeigen.

plt.figure(figsize = (15, 10))
ua_df_with_others = replace_df_minors_with_others(ua_df, 'count')
plt.pie(ua_df_with_others['count'], labels = ua_df_with_others['user_agent'], autopct = '%1.1f%%', shadow = True, startangle = 90)
plt.title('User Agent')
plt.show()

index.png

Selbst wenn Sie den Header "User-Agent" direkt anzeigen, ist dies schwer zu verstehen. Was ist "Mozilla / 5.0 (Windows NT 6.1; WOW64; Trident / 7.0; rv: 11.0) wie Gecko" ...

Konvertieren wir es also mithilfe einer Bibliothek namens Woothee in eine leicht verständliche Anzeige. Installieren Sie mit dem folgenden Befehl.

$ pip install woothee

Verwenden Sie diese Option, um das Client-Betriebssystem und den Benutzeragenten in einem Kreisdiagramm anzuzeigen.

import woothee

ua_counter = {}
os_counter = {}

for index, row in ua_df.sort_values(['count'], ascending=False).iterrows():
    ua = woothee.parse(row['user_agent'])
    uaKey = ua.get('name') + ' (' + ua.get('version') + ')'
    if not uaKey in ua_counter:
        ua_counter[uaKey] = 0
    ua_counter[uaKey] = ua_counter[uaKey] + 1
    osKey = ua.get('os') + ' (' + ua.get('os_version') + ')'
    if not osKey in os_counter:
        os_counter[osKey] = 0
    os_counter[osKey] = os_counter[osKey] + 1

plt.figure(figsize = (15, 10))
plt.subplot(1,2,1)
plt.title('Client OS')
os_counter_with_others = replace_dict_minors_with_others(os_counter)
plt.pie(os_counter_with_others.values(), labels = os_counter_with_others.keys(), autopct = '%1.1f%%', shadow = True, startangle = 90)

plt.subplot(1,2,2)
plt.title('User Agent')
ua_counter_with_others = replace_dict_minors_with_others(ua_counter)
plt.pie(ua_counter_with_others.values(), labels = ua_counter_with_others.keys(), autopct = '%1.1f%%', shadow = True, startangle = 90)
plt.show()

Screenshot from 2020-03-24 17-46-52.png

"UNBEKANNT" hat zugenommen, aber ist es leichter zu verstehen? Trotzdem verwenden viele Leute immer noch IE unter Windows ...

Visualisierung des Fehlerantwortstatuscodes

Schauen wir uns als nächstes den Prozentsatz der Statuscodes (400 oder mehr) der Antwort an, die einen Fehler verursacht hat.

error_df = df[df['status'] >= 400]
plt.figure(figsize = (10, 10))
labels = ['4xx', '5xx']
plt.pie(error_df.groupby([error_df['status'] // 100]).count().time, labels=labels, counterclock=False, startangle=90)

labels2 = ['400', '401', '403', '404', '406', '500', '501', '502']
plt.pie(error_df.groupby(['status']).count().time, labels=labels2, counterclock=False, startangle=90, radius=0.7)

centre_circle = plt.Circle((0,0),0.4, fc='white')
fig = plt.gcf()
fig.gca().add_artist(centre_circle)
plt.title('Error Status Code')
plt.show()

Screenshot from 2020-03-24 17-55-55.png

Das Merkmal dieses Zugriffsprotokolls ist, dass es viele Fehlerantworten mit dem Statuscode 406 gibt, der im Allgemeinen unbekannt ist.

Visualisierung des Ladestatus

Lassen Sie uns nun ein anderes Diagramm ausgeben. Überprüfen Sie zunächst den Ladestatus.

plt.figure(figsize = (15, 5))
access = df['request']
access.index = df['time']
access = access.resample('S').count()
access.index.name = 'Time'
access.plot()
plt.title('Total Access')
plt.ylabel('Access')
plt.show()

Screenshot from 2020-03-24 18-04-18.png

Es gibt ständigen Zugriff, manchmal über 100 pro Sekunde.

Visualisierung der Beziehung zwischen Antwortgröße und Zeit

Mal sehen, ob es einen Zusammenhang zwischen der Größe der Antwort und der Zeit gibt.

plt.figure(figsize = (15, 5))
plt.title('size v.s. response_time')
plt.scatter(df['size']/1000, df['response_time']/1000000, marker='.')
plt.xlabel('Size(KB)')
plt.ylabel('Response Time')
plt.grid()

Screenshot from 2020-03-24 18-09-32.png

Obwohl es keine klare Beziehung gibt, kann nicht gesagt werden, dass die Größe der Antwort umso kürzer ist, je größer die Antwort ist, wenn die Größe nicht 0 ist.

Schließlich

Ich denke, Pandas und Matplotlib können verwendet werden, um Zugriffsprotokolle zu analysieren. Wenn Sie diese Bibliotheken beherrschen, ist dies hilfreich für die Fehlerbehebung.

Ich bin noch im Prototypenstadium, aber ich bin auch [GitHub] verpflichtet (https://github.com/k-tamura/access-log-analyzer).

Recommended Posts

Analysieren Sie Apache-Zugriffsprotokolle mit Pandas und Matplotlib
Lesen Sie CSV und analysieren Sie mit Pandas und Seaborn
Implementieren Sie "Data Visualization Design # 3" mit Pandas und Matplotlib
Führen Sie Apache-Zugriffsprotokolle zusammen
Vielseitige Datenerfassung mit Pandas + Matplotlib
Ich möchte Protokolle mit Python analysieren
CentOS 6.4, Python 2.7.3, Apache, mod_wsgi, Django
Zeichnen Sie eine hierarchische Achsenbeschriftung mit matplotlib + pandas
Zeichnen Sie Dreiecksfunktionen mit Numpy und Matplotlib
Laden Sie csv mit Pandas und spielen Sie mit Index
Python-Lernnotiz für maschinelles Lernen von Chainer Kapitel 11 und 12 Einführung in Pandas Matplotlib
Pandas-Grundlagen für Anfänger ③ Erstellen Sie ein Histogramm mit matplotlib
Installieren Sie Pip und Pandas mit Ubuntu oder VScode
Visualisieren Sie Daten interaktiv mit TreasureData, Pandas und Jupyter.
Animation mit matplotlib
Japanisch mit Matplotlib
Animation mit matplotlib
Histogramm mit Matplotlib
Erstellen Sie eine Animation mit matplotlib
Aggregieren Sie Git-Protokolle mit Git Python und analysieren Sie Assoziationen mit Orange
Lesen und analysieren Sie den Datensatz im Arff-Format mit python scipy.io
Zugriff mit dem Cache beim Lesen von_json mit Pandas
So extrahieren Sie Nullwerte und Nicht-Nullwerte mit Pandas
[Linux] [Python] [Pandas] Laden Sie die Microsoft Access-Datenbank (* .mdb) mit Pandas
Untersuchen Sie den Java- und Python-Datenaustausch mit Apache Arrow
Extrahieren Sie den Maximalwert mit Pandas und ändern Sie diesen Wert
[Linux] Erstellen Sie ein Selbstzertifikat mit Docker und Apache
Animieren Sie die Alpha- und Beta-Werte der weltweit besten Marktwertaktien mit Pandas + Matplotlib