개요
프로그램 개발하면서 로그의 중요성을 말할 것도 없다. 이력 추적이 가능하고, 디버깅에도 유용하기 때문이다. Golang 은 로그 패키지를 기본으로 제공하고 있다. 이 번 포스팅에서는 Golang 에서 제공하는 Logger에 대해서 간단하게 알아보고 이 패키지를 이용해서 추후 포스팅에 개선해 나가는 방법을 기술한다.
기본 사용
log package 를 import 하는 것만으로 로그를 사용할 수 있다.
package main
import "log"
func main() {
log.Println("Log")
}
실행해보면 아래와 같은 출력 결과를 볼 수 있다. 기본적으로 log 패키지는 날짜, 시간, Text 를 출력한다.
2022/07/02 14:13:11 Log
하지만 milli or microsec 단위까지 보고 싶은 경우도 생기고, 이 로그가 어떤 파일의 어떤 라인에서 발생한 것인지 알고 싶어 할 수 있다. 그럴 경우 제공되는 flags 를 이용해서 추가 정보를 출력할 수도 있다.
package main
import (
"log"
"os"
)
func main() {
logger := log.New(os.Stdout, "[Prefix] ", log.LstdFlags|log.Lmicroseconds|log.Lshortfile)
logger.Println("Log")
}
기본으로 제공되는 log 를 사용하는 대신 log.New 메소드를 이용해서 로거를 생성했다. 각 파라메터가 의미하는 바는 로그를 출력할 FileWriter(위 예제는 표준 출력을 사용했다), 로그 출력 시 text 앞에 출력할 내용(보통 [Debug], [Info] 등 Log Level 을 출력하는 데 사용함), 마지막으로 Flags 는 로그 출력에 포함할 옵션을 지정한다(default 는 log.lstdFlags. Flag 에 대한 내용은 아래 참고)
const (
Ldate = 1 << iota // the date in the local time zone: 2009/01/23
Ltime // the time in the local time zone: 01:23:23
Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
Llongfile // full file name and line number: /a/b/c/d.go:23
Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
Lmsgprefix // move the "prefix" from the beginning of the line to before the message
LstdFlags = Ldate | Ltime // initial values for the standard logger
)
위 코드를 실행해보면 아래와 같이 나온다
$ go run hello.go
[Prefix] 2022/07/02 14:30:53.227157 hello.go:10: Log
파일 출력
로그는 기록용이기 때문에 보통 파일에 쓰게 된다. 만드는 방법은 매우 간단하다.
package main
import (
"log"
"os"
)
func main() {
logFile, err := os.OpenFile("test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
logger := log.New(logFile, "[Prefix] ", log.LstdFlags|log.Lmicroseconds|log.Lshortfile)
logger.Println("Log")
}
실행해보면 test.log 가 생성되고, 표준 출력에 출력된 것과 같은 내용이 들어간 것을 확인할 수 있다.
$ go run hello.go
$ cat test.log
[Prefix] 2022/07/02 14:42:48.376603 hello.go:15: Log
다중 파일 출력
로그를 통합 기록, 레벨별 출력, 표준출력 까지 하려고하면 어떻게 해야될까? 이 것도 golang 에서 제공해주는 기본 패키지인 io.MultiWriter 를 사용하면 간단해진다.
아래 코드를 보자. 기본적으로 모든 출력은 test.log 파일과 표준 출력에 나오게 되어있고, 각 level logger 별로도 따로 파일에 쓸 수 있도록 했다.
package main
import (
"io"
"log"
"os"
)
func main() {
logFile, err := os.OpenFile("test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
infoLogFile, err := os.OpenFile("test.log.info", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
errorLogFile, err := os.OpenFile("test.log.error", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
infoHandle := io.MultiWriter(logFile, infoLogFile, os.Stdout)
errorHandle := io.MultiWriter(logFile, errorLogFile, os.Stdout)
flags := log.LstdFlags | log.Lmicroseconds | log.Lshortfile
infoLogger := log.New(infoHandle, "[INFO] ", flags)
errorLogger := log.New(errorHandle, "[ERROR] ", flags)
infoLogger.Println("INFO log")
errorLogger.Println("ERROR log")
}
실행 해보면 실행했을 때 모든 로그가 출력되는 게 보이고 test.log 에는 모든 로그가 기록되고, 각 로그파일에 각 레벨에 맞는 log가 기록 됨을 볼 수 있다.
$ go run hello.go
[INFO] 2022/07/02 14:56:42.424966 hello.go:32: INFO log
[ERROR] 2022/07/02 14:56:42.425071 hello.go:33: ERROR log
$ cat test.log
[Prefix] 2022/07/02 14:42:48.376603 hello.go:15: Log
[INFO] 2022/07/02 14:56:42.424966 hello.go:32: INFO log
[ERROR] 2022/07/02 14:56:42.425071 hello.go:33: ERROR log
$ cat test.log.info
[INFO] 2022/07/02 14:56:42.424966 hello.go:32: INFO log
$ cat test.log.error
[ERROR] 2022/07/02 14:56:42.425071 hello.go:33: ERROR log
이런식으로 로그 레벨을 다중파일로 나눠서 적기보다는 로그 수집기 같은 걸 이용해서 하는 게 파일 IO 측면에서는 더 좋을 수도 있다. 하지만 따로 로그 수집기가 없으면 애초에 로그를 남길 때 파일로 분리해서 저장하는 것도 좋을 것 같다.
결론
golang 에서 기본적으로 주어지는 log 패키지를 이용해서도 훌륭한 로거를 만들 수 있다. 수행하는 코드가 직접 로그를 파일에 쓰는 방식에서는 log 패키지만 써도 되지만 속도가 중요한 프로그램일 경우 IO block 은 상당한 성능 저하를 가져올 수 있다. 그래서 non-block 방식의 로거가 필요할 수도 있는데 비동기 로거를 만들기 위해서는 log 패키지로는 어렵기 때문에 따로 로거 패키지 작성해야된다.
다음 포스팅 부터는 non-blocking 에 대해서 알아보고, 어떻게 non-blocking 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)으로 Asynchronous Non-Blocking Logger 만들기 1편 (0) | 2022.07.07 |
| 견고한 Go 모듈 만들기 (0) | 2022.07.03 |