본문 바로가기

서버 관리

출력 버퍼 flush 에 대한 고찰

[1. 개요]

애플리케이션을 실행하고, 그 로그를 출력하는 상황은 크게 아래와 같이 두가지가 있을 수 있다.

  1. 콘솔 상에서 바로 확인
  2. 자식 프로세스 등으로 실행 및 파이프로 연결하여 확인

실행 한 애플리케이션이 로그 출력 후 출력 버퍼를 바로 flush 하지 않는다면 

로그를 확인 하는 시점이 상당히 다르다는 것이다.

 

1번 방식은 라인 단위로 즉시 확인 할 수 있지만,

2번 방식은 라인 단위로 즉시 확인 할 수 없다.

 

그 이유는

  • 콘솔은 라인 버퍼링 방식으로, 개행 문자를 만나는 즉시 출력 버퍼를 비운다.
  • 파이프, 리다이렉션 등은 풀 버퍼링 방식으로 출력 버퍼가 가득 차야 버퍼를 비운다.

그래서, 2번 방식으로 자식 프로세스의 로그를 실시간으로 확인하여야 하는 경우가 문제가 된다.


[2. 예시]

아래 코드를 빌드 후, 아래와 같은 방식으로 실행하면 프로그램 종료 후 한번에 메시지가 출력 된다.

  • g++ main.cpp
  • ./a.out | cat
#include <stdio.h>
#include <unistd.h>

int main()
{
        for (int i=0; i<5; i++) {
                printf("hello %d\n", i);
                sleep(2);
        }

        return 0;
}

[3. 문제 상황]

이러한 방식은 특정한 상황에서 문제가 될 수 있다.

  • 어떤 프로그램을 자식 프로세스로 실행,
  • 출력 버퍼를 파이프로 연결하여 부모 프로세스에서 로그를 가공 후 파일에 저장
  • 그런데, 자식 프로세스가 예기치 않게 shutdown
  • 추후 문제 확인 후 로그를 살펴 보지만 로그가 없거나 로그의 마지막 위치가 다름

[4. 해결 방안]

가장 원시적인 해결 방안은 애플리케이션의 로그 출력 후 바로 flush 하는 코드를 작성하는 것이다.

그러나, 모든 곳에 flush 코드를 삽입하는게 제한 적일 수 있다.

 

이것보다 조금 나은 방안은 앱 시작 직후 바로 아래 함수를 이용해서 출력 버퍼의 사이즈를 비우는 것이다.

  • setvbuf

그러나, 소스 수정이 불가능 한 상황등이 있다면 특히 리눅스 환경이라면 아래와 같이 stdbuf 명령어의 사용을 고려 해 볼 수 있다.

  • stdbuf
  • stdbuf -oL ./a.out | cat

윈도우 환경이라면 (특히 윈도우10 이후라면) ConPTY 의 사용을 고려해 볼 수 있다.

ConPTY 는 리눅스의 tty 처럼 가상의 콘솔이 있다고 만들어 주는 것으로, 라인 버퍼링 방식을 유도한다.