2012-01-18 3 views
0

좋아요,이 시나리오입니다 : "큰 이미지"페이지를 실제로 보지 않고 이미지 미리보기 이미지를 다운로드 한 횟수를 찾으려면 로그를 구문 분석해야합니다 ... 이것은 기본적으로 핫 링크입니다 "엄지"와 "전체"이미지보기의 비율에 기반한 보호 시스템PHP에서 아파치 로그를 효율적으로 파싱하기

서버가 엄지 손톱에 대한 요청에 의해 끊임없이 포격됨을 고려할 때, 가장 효율적인 해결책은 디스크에 매일 기록하는 버퍼링 된 아파치 로그를 사용하는 것처럼 보입니다 , 1Mb를 실행 한 다음 주기적으로 로그를 분석하십시오.

제 질문은 다음과 같습니다. PHP에서 아파치 로그를 구문 분석하여 데이터를 저장하는 방법은 다음과 같습니다.

  • 로그 사용 및 업데이트를 실시간으로, 그리고이 작업을 수행하는 동안 나는 그것을 읽을 수 있도록 내 PHP 스크립트를 필요로합니다
  • PHP 스크립트는 "기억"해야합니다
  • 되는 부분 최소한이어야한다
  • 메모리 소비를 두 번 같은 부분을 읽고 스큐 데이터하지 않도록 로그를 쉽게 몇 시간에 데이터의 10 기가비트에 도달 할 수 있기 때문에 그것은, 읽기, 로그인

는 PHP 로거 스크립트 60 초마다 한 번 호출되고 그 시간 동안 로그 라인을 처리 할 수 ​​있습니다.

내가 함께 몇 가지 코드를 해킹 시도했다하지만 난 데 문제 파일 크기

다음

을 "이동"로 포인터를 추적 할 수있는 방법을, 메모리의 최소 금액을 사용 찾는데 어려움이 로그의 일부입니다 :

212.180.168.244 - - [18/Jan/2012:20:06:57 +0100] "GET /t/0/11/11441/11441268.jpg HTTP/1.1" 200 3072 "-" "Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.10.229 Version/11.60" "-" 
122.53.168.123 - - [18/Jan/2012:20:06:57 +0100] "GET /t/0/11/11441/11441276.jpg HTTP/1.1" 200 3007 "-" "Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.10.229 Version/11.60" "-" 
143.22.203.211 - - [18/Jan/2012:20:06:57 +0100] "GET /t/0/11/11441/11441282.jpg HTTP/1.1" 200 4670 "-" "Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.10.229 Version/11.60" "-" 

여기에 귀하의 검토를 위해 코드를 첨부 :

<?php 
//limit for running it every minute 
error_reporting(E_ALL); 
ini_set('display_errors',1); 
set_time_limit(0); 
include(dirname(__FILE__).'/../kframework/kcore.class.php'); 
$aj = new kajaxpage; 
$aj->use_db=1; 
$aj->init(); 
$db=kdbhandler::getInstance(); 
$d=kdebug::getInstance(); 
$d->debug=TRUE; 
$d->verbose=TRUE; 

$log_file = "/var/log/nginx/access.log"; //full path to log file when run by cron 
$pid_file = dirname(__FILE__)."/../kframework/cron/cron_log.pid"; 
//$images_id = array("8308086", "7485151", "6666231", "8343336"); 

if (file_exists($pid_file)) { 
    $pid = file_get_contents($pid_file); 
    $temp = explode(" ", $pid); 
    $pid_timestamp = $temp[0]; 
    $now_timestamp = strtotime("now"); 
    //if (($now_timestamp - $pid_timestamp) < 90) return; 
    $pointer = $temp[1]; 
    if ($pointer > filesize($log_file)) $pointer = 0; 
} 
else $pointer = 0; 

$pattern = "/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})[^\[]*\[([^\]]*)\][^\"]*\"([^\"]*)\"\s([0-9]*)\s([0-9]*)(.*)/"; 
$last_time = 0; 
$lines_processed=0; 

if ($fp = fopen($log_file, "r+")) { 
    fseek($fp, $pointer); 
    while (!feof($fp)) { 
     //if ($lines_processed>100) exit; 
     $lines_processed++; 
     $log_line = trim(fgets($fp)); 
     if (!empty($log_line)) { 
      preg_match_all($pattern, $log_line, $matches); 
      //print_r($matches); 
      $size = $matches[5][0]; 
      $matches[3][0] = str_replace("GET ", "", $matches[3][0]); 
      $matches[3][0] = str_replace("HTTP/1.1", "", $matches[3][0]); 
      $matches[3][0] = str_replace(".jpg/", ".jpg", $matches[3][0]); 
      if (substr($matches[3][0],0,3) == "/t/") { 
       $get = explode("-",end(explode("/",$matches[3][0]))); 
       $imgid = $get[0]; 
       $type='thumb'; 
      } 
      elseif (substr($matches[3][0], 0, 5) == "/img/") { 
       $get1 = explode("/", $matches[3][0]); 
       $get2 = explode("-", $get1[2]); 
       $imgid = $get2[0]; 
       $type='raw'; 
      } 
      echo $matches[3][0]; 
      // put here your sql insert or update 
      $imgid=(int) $imgid; 
      if (isset($type) && $imgid!=1) { 
       switch ($type) { 
        case 'thumb': 
         //use the second slave in the registry 
         $sql=$db->slave_query("INSERT INTO hotlink SET thumbviews=1, imageid=".$imgid." ON DUPLICATE KEY UPDATE thumbviews=thumbviews+1 ",2); 
         echo "INSERT INTO hotlink SET thumbviews=1, imageid=".$imgid." ON DUPLICATE KEY UPDATE thumbviews=thumbviews+1"; 
        break; 
        case 'raw': 
         //use the second slave in the registry 
         $sql=$db->slave_query("INSERT INTO hotlink SET rawviews=1, imageid=".$imgid." ON DUPLICATE KEY UPDATE rawviews=rawviews+1",2); 
         echo "INSERT INTO hotlink SET rawviews=1, imageid=".$imgid." ON DUPLICATE KEY UPDATE rawviews=rawviews+1"; 
        break; 
       } 
      } 

      // $imgid - image ID 
      // $size - image size 

      $timestamp = strtotime("now"); 
      if (($timestamp - $last_time) > 30) { 
       file_put_contents($pid_file, $timestamp . " " . ftell($fp)); 
       $last_time = $timestamp; 
      } 
     } 
    } 
    file_put_contents($pid_file, (strtotime("now") - 95) . " " . ftell($fp)); 
    fclose($fp); 
} 

?> 

답변

0

해결책은 MySQL 데이터베이스에 로그를 저장하는 것입니다. mysql에 저장 한 후 로그 파일을 구문 분석 할 C 언어 프로그램을 작성할 수 있습니다. 그것은 훨씬 더 빠르며 그리 어렵지 않습니다. 또 다른 옵션은 phyton을 사용하는 것이지만 데이터베이스를 사용해야한다고 생각합니다. 전체 텍스트 색인을 사용하여 문자열을 일치시킬 수 있습니다. 파이썬은 바이너리로 컴파일 될 수도 있습니다. 이것은 그것을보다 효율적으로 만든다. 요청에 따라 : 로그 파일이 증분 스택. 한 번에 10GB를주는 것이 아닙니다.

+1

그는 몇 시간 만에 10G의 데이터를 보았습니다. 필자가 실제로 필요로하는 것을 요약하기 전에 MySQL에서 바라는 것은 아닙니다. 이와 같은 데이터에 대한 전체 텍스트 인덱스 (MyISAM을 암시 함)는 재앙입니다. – Evert

+0

@Evert : 그러나 iit는 0 바이트 로그 파일로 시작합니까? 내 대답을 보라. – Bytemain

+0

빈 로그로 시작하지 않습니다 ... 그것은 수십 GB의 데이터로 시작됩니다 :/내가 스크립트가 메모리 할당 오류로 시간 초과를 게시 했으므로 어딘가에 누출이 있어야한다고 생각합니다. T는 그것을 찾은 것 같아요 ... 나는 fgets를 사용하여 메모리의 현재 줄을 유지하는 인상을 받고있었습니다. 포인터를 추적하는 "pid"파일 아이디어가 있습니까 ?? – Igor

0

개인적으로 로그 항목을 실행중인 스크립트로 직접 보냅니다. 아파치는 파이프 (|)로 로그의 파일 이름을 시작함으로써이를 허용합니다. 그래도 작동하지 않는다면, FIFO를 만들 수 있습니다 (mkfifo 참조).

실행중인 스크립트 (그게 무엇이든)는 x 줄을 버퍼링하고이를 기반으로 수행해야 할 작업을 수행 할 수 있습니다. 데이터를 읽는 것이 그리 어려운 일이 아니며 병목 현상이 발생하지 않아야합니다.

데이터베이스에서 INSERT 문과 관련된 문제가 발생할 것으로 생각됩니다.

1

아마도 꼬리의 PHP 버전을 조정하여 선을 세지 않고 마지막 타임 스탬프를 검색 한 다음 그 점에서 선을 읽고 하나씩 처리 할 수 ​​있을까요?

내가 조금 궁금하지만, 그래서 지금 할 :(나는이 대답은 늦게 알고

0

을 불행하게도 드릴 수 없습니다로는 자신을 시도해하지만 수시겠습니까 여전히 도움이 될 수 있습니다. (코드는 항상 개선 될 수 있습니다.)

Apache는 여러 개의 로그 파일을 지원하며 로그 파일을 생성 할 수있는 능력은 여러 개의 로그 파일을 지원합니다 다른 형식으로 http://httpd.apache.org/docs/1.3/multilogs.html

실시간 로그 모니터링에 필요한 최소한의 데이터만으로 두 번째 로그 파일을 만듭니다. 이 경우 처음부터 사용자 에이전트 문자열 등을 로그에서 제거 할 수 있습니다.

예제 로그 라인을 기반으로하면 PHP가로드해야하는 데이터의 양이 반으로 줄어들 수 있습니다.

관련 문제