bash - script - see logs linux
Dividir el archivo access.log por fechas usando herramientas de lĂnea de comandos (7)
Tengo un archivo access.log de Apache, que tiene un tamaño de alrededor de 35 GB. Abordar a través de él ya no es una opción, sin esperar mucho.
Quería dividirlo en muchos archivos pequeños, utilizando la fecha como criterios de división.
La fecha está en formato [15/Oct/2011:12:02:02 +0000]
. ¿Alguna idea de cómo podría hacerlo utilizando solo scripts bash, programas de manipulación de texto estándar (grep, awk, sed y likes), canalizaciones y redirección?
El nombre del archivo de entrada es access.log
. Me gustaría que los archivos de salida tuvieran un formato como access.apache.15_Oct_2011.log
(eso haría el truco, aunque no está bien cuando se ordena).
Aquí hay una versión de awk
que genera archivos de registro de orden léxico.
Algunas mejoras de eficiencia: todo hecho en una sola pasada, solo genera fname
cuando no es el mismo que antes, cierre fname
al cambiar a un nuevo archivo (de lo contrario, podría quedarse sin descriptores de archivos).
awk -F"[]/:[]" ''
BEGIN {
m2n["Jan"] = 1; m2n["Feb"] = 2; m2n["Mar"] = 3; m2n["Apr"] = 4;
m2n["May"] = 5; m2n["Jun"] = 6; m2n["Jul"] = 7; m2n["Aug"] = 8;
m2n["Sep"] = 9; m2n["Oct"] = 10; m2n["Nov"] = 11; m2n["Dec"] = 12;
}
{
if($4 != pyear || $3 != pmonth || $2 != pday) {
pyear = $4
pmonth = $3
pday = $2
if(fname != "")
close(fname)
fname = sprintf("access_%04d_%02d_%02d.log", $4, m2n[$3], $2)
}
print > fname
}'' access-log
Bash puro, haciendo un paso a través del registro de acceso:
while read; do
[[ $REPLY =~ /[(..)/(...)/(....): ]]
d=${BASH_REMATCH[1]}
m=${BASH_REMATCH[2]}
y=${BASH_REMATCH[3]}
#printf -v fname "access.apache.%s_%s_%s.log" ${BASH_REMATCH[@]:1:3}
printf -v fname "access.apache.%s_%s_%s.log" $y $m $d
echo "$REPLY" >> $fname
done < access.log
Combiné las soluciones de Theodore y Thor para usar la mejora de eficiencia de Thor y los archivos diarios, pero conservé el soporte original para direcciones IPv6 en un archivo de formato combinado.
awk ''
BEGIN {
m2n["Jan"] = 1; m2n["Feb"] = 2; m2n["Mar"] = 3; m2n["Apr"] = 4;
m2n["May"] = 5; m2n["Jun"] = 6; m2n["Jul"] = 7; m2n["Aug"] = 8;
m2n["Sep"] = 9; m2n["Oct"] = 10; m2n["Nov"] = 11; m2n["Dec"] = 12;
}
{
split($4, a, "[]/:[]")
if(a[4] != pyear || a[3] != pmonth || a[2] != pday) {
pyear = a[4]
pmonth = a[3]
pday = a[2]
if(fname != "")
close(fname)
fname = sprintf("access_%04d-%02d-%02d.log", a[4], m2n[a[3]], a[2])
}
print >> fname
}''
Hice una leve mejora en la respuesta de Theodore para poder ver el progreso al procesar un archivo de registro muy grande.
#!/usr/bin/awk -f
BEGIN {
split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ", months, " ")
for (a = 1; a <= 12; a++)
m[months[a]] = a
}
{
split($4, array, "[:/]")
year = array[3]
month = sprintf("%02d", m[array[2]])
current = year "-" month
if (last != current)
print current
last = current
print >> FILENAME "-" year "-" month ".txt"
}
También encontré que necesitaba usar gawk
( brew install gawk
si no lo tienes) para que esto funcione en Mac OS X.
Perl vino al rescate:
cat access.log | perl -n -e''m@/[(/d{1,2})/(/w{3})/(/d{4}):@; open(LOG, ">>access.apache.$3_$2_$1.log"); print LOG $_;''
Bueno, no es exactamente un programa de manipulación "estándar", pero está hecho para la manipulación de texto.
También he cambiado el orden de los argumentos en el nombre del archivo, para que los archivos se nombren como access.apache.yyyy_mon_dd.log para facilitar la clasificación.
Un poco feo, eso es bash para ti:
for year in 2010 2011 2012; do
for month in jan feb mar apr may jun jul aug sep oct nov dec; do
for day in 1 2 3 4 5 6 7 8 9 10 ... 31 ; do
cat access.log | grep -i $day/$month/$year > $day-$month-$year.log
done
done
done
Una forma de utilizar awk
:
awk ''BEGIN {
split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ", months, " ")
for (a = 1; a <= 12; a++)
m[months[a]] = a
}
{
split($4,array,"[:/]");
year = array[3]
month = sprintf("%02d", m[array[2]])
print > FILENAME"-"year"_"month".txt"
}'' incendiary.ws-2009
Esto dará salida a archivos como:
incendiary.ws-2010-2010_04.txt
incendiary.ws-2010-2010_05.txt
incendiary.ws-2010-2010_06.txt
incendiary.ws-2010-2010_07.txt
Contra un archivo de registro de 150 MB, la respuesta de Chepner tomó 70 segundos en un Core Xeon E31270 de 3.4 GHz 8, mientras que este método tomó 5 segundos.
Inspiración original: " ¿Cómo dividir el archivo de registro de apache existente por mes? "