Shell Script 전체 코드

#! /usr/bin/env bash

hosts = (a.example.com b.example.com)
LOG = "access.log"
pattern = "ERROR|WARN"

for host in $hosts
do
    if [ -z $pattern ]; then
        ssh deploy@$host "tail -F $LOG" | awk -v server=$host '{print "["server"]", $0}' &
    else
        ssh deploy@$host "tail -F $LOG" | awk -v server=$host -v pattern=$pattern '$0~pattern {print "["server"]", $0}' &
    fi
done

trap "ps | grep ssh | grep $LOG | awk '{print \$1}' | xargs kill -9" INT
wait

명령어

각 명령어에 대한 자세한 설명은 생략하고 간단하게 나열합니다.


tail

ssh deploy@$host "tail -F $LOG" &

원격 서버에 명령어를 전달하려면 ssh deploy@$host "{command}" 형태로 사용합니다.

명령어의 끝에 & 을 붙여서 모든 tail 명령어를 백그라운드에서 실행시키고 출력값만 현재 쉘에서 모아서 봅니다.


awk

awk -v server=$host '{print "["server"]", $0}'

여기서 awk 명령어는 tail 로 출력되는 로그들을 특정 포맷으로 감싸서 재출력하는 기능을 합니다.

-v 옵션은 awk action 에 특정 변수를 넘겨주고 싶을 때 사용하고 $0 은 레코드 전체를 의미합니다.

awk -v server=$host '{print "["server"]", $0}' 은 로그들을 [a.example.com] 로그내용~ 으로 변환해서 출력해줍니다.

로그들이 여러개 쌓이면 어디 서버에서 발생한 건지 알수 없기 때문이죠


awk (filter)

awk -v server=$host -v pattern=$pattern '$0~pattern {print "["server"]", $0}'

ERROR 또는 WARN 로그만 보고 싶을 수도 있습니다.

정규표현식 ~ 오퍼레이터를 사용하여 레코드 전체 ($0) 에서 정규표현식 $pattern 이 포함되어 있는 경우에만 출력하도록 필터를 걸었습니다.

처음에는 grep 명령어를 사용했으나 속도가 너무 느려서 실시간 전달이 안되고 로그가 뚝뚝 끊겨서 전달되어 보기 굉장히 불편했습니다.


trap & wait

trap "ps | grep ssh | grep $LOG | awk '{print \$1}' | xargs kill -9" INT
wait

& 기호를 붙여 백그라운드에서 실행했기 때문에 로그를 그만 볼때 모든 프로세스를 한번에 종료시켜야 합니다.

trap <handler> <signal> 은 특정 시그널을 받았을 때 handler 를 실행시키는 명령어입니다.

wait 은 자식 프로세스가 종료될 때까지 대기합니다.

즉, 위 코드는 실행한 쉘 스크립트를 대기 시켰다가 Ctrl + C 시그널을 받았을 때 백그라운드 프로세스를 모두 kill 합니다.

wait 명령어를 넣지 않으면 스크립트를 실행한 후에 전달할 시그널이 애매합니다.

그리고 로그 보고 있을 거니까 부모 프로세스를 굳이 조작할 필요도 없습니다.


Reference

+ Recent posts