어떤 프로그램이든지, 내 프로그램이 잘 돌아가기 위해서는 로그라는 도구를 많이 사용한다.
단순히 정상 동작에 대한 로그를 출력할 수도 있고, 디버깅을 위한 로그를 출력할 수도 있다.
Go 언어에서는 기본적으로 log
패키지를 제공하고 있으나, 충분한 기능을 제공하는 편은 아니다.
정말 기본적인 시스템 로그를 남길 때 사용하는 편이고 일반적으로는 외부 패키지나 go 1.21 부터 도입된 slog 패키지를 사용하는 편이다.
그 이전에는 외부 솔루션이었던 zerolog, zap 등을 사용했다.
단순히 편의성에만 목적이 있었던 것은 아니고, 성능적으로도 더 뛰어났기 때문에 사용했다. 이전엔 레포지토리 내의 benchmark에서 성능에 대한 부분을 확인하고 도입을 결정하기도 했다.
JSON으로 로깅이 왜 필요한가?
모든 로그를 JSON으로 찍을 필요는 없지만, Log를 검색하는 플랫폼들을 사용한다면, 보다 구조화된 로깅을 필요할때가 있다.
요즘은 플랫폼들이 검색을 정말 잘 하는 편이지만, json과 같이 구조화되면, 검색 성능이 증가할 뿐만 아니라 보다 복잡한 쿼리로도 검색을 할 수 있다.
예를 들어, level:info
와 같이 검색을 하면, info 레벨의 로그만 검색한다거나, level:info AND message:"hello"
와 같이 검색을 하면, info 레벨이면서 message에 hello가 포함된 로그를 검색할 수 있다.
slog 패키지 사용하기
앞서 외부 라이브러리를 사용해도 된다고 했지만, 최근에는 기본 패키지 slog가 너무 잘 나오다보니, 외부 솔루션을 사용할 필요가 거의 없어졌다.
일반적으로 다른 라이브러리에서 사용하고 있던 기능들을 모두 포함하였으며, 성능도 괜찮은 편이다
package main
import (
"log/slog"
)
func main() {
slog.Info("Log test", // Message
"key1", "value1", // Custom Field-Value
"key2", "value2", // Custom Field-Value
)
}
2025/03/07 16:25:53 INFO Log test key=value
JSON으로 출력하기
JSON으로 출력하는 방식은 그리 어렵지 않다. logger
를 설정해주면 되는데 바로 slog.NewJSONHandler
를 사용하는 것이다.
package main
import (
"log/slog"
"os"
)
func main() {
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
slog.SetDefault(logger)
slog.Info("Log test", // Message
"key1", "value1", // Custom Field-Value
"key2", "value2", // Custom Field-Value
)
}
위와 같이 JSONHandler로 처리하는 logger를 만들고 이를 시스템의 Default로 설정하면 된다.
{"time":"2025-03-07T16:27:52.268722+09:00","level":"INFO","msg":"Log test","key1":"value1","key2":"value2"}
다음과 같이 결과를 얻을 수 있다.
기본적으로 TextHandler
, JSONHandler
를 제공하며, 디폴트로는 TextHandler
가 사용된다.
context
를 통해 ctx
에 담아두었던 값을 활용할 수도 있는데, 이는 추후 Request ID를 로깅하는 방법을 포스팅 하면서 후술하도록 하겠다