먼저 들어가기 전에 non-block 이나 golang 기본 적인 로깅에 대해서 모르신다면 이 전 포스팅을 확인해주세요.
Go(Golang)로 로거 만들기: Logging in Golang
개요 프로그램 개발하면서 로그의 중요성을 말할 것도 없다. 이력 추적이 가능하고, 디버깅에도 유용하기 때문이다. Golang 은 로그 패키지를 기본으로 제공하고 있다. 이 번 포스팅에서는 Golang
banaconda.tistory.com
동기, 비동기, 블록, 논 블록 개념: Synchornous, Asynchronous, Blocking, Non-Blocking with Go(Golang)
개요 대부분의 개발자들은 동기(Synchronous)/비동기(Asyncronous)에 대해서는 들어봤을 것이다. 블록(Blocking)/논블록(Non-Blocking) 에 대해서도 아마 들어봤을 수도 있다. 처음엔 동기 + 블록으로 개발을
banaconda.tistory.com
개요
기본으로 제공해주는 log 패키지는 동기 방식으로 동작한다. main 로직과 logger 가 같은 시스템 안에서 돌아가기 때문에 main 로직에서는 로거가 디스크에 쓰기 작업이 끝날 때까지 기다리고 다음 작업을 한다.
아래 그림을 보면 로거를 호출하고 로거가 리턴할 때까지 main 로직은 멈춰있는 상태가 된다.
반면에 비동기로 동작하는 Asynchronous Logger 는 main 로직과 logger 로직은 다른 시스템에서 동작하고, 중간에 버퍼를 둬서 main 로직은 buffer 에 데이터를 쓰고 추후에 Logger 가 buffer 에서 데이터를 빼서 디스크에 쓰게 된다.
아래 그림을 보면 로거를 호출하고 바로 다음 로그를 넣는 모습을 볼 수 있다. 또한 IO 작업이 진행 될 동안 main 로직은 다른 작업을 할 수 있다.
이 처럼 비동기 방식의 Logger 는 buffer 가 허용되는 만큼 Bursting 을 견딜 수 있고, IO 가 진행되는 동안 다른 작업을 할 수 있기 때문에 고성능의 어플리케이션에 유용하다.
Asynchronous Non-blocking logger 의 장단점
장점
- 더 높은 throughput 을 낼 수 있다. 특히 로그를 Bursting(단 시간에 대량으로 발생)하는 어플리케이션의 경우 로그로 인한 지연을 줄일 수 있다.
- 로깅 응답 시간을 줄일 수 있다. 로깅을 시작하고 바로 응답이 오기 때문에 ASync Logger 가 Sync Logger 보다 일정하게 낮은 지연으로 응답을 줄 수 있다.
단점
- 로그를 호출하는 쪽과 로그를 파일에 쓰는 쪽의 프로세스가 다르기 떄문에 에러 처리가 힘들다. 로깅 시 signal or exceptionHandler 등을 통해서 처리할 수는 있지만 완벽하게 처리는 힘들다. 그래서 Sync Logger 와 적절하게 섞어서 쓰기도 한다.
- Mutable Object 를 처리할 때, 로그 쓰는 과정에 값이 변할 수도 있다.
- Single core CPU를 사용하면 성능향상이 별로 없을 수도 있다.
- 버퍼가 꽉 찰 경우, Sync Logger 와 같이 병목이 생기게 된다.
설계
Features
- LogLevel: Trace, Debug, Info, Warn, Error 5 가지 분류와 동적으로 변경
- 가변인자를 받는 포맷 스트링 지원
- Log format Flags: 날짜, 시간, 파일, line 등
- 비동기 방식의 로거
Logger Client Interface
Logger Client 는 아래와 같은 Level 별로 로깅할 수 있는 interface를 가지고 있어야한다. 그리고 동적으로 Log Level 을 변경할 수 있는 interface 와 Close 시 buffer 에 남아있는 log 들을 쓰기위해 Close interface 도 있어야한다.
type Logger interface {
Trace(format string, v ...any)
Debug(format string, v ...any)
Info(format string, v ...any)
Warn(format string, v ...any)
Error(format string, v ...any)
SetLogLevel(level int)
GetLogLevel() int
Close()
}
Log Level
log level 은 const enum 타임으로 쓰고, 로그 prefix 에 표시 하기 위한 string 전환용 맵도 가지고 있는다.
const (
Trace = iota
Debug
Info
Warn
Error
)
var levelStringMap = map[int]string{
Trace: "TRACE",
Debug: "DEBUG",
Info: "INFO",
Warn: "WARN",
Error: "ERROR",
}
Log Message
Logging Client 와 Server 간에 주고 받을 메시지 포맷을 정의한다. cmd 는 Server 에게 요청하는 명령이고, buf 에 Client 에서 만든 로그를 담는다.
const (
write = iota
exit
)
type logMessage struct {
cmd int
buf []byte
}
Log Flags
bit flags 를 이용해서 log 포맷, blocking 유무, stdout 으로 출력 유무 등을 선택할 수 있게 한다.
const (
Ldate = 1 << iota
Ltime
Lmicroseconds
Llongfile
Lshortfile
LUTC
Lblocking
Lstdout
LstdFlags = Ldate | Ltime
)
정리
이 번 포스팅에서는 Async + Non-Blocking Logger 에 대해서 알아보고, 만들기 위한 기초 설계를 알아봤다. 다음 포스팅에서는 실제로 구현을 통해서 Logger 를 완성해보자.
참고
'Programming > Golang' 카테고리의 다른 글
| Go(Golang)으로 Asynchronous Non-Blocking Logger 만들기 3편 (0) | 2022.07.11 |
|---|---|
| Go(Golang)으로 Asynchronous Non-Blocking Logger 만들기 2편 (0) | 2022.07.08 |
| Go(Golang)로 로거 만들기: Logging in Golang (0) | 2022.07.04 |
| 견고한 Go 모듈 만들기 (0) | 2022.07.03 |