웹서버의 경우 로그를 따로 남기지 않으면 개발하기가 매우 어렵습니다. 문제가 발생하더라도 에러의 발생 위치나 원인을 파악하기가 어렵습니다. 따라서 저희는 아래와 같이 서버가 실행되면 로그 파일을 먼저 열고, log_fd를 static 변수로 저장하여 필요한 로그들을 모두 남겼습니다.
void
ServerManager::openLog()
{
std::string date = ft::getTimestamp();
date = date.substr(1, date.size() - 2);
std::string log_path = "log/" + date + "_log";
if ((ServerManager::log_fd = open(log_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0755)) == -1)
return ;
}
로그 포인트
서버가 만들어졌을 때, 새로운 커넥션이 감지되었을 때, 커넥션이 생성되거나 생성에 실패하였을 때, 새로운 리퀘스트가 감지되었을 때, 정상적으로 새로운 리퀘스트가 만들어졌거나 리퀘스트를 읽는 과정에서 에러가 발생하였을 때, 새로운 응답을 만들었을 때, 응답을 모두 전송했을 때, 커넥션이 닫힐 때 등이 저희가 설정한 로그 포인트들입니다. 그리고 주기적으로 서버가 HealthLog를 남기도록 하여 block되지 않고 제어흐름이 살아있음을 확인했습니다.
[Created][Servers]2 servers created successfully.
[2020_1023_1544_07][Sended][Response][Server:default][200][OK][CFD:7][headers:4][body:1159] Response sended
[2020_1023_1544_07][Sended][Response][Server:default][200][OK][CFD:6][headers:4][body:1159] Response sended
[2020_1023_1544_07]read/write operation return fail: Connection close detected by client 7
[Deleted][Connection][Server:default][CFD:7] Connection closed.
[2020_1023_1544_07][Sended][Response][Server:default][200][OK][CFD:8][headers:4][body:1159] Response sended
[2020_1023_1544_07]read/write operation return fail: Connection close detected by client 6
단순히 로그를 남긴다는 액션, 남기는 시점만이 아니라 남기는 내용이 중요합니다. 시간, server/client fd는 존재한다면 포함하는 것이 디버깅에 좋습니다. 요청과 응답은 body size를 기록하는 편이 data 손실 여부를 체크하기에 좋고, 요청의 경우 URI, path_info, query를 기록하고 응답의 경우 상태 코드와 사유 구절을 기록하는 것이 디버깅에 좋습니다.
구현이 어느 정도 성공적으로 진행되었다면, 로그의 복잡성을 줄이기 위해 로그 레벨을 도입해보세요. header file에서 macro로 로그 레벨을 정의하고, 각 로그 함수의 시작줄에서 해당 로그를 남기기 위한 최소 레벨을 검사하도록 하면 선택적으로 로그 수준을 조절할 수 있습니다.