Abrufen spezifischer Informationen aus einer Protokolldatei

Abrufen spezifischer Informationen aus einer Protokolldatei

Ich möchte einige Teile aus meiner Protokolldatei abrufen. Ich habe versucht, den Anforderungsteil auszuschneiden, um Benutzer, Modul, Aktion, doAjax und ajaxAction abzurufen.

Ich habe zum Beispiel

195.xx.x.x - - [25/Apr/2017:09:60:xx +0200] "POST /userx/index.php?m=contacts&a=form&...
192.xx.x.x - - [25/Apr/2017:09:45:xx +0200] "POST /usery/index.php?m=customer&doajax=request&action=getContacts...
197.xx.x.x - - [25/Apr/2017:09:20:xx +0200] "GET /userx/index.php?m=meeting&doajax=date&id=3

und ich möchte haben:

[user]|[module]|[action]|[doAjax]|[ajaxAction] 
usery  contacts  form     null     null
userx  customer  null     request  getContacts
userz  meeting   null     date     null

Wo:

userx --> user  
m=xxx -->module   
a=xxx -->action  
doajax=xxx-->doAjax   
action=xxx-->ajaxAction  

Ich habe versucht, „set“ zu verwenden awk, aber um nur die 7. Spalte auszuschneiden, in der ich meine Anfrage mit diesem Befehl finden kann:

awk '{printf $7; next ; }' logfile

Wie kann ich also vorgehen, um Benutzer, Modul, Aktion, doAjax und AjaxAction zu extrahieren, um anschließend nur meine Anfrage auszudrucken?

Antwort1

Ein Perl-Einzeiler:

$ perl -lne '
BEGIN{
    printf "%-10s%-10s%-10s%-10s%-15s\n", qw([user] [module] [action] [doAjax] [ajaxAction]);
} 
$usr = $mde = $act = $doAj = $ajAc = "null"; 
$usr=$1 if m|\s/([^/]+)/|; 
$mde=$1 if /m=(.+?)(&|$)/; 
$act=$1 if /a=(.+?)(&|$)/; 
$doAj=$1 if /doajax=(.+?)(&|$)/; 
$ajAc=$1 if /action=(.+?)(&|$)/; 
printf "%-10s%-10s%-10s%-10s%-15s\n", ($usr,$mde,$act,$doAj,$ajAc)' file 
[user]    [module]  [action]  [doAjax]  [ajaxAction]   
userx     contacts  form      null      null           
usery     customer  null      request   getContacts    
userx     meeting   null      date      null           

Der grundlegende Trick besteht darin, nach jedem String zu suchen, der Ihre URL-Teile identifiziert, und, falls gefunden, die entsprechende Variable darauf zu setzen. In jedem Fall suchen wir nach dem Bezeichner, gefolgt von einem =(zB m=) und dann entweder einem &oder dem Zeilenende (&|$). Da der übereinstimmende Teil in Klammern gesetzt ist (zB m=(.+?)), können wir ihn dann als referenzieren $2und das ist, was in jeder Variable gespeichert wird.

Wenn Sie es unbedingt als Trennzeichen benötigen |und es Ihnen nichts ausmacht, dass die Ausgabe dadurch schlechter lesbar wird, können Sie stattdessen Folgendes verwenden:

$ perl -lne '
BEGIN{
    printf "%s|%s|%s|%s|%s\n", qw([user] [module] [action] [doAjax] [ajaxAction]);
} 
$usr = $mde = $act = $doAj = $ajAc = "null"; 
$usr=$1 if m|\s/([^/]+)/|; 
$mde=$1 if /m=(.+?)(&|$)/; 
$act=$1 if /a=(.+?)(&|$)/; 
$doAj=$1 if /doajax=(.+?)(&|$)/; 
$ajAc=$1 if /action=(.+?)(&|$)/; 
print join "|", ($usr,$mde,$act,$doAj,$ajAc)' file 
[user]|[module]|[action]|[doAjax]|[ajaxAction]
userx|contacts|form|null|null
usery|customer|null|request|getContacts
userx|meeting|null|date|null

Ein besserer Ansatz (mit besser lesbarer Ausgabe) wäre printfstattdessen die Verwendung von:

Antwort2

Wenn Sie dies lieber in awk tun möchten, können Sie wie folgt vorgehen: Mit Split können Sie einen String mit einem beliebigen Feldtrennzeichen teilen.

awk  '{split($7,a,"/"); split(a[3],b,"m="); split(b[2],c,"&"); split(c[2],d,"="); print a[2], c[1], d[1], d[2] }' logfile

Dadurch werden die gewünschten Spalten generiert.

userx contacts a form
usery customer doajax request
userx meeting doajax date

Der verbleibende Schritt ist das Formatieren. Arrays in awk sind assoziativ und können mit Strings indiziert werden - sieheHier. Sie können Folgendes tun; hier wird op (kurz für output) auf null initialisiert. Dann setzen wir op[d[1]]=d[2].

awk  '{split($7,a,"/"); split(a[3],b,"m="); split(b[2],c,"&"); split(c[2],d,"="); op["a"]="null"; op["doajax"]="null"; op["ajaxaction"]="null"; op[d[1]]=d[2];print a[2], c[1], op["a"], op["doajax"], op["ajaxaction"] }' junk.txt 

[geändert zu]

awk  '{split($7,a,"/"); split(a[3],b,"m="); split(b[2],c,"&"); split(c[2],d,"="); op["a"]="null"; op["doajax"]="null"; op["action"]="null"; op[d[1]]=d[2]; split(c[3],f,"="); split(f[2],g,"."); op[f[1]]=g[1]; print a[2], c[1], op["a"], op["doajax"], op["action"] }' junk.txt 

Die Ausgabe ist wie folgt

userx contacts form null null
usery customer null request getContacts
userx meeting null date null

Antwort3

perl -lane '
BEGIN {
   print $H = join "|", map { s/.*/[$&]/r } @H = qw/user module action doAjax ajaxAction/;
   pos($H) = 0;
   push(@pos, pos($H)-$p), $p=pos($H) while $H =~ /\[/g;
   $fmt = join "", map { "\%-${_}s" } @pos[1..$#pos], length($H)-$p;
}

   my(%h, %H) = $F[-1] =~ /[?&]\K([^=]+)=([^&]+)/g;
   @H{@H} = ($F[-1] =~ m|^/([^/]+)|, @h{qw/m a doajax action/});
   print sprintf $fmt, map { $H{$_} // "null" } @H;
' logfile

Ergebnisse

[user]|[module]|[action]|[doAjax]|[ajaxAction]
userx  contacts form     null     null
usery  customer null     request  getContacts
userx  meeting  null     date     null

Erläuterung

  1. Perl-Optionen:

    -l machtORS = RS = \n

    -aspeichert Felder in einem Array @F, das durch Aufteilen des aktuellen Datensatzes erhalten wurde /\s+/, also z. B.$F[0] => $1, $F[1] => $2, ..., $F[-1] => $NF

    -n richtet eine implizite Schleife ein, die die Eingabedatei zeilenweise liest UND keine Ausgabe erzeugt, sofern diese nicht angefordert wird.

  2. BEGIN-Block:

    Zuerst drucken wir den Header. Dann bestimmen wir das Format dynamisch basierend auf dem Header. Für jede gelesene Zeile richten wir einen Hash %h ein, dessen Schlüssel die Zeichenfolgen vor = und deren Werte die Zeichenfolgen nach = sind. Die zu betrachtende Zeichenfolge wird links von ? oder & und rechts von & begrenzt. Als Nächstes richten wir einen weiteren Hash %H ein, dessen Schlüssel umbenannte Versionen des %h-Hashes sind. Als Nächstes drucken wir den Hash basierend auf dem Format, das wir im BEGIN-Block berechnet haben.

verwandte Informationen