쉘 스크립트로 액세스로그에서 날짜별 액세스 수 추출하기

2021. 2. 18. 11:45IT/S . e . v . e . r . I . n . f . r . a

아파치 로그나 애플리케션 서비스의 액세스 로그를 날짜별로 서머리하고 싶을 때가 있다.

오늘은 액세스 로그에서 날짜별 액세스 수를 집계하는 쉘 스크립트를 살펴보도록 하자. 쉘 스크립트 초보자를 대상으로 하는 포스트이기 때문에 하나하나 짚어가며 설명을 하려 한다. 쉘 스크립트를 어느정도 이해하고 있는 분이라면 아래 소스만으로도 충분하리라 생각된다.

먼저 소스를 봅시다.

#!/bin/bash

VAR_LOG="/var/log/httpd/access*"

rm -rf  access.txt

function daily() {
  for i in $(seq -f "%02g" 31);  do
    VAR_LOC_CNT=$(grep "\[$i/$2" ${VAR_LOG} | wc -l)
    
    if [[ $VAR_LOC_CNT -gt 0 ]]; then
      echo "$1월$i일 액세스수 : ${VAR_LOC_CNT}" >> access.txt
    fi
  done
}

daily 1 Jan
daily 2 Feb

먼저 액세스로그가 위치한 곳에 여러파일로 분산되어 있는 로그 파일들 전체를 대상으로 하기 위해 아스테리스크(*)로 지정을 했다.

VAR_LOG="/var/log/httpd/access*"

그리고는 이전에 서머리 되었던 파일을 삭제해준다.

rm -rf  access.txt

그 다음은 소스의 재사용을 위해 함수로 로직을 만들어 주었는데

쉘 스크립트에서 함수 사용시는 아래와 같은 형식으로 해주면 된다.

function <함수명>() {
  함수 로직
}

함수명

함수의 앞에 fuction은 생략해도 문제는 없다.

추가로 함수에서 인자를 받는 형식도 알아보자

function <함수명>() {
  VAR_LOC_PARAM1=$1
  VAR_LOC_PARAM2=$2
  
  함수로직
}

함수명 <param1> <param2>

함수에서 인자를 받아드릴 때는 $1, $2, $3의 형식으로 순차적으로 받아드린다.

받아 드린 인자를 직접 사용해도 되고 함수내에서 여러번 사용된다면 위에와 같이 로컬 변수에 할당해서 사용하여도 된다.

이때 사용된 로컬 변수는 함수내에서만 유효하기 때문에 함수를 벗어나면 사용할 수 없다.

자, 그럼 이제 본격적으로 로직을 살펴보도록 하자

for i in $(seq -f "%02g" 31);  do
  VAR_LOC_CNT=$(grep "\[$i/$2" ${VAR_LOG} | wc -l)
  
  if [[ $VAR_LOC_CNT -gt 0 ]]; then
    echo "$1월$i일 액세스수 : ${VAR_LOC_CNT}" >> access.txt
  fi
done

아파치의 액세스 로그를 보면 아래와 같은 형식으로 되어 있는데 이것을 날짜별로 추출하기 위해서는 날짜를 기준으로 데이터를 가져와야 한다. 그러면 루프를 돌면서 날짜별로 로그의 수를 가져오면 되는데 로그를 보면 [01/Jan/2021:08:11:49 +0900] 로 되어 있어 루프를 돌 때 입력되는 날짜의 포맷이 일반 루프문에서는 대응이 불가하다

/var/log/httpd/access_log:111.111.111.111 222.222.222.222 - - [01/Jan/2021:08:11:49 +0900] 
"GET / HTTP/1.1" 200 26216 "http://xxx.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) 
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"

그 때 필요한 것이 seq 커맨드다.

이 seq 커맨드는 숫자의 자릿수 포맷을 지정할 수 있는 명령어 인데 사용법은 아래와 같다.

seq -f "%02g" 10

01 02 03 04 05 06 07 08 09 10

여기서 "-f" 옵션은 특정 자릿수까지 0으로 채울 수 있다. "%03g"를 하면 세자리수이고, "%02g"하면 두자리수이다.

이외에도 -w 나 -s 옵션도 있지만 여기서는 다루지 않는다.

 이 seq를 루프문에 사용하여 날짜를 두자리 포맷으로 가져오도록 만들어 보자

for i in $(seq -f "%02g" 31);  do
  echo $i
done

루프문으로 받아온 두자리 포맷의 숫자를 가지고 로그에서 검색을 해서 그 숫자를 세는 커맨드는 아래와 같다.

grep "\[01/Jan" /var/log/httpd/access* | wc -l

grep에서 "["는 특수문자이기 때문에 글자 그대로 해석하도록 "\"로 감싸주었고 나머지 글자들은 아까 위에서 보았던 로그의 [01/Jan/2021:08:11:49 +0900] 부분과 일치하는 열을 찾아오도록 되어 있다. 찾아온 열은 파이프로 연결된 wc -l을 통해 갯수를 카운트해서 출력해주는 로직이다.

위에 다룬 루프문과 날짜별 카운트를 합치게 되면 전체 로그에서 날짜별로 카운트 된 숫자들을 받아 올 수 있다.

위에 소스는 조금만 응용을 하면 여러가지 로그에서 필요한 로그의 통계를 낼 수 있을 것이다.