[완료] Perl을 이용하여 대용량의 로그파일을 분석하는 스크립트 작성하고자 합니다.

mandugukbap의 이미지

대용량의 로그파일을 읽어 들여서 분석을 하는 스크립트를 펄로 만들고 있습니다.

로그파일은 다음과 같은 포맷을 가지고 있다고 가정해 보겠습니다.

필드1               필드2
 
aaaaaaaaaaaaa       ADDF0909
aaaaaaaaaaaab       AABBCC
aaaaaaaaaaaac       13DA
aaaaaaaaaaaad       AACFDEEFFE
aaaaaaaaaaaac       10FF
aaaaaaaaaaaab       FF00
aaaaaaaaaaaaa       FFFFFF
aaaaaaaaaaaaa       CDEDDFFF
aaaaaaaaaaaab       0998DDFCCF
aaaaaaaaaaaae       0C0CCCDDFFAAAB
aaaaaaaaaaaab       ACACDFFFDDCCEDFDDDDDFE
aaaaaaaaaaaaa       1319DDEDFEABDEFFE1EF
.
.
.
.

대략 이런 구조의 로그파일이고 몇만에서 몇십만 라인의 로그를 분석하게 됩니다.
이 로그에서 필드1을 기준으로한 필드2의 각 비트별 1로 세트된 갯수 (예, 필드1의 값으로 aaaaaaa를 가지는 라인들의 필드2들을 루프를 돌려 각 비트 인덱스별로 1로 세트된 비트들의 총합)를 구하려 합니다. 필드1은 대충 예를 들었지만 MD5해쉬 입니다.

즉, 결과는 아래와 같은 모양이어야 할겁니다.

aaaaaaaaaaaaa:0 2099382
aaaaaaaaaaaaa:1 1888111
aaaaaaaaaaaaa:2 1128111
aaaaaaaaaaaaa:3 3399001
aaaaaaaaaaaaa:4 4444133
aaaaaaaaaaaaa:5 3888112
.....(중략)
aaaaaaaaaaaaa:131 210101
aaaaaaaaaaaab:0 140
aaaaaaaaaaaab:1 211
aaaaaaaaaaaab:2 201
.....(중략)
aaaaaaaaaaaab:1710 3010
aaaaaaaaaaaac:0 10
aaaaaaaaaaaac:1 12
aaaaaaaaaaaac:2 14
aaaaaaaaaaaac:3 19
.....(하략)

첫번째 필드는 (해쉬:로그파일 필드2의 각 비트)의 조합이고 두 번째 필드는 그 비트가 1로 세팅된 횟수입니다.

아시다시피 대용량의 파일의 값을 메모리에 누적하는 것은 거의 불가능하기 때문에 필드1을 기준으로 정렬한 후 라인단위로 읽어들여 같은 해쉬끼리 값을 누적해서 출력파일에 쓰는 방법을 써야 할 것으로 알고 있습니다.

그래서 펄 코드로 다음과 같은 코드를 생각중입니다.

  20 my $tmpTrans = "out.tmp";
  21 my $inputFile = shift or die "Usage: eval2.pl filename" . CRLF;
  22 `cat $inputFile | sort -k 1 -o $tmpTrans`;
  23 open(LOGFILE, "<$tmpTrans") or die "Cannot open $tmpTrans." . CRLF;
  24     
 
     my @buff; #<- 이 녀석이 문제
 
  25 while (my $line = <LOGFILE>)
  26 {   
  27     my ($hash, $hex) = split(" ", $line);
 
         # $hex의 비트 길이가 일정하지 않음.
 
  28 }

세트된 비트의 값을 누적하기 위한 버퍼가 while문 밖에 있어야 할 듯 한데, 위의 로그파일에서 보시다시피 필드2의 비트길이가 일정하지 않습니다. 뿐만 아니라 비트길이가 일정하지 않기 때문에 while문 밖에 @buff를 생성할 경우 행여 $hex값이 무한히 길어질 경우 결국 오버플로우가 발생할 것 같아 걱정입니다.

설명이 잘 되었는지 모르겠습니다. 대용량의 로그파일을 분석하시는 분들은 자주 접하는 문제이실 거라고 생각됩니다. 부디 위의 코드를 이용(수정)해서 힌트를 좀 주시면 감사 드리겠습니다.

terzeron의 이미지

Perl에서는 사실 문자열의 길이에 별로 신경을 쓰지 않습니다.
@buff에 모든 데이터를 저장하려고 하지 마시고,
이 문제는 associative array로 해결하시는 게 좋을 것 같습니다.
필드1을 키로 하고(또는 필드1 + 필드2의 sequence number)
비트 카운트를 값으로 저장하시면 됩니다.

10만에서 100만 정도는 메모리가 넉넉하기만 하면 가능합니다.
이 경우는 얼핏 보아하니 원본 파일의 크기 정도를 메모리로 사용할 겁니다.

mandugukbap의 이미지

답변 감사드립니다.

위의 예는 간순화된 예이고 로그파일은 더 많은 정보를 포함하고 있습니다.

실제로 확장성을 고려 않고 말씀 하신대로 돌릴 경우 반나절도 안되서 오버플로우가 발생합니다.

aero의 이미지

파일이 클경우 다 메모리에 올려서 할 것이냐 그냥 라인단위로 읽으면서 처리할 것이냐는 Perl에서

while (<>) {
    ....
}


foreach (<>) {
    ....
}

둘의 차이라고 할 수 있습니다. while은 한줄씩 읽어서 처리하고 foreach는 다 읽어 메모리에 올려놓은다음 처리를 시작하죠 파일이 크면 메모리가 허용하는 한 다 읽어들입니다.

일단 주어진 문제에 대해서 간단히 구현해봤습니다.

p.txt

aaaaaaaaaaaaa       FE
aaaaaaaaaaaaa       AD
aaaaaaaaaaaab       AABC
aaaaaaaaaaaac       13DA
aaaaaaaaaaaad       AA
aaaaaaaaaaaac       10
aaaaaaaaaaaab       FF00
aaaaaaaaaaaaa       FFFF
aaaaaaaaaaaaa       CDE
aaaaaaaaaaaab       09

p.pl

#!/usr/bin/env perl
use strict;
use warnings;
use Bit::Vector;
 
my %data;
 
while ( my $line = <> ) {
    chomp $line;
    my ( $field1, $field2 ) = split /\s+/, $line;
    my $vec = Bit::Vector->new_Hex( length($field2)*4 ,$field2 );
 
    foreach my $index ( 0 .. $vec->Size()-1 ) {
        $data{$field1}->[$index] += $vec->contains($index);
    }
}
 
foreach my $key ( sort keys %data ) {
    foreach my $index ( 0 .. $#{ $data{$key} } ) {
        print "$key:$index ",$data{$key}->[$index],"\n";
    }
}

실행결과
$ perl p.pl p.txt
aaaaaaaaaaaaa:0 2
aaaaaaaaaaaaa:1 3
aaaaaaaaaaaaa:2 4
aaaaaaaaaaaaa:3 4
aaaaaaaaaaaaa:4 3
aaaaaaaaaaaaa:5 3
aaaaaaaaaaaaa:6 3
aaaaaaaaaaaaa:7 4
aaaaaaaaaaaaa:8 1
aaaaaaaaaaaaa:9 1
aaaaaaaaaaaaa:10 2
aaaaaaaaaaaaa:11 2
aaaaaaaaaaaaa:12 1
aaaaaaaaaaaaa:13 1
aaaaaaaaaaaaa:14 1
aaaaaaaaaaaaa:15 1
aaaaaaaaaaaab:0 1
aaaaaaaaaaaab:1 0
aaaaaaaaaaaab:2 1
aaaaaaaaaaaab:3 2
aaaaaaaaaaaab:4 1
aaaaaaaaaaaab:5 1
aaaaaaaaaaaab:6 0
aaaaaaaaaaaab:7 1
aaaaaaaaaaaab:8 1
aaaaaaaaaaaab:9 2
aaaaaaaaaaaab:10 1
aaaaaaaaaaaab:11 2
aaaaaaaaaaaab:12 1
aaaaaaaaaaaab:13 2
aaaaaaaaaaaab:14 1
aaaaaaaaaaaab:15 2
aaaaaaaaaaaac:0 0
aaaaaaaaaaaac:1 1
aaaaaaaaaaaac:2 0
aaaaaaaaaaaac:3 1
aaaaaaaaaaaac:4 2
aaaaaaaaaaaac:5 0
aaaaaaaaaaaac:6 1
aaaaaaaaaaaac:7 1
aaaaaaaaaaaac:8 1
aaaaaaaaaaaac:9 1
aaaaaaaaaaaac:10 0
aaaaaaaaaaaac:11 0
aaaaaaaaaaaac:12 1
aaaaaaaaaaaac:13 0
aaaaaaaaaaaac:14 0
aaaaaaaaaaaac:15 0
aaaaaaaaaaaad:0 0
aaaaaaaaaaaad:1 1
aaaaaaaaaaaad:2 0
aaaaaaaaaaaad:3 1
aaaaaaaaaaaad:4 0
aaaaaaaaaaaad:5 1
aaaaaaaaaaaad:6 0
aaaaaaaaaaaad:7 1

참고로 Bit::Vector ( http://search.cpan.org/dist/Bit-Vector/ )모듈은 아무리큰 hex,dec 숫자도 문제없이 다루니 숫자가 커서 overflow나지 않을까 걱정 안하셔도 됩니다.

mandugukbap의 이미지

자세한 설명 감사 드립니다.

Perl에 Bit-Vector란 모듈이 있다는 것을 알게된 것은 뜻하지 않은 득템이네요.

그런데 예를 들어 주신 코드에서 제가 걱정 스러운 것은 while문 밖에 있는 %data 입니다.

질문 글에서 드린 말씀처럼 로그파일(들)의 크기가 천문학적입니다.

분석 한 번 시작하면 끝나기까지 3~4일 정도 걸리는 분량입니다.

분석 서버의 메모리가 32GB이긴 하지만 이런식으로 메모리에 누적을 한다면 몇 시간 지나지 않아서 오버플로우가 생겨 버릴것 같습니다.

필드2의 길이도 문제이긴 하지만 전체적인 라인수가 우선적으로 고려되어야 할 문제라서 말입니다.

우선 while문 내에서 같은 해쉬를 가지는 라인들을 구분자(|)로 묶은 다음에 다른 함수에 넘겨 다시 푼(split) 후 로깅을 하는 방식을 쓰고는 있지만 엘레강트해 보이지가 않네요.

어쨌든 답변 대단히 감사 드립니다.

aero의 이미지

%data 해시는
(
"aaaaaaaaaaaad" => [0, 1, 0, 1, 0, 1, 0, 1],
"aaaaaaaaaaaaa" => [2, 3, 4, 4, 3, 3, 3, 4, 1, 1, 2, 2, 1, 1, 1, 1],
"aaaaaaaaaaaac" => [0, 1, 0, 1, 2, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0],
"aaaaaaaaaaaab" => [1, 0, 1, 2, 1, 1, 0, 1, 1, 2, 1, 2, 1, 2, 1, 2],
)

이런형태가 될 겁니다. 생각 보다 메모리를 많이 먹으리라고는 보이지 않는데요.
그래도 걱정되신다면 일단 파일을 읽어 필드1을 파일명으로 하고 같은 필드1에
대한 필드2들을 개개의 파일에 쭉 쓰는 작업을 마친후 필드1이름으로된
개별 파일하나하나에 대해서 위 코드로 작업하시는 방법을 쓰셔도 되겠네요.

mandugukbap의 이미지

%data 에서
(
"aaaaaaaaaaaad" => [0, 1, 0, 1, 0, 1, 0, 1],
"aaaaaaaaaaaaa" => [2, 3, 4, 4, 3, 3, 3, 4, 1, 1, 2, 2, 1, 1, 1, 1],
"aaaaaaaaaaaac" => [0, 1, 0, 1, 2, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0],
"aaaaaaaaaaaab" => [1, 0, 1, 2, 1, 1, 0, 1, 1, 2, 1, 2, 1, 2, 1, 2],
)

우선 해쉬는 md5이지만 표본의 범위는 단순 추정으로 10~100만일 것 같습니다. 또한, 각 해쉬가 가지는 배열 인덱스의 범위가 0 ~ 26796 입니다(그런데 작은 단위의 표본을 통한 결과에 의하면 max값에 가까운 값들이 더 많더군요.). 각 인덱스에 누적되는 값이 int 범위이니 4byte라고 생각한다면 표본 범위 최소 10만, 인덱스 범위의 대략적인 중간값인 15000만으로 추정을 할 때 대략 5GB이군요. 이게 단순 추정치라서 약간 불안하긴 합니다만 큰 문제는 없겠네요.

감사합니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.