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.
Importieren Sie zunächst die minimal erforderlichen Pandas und Matplotlib.
import pandas as pd
from pandas import DataFrame, Series
import matplotlib.pyplot as plt
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)
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})
Lassen Sie uns das Zugriffsprotokoll nach dem Laden analysieren.
Überprüfen Sie zuerst die ersten und letzten 5 Zeilen der Datei.
df
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.
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)
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.
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()
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.
ua_df = DataFrame(df.groupby(['user_agent']).size().index)
ua_df['count'] = df.groupby(['user_agent']).size().values
ua_df
Es gab 490 Typen. Dies scheint das Ergebnis zu sein, da nicht nur die Endbenutzer, sondern auch viele Anwendungen zusammenarbeiten.
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()
Hmmm, die Statuscode-Beschriftungen mit niedrigem Prozentsatz überlappen sich und sehen nicht gut aus ...
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
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()
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()
"UNBEKANNT" hat zugenommen, aber ist es leichter zu verstehen? Trotzdem verwenden viele Leute immer noch IE unter Windows ...
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()
Das Merkmal dieses Zugriffsprotokolls ist, dass es viele Fehlerantworten mit dem Statuscode 406 gibt, der im Allgemeinen unbekannt ist.
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()
Es gibt ständigen Zugriff, manchmal über 100 pro Sekunde.
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()
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.
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