2012-07-27 20 views
16

Tôi có một tệp truy cập Apache.log, có dung lượng khoảng 35GB .. Xem qua nó không phải là một tùy chọn nữa mà không phải chờ đợi nhiều.Split access.log file theo ngày sử dụng các công cụ dòng lệnh

Tôi muốn chia nhỏ trong nhiều tệp nhỏ, bằng cách sử dụng ngày làm tiêu chí chia tách.

Ngày có định dạng "[15/Oct/2011: 12: 02: 02 +0000]". Bất kỳ ý tưởng làm thế nào tôi có thể làm điều đó bằng cách sử dụng chỉ bash scripting, chương trình thao tác văn bản tiêu chuẩn (grep, awk, sed, và thích), đường ống và chuyển hướng?

Tên tệp đầu vào là access.log. Tôi muốn tập tin đầu ra có định dạng như access.apache.15_Oct_2011.log (mà sẽ làm các trick, mặc dù không thoải mái khi sắp xếp ..)

Trả lời

16

Một cách sử dụng 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 

chí này tập tin đầu ra như:

incendiary.ws-2010-2010_04.txt 
incendiary.ws-2010-2010_05.txt 
incendiary.ws-2010-2010_06.txt 
incendiary.ws-2010-2010_07.txt 

Against một log file 150 MB, câu trả lời bằng cách chepner mất 70 giây trên một 3,4 GHz 8 Core Xeon E31270, trong khi phương pháp này mất 5 giây.

gốc cảm hứng: "How to split existing apache logfile by month?"

+0

Bạn nói đúng, thưa ngài. Tôi vừa thử nghiệm giải pháp perl, và giải pháp awk nhanh hơn gấp 3 lần. Tôi nghi ngờ nó đã làm với thực tế là ví dụ awk không sử dụng biểu thức thông thường nhưng tách chuỗi đơn giản, mà có thể hiệu quả hơn. Đánh dấu là câu trả lời được chấp nhận. –

+0

Tôi vừa cập nhật nó để có đầu ra tên tệp tốt hơn. –

+0

Ồ, và tôi chắc chắn đang sử dụng tính năng này để sản xuất với 20 GB tệp mà không gặp sự cố nào. Mất khoảng 2 GB/phút trên hệ thống của tôi. –

4

Perl đã đến cứu hộ:

cat access.log | perl -n -e'[email protected]\[(\d{1,2})/(\w{3})/(\d{4}):@; open(LOG, ">>access.apache.$3_$2_$1.log"); print LOG $_;' 

Vâng, nó không chính xác là "tiêu chuẩn" thao tác chương trình, nhưng nó được thực hiện cho thao tác văn bản tuy nhiên.

Tôi cũng đã thay đổi thứ tự các đối số trong tên tệp, do đó các tệp được đặt tên như access.apache.yyyy_mon_dd.log để sắp xếp dễ dàng hơn.

+0

Điều này làm việc cho tôi nơi câu trả lời được đánh dấu không và có thể chấp nhận được. – cmenning

1

Loại xấu xí, đó là bash cho bạn:

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 
+0

rất thông minh, cảm ơn;) điều này sẽ làm việc tuyệt vời cho tập tin nhỏ (filesize ít hơn số lượng ram), vì nó vòng qua toàn bộ tập tin khoảng 1.116 lần :) –

+2

rất đúng, nó không phải là một kịch bản hiệu quả. nó sẽ là tốt cho việc sử dụng thường xuyên. Cảm ơn! – ncultra

+1

sẽ nhanh hơn để bỏ vòng lặp ngoài và xử lý tệp trong hai lần truyền - lần đầu tiên vượt qua chia tệp thành các mục theo năm. Thẻ thứ hai sau đó sẽ xử lý tệp mỗi năm và chia các mục theo ngày. Nó thậm chí có thể nhanh hơn để bỏ vòng lặp thứ hai và xử lý tệp trong ba lần truyền. – ncultra

10

tinh khiết bash, đã có một đường chuyền thông qua bản ghi truy cập:

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 
+3

Phương pháp trong câu trả lời của tôi nhanh hơn đáng kể: Đối với tệp nhật ký 150 MB, câu trả lời này mất ** 70 giây ** trên 3,4 GHz 8 Core Xeon E31270, trong khi phương pháp trong tôi mất ** 5 giây **. –

+0

Không ngạc nhiên :) – chepner

+0

Tuy nhiên, câu trả lời này tạo các tệp nhật ký trên cơ sở hàng ngày chứ không phải trên cơ sở hàng tháng. Điều này không ít, không có thắc mắc nó là nhanh hơn. –

4

Dưới đây là một phiên bản awk rằng kết quả đầu ra file log lexically sắp xếp.

Một số cải tiến hiệu quả: tất cả được thực hiện trong một lần, chỉ tạo ra fname khi không giống như trước, đóng fname khi chuyển sang tệp mới (nếu không bạn có thể hết bộ mô tả tệp).

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 
0

tôi đã thực hiện một sự cải thiện nhẹ vào câu trả lời của Theodore vì vậy tôi có thể nhìn thấy sự tiến bộ khi xử lý một rất log file lớn.

#!/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" 
} 

Ngoài ra tôi thấy rằng tôi cần thiết để sử dụng gawk (brew install gawk nếu bạn không có nó) để làm việc này trên Mac OS X.

1

tôi kết hợp các giải pháp Theodore và Thor sử dụng cải thiện hiệu quả của Thor và các tệp hàng ngày, nhưng vẫn giữ lại hỗ trợ ban đầu cho địa chỉ IPv6 trong tệp định dạng kết hợp.

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 
}' 
+0

Điều này thực sự ấn tượng! Cảm ơn bạn –

Các vấn đề liên quan