Go에서 애플리케이션 로깅의 패턴은 무엇입니까? 로그해야하는 고 루틴 5 개가있는 경우 …
- 싱글을
log.Logger
만들고 전달 하시겠습니까? - 그것에 대한 포인터를 전달
log.Logger
하시겠습니까? - 각 고 루틴 또는 함수가 로거를 만들어야합니까?
- 로거를 전역 변수로 만들어야합니까?
답변
- 단일 로그를 만들고 로거를 전달 하시겠습니까?
가능합니다. log.Logger는 여러 goroutines에서 동시에 사용할 수 있습니다.
- 해당 로그에 대한 포인터를 전달합니다.
log.New*Logger
는 일반적으로 객체를 포인터로 전달해야 함을 나타내는를 반환합니다 . 값으로 전달하면 구조체의 복사본 (즉, Logger의 복사본)이 생성되고 여러 고 루틴이 동일한 io.Writer에 동시에 쓸 수 있습니다 . 작성자의 구현에 따라 심각한 문제가 될 수 있습니다.
- 각 고 루틴 또는 함수가 로거를 만들어야합니까?
각 기능이나 고 루틴에 대해 별도의 로거를 만들지 않을 것입니다. 고 루틴 (및 기능)은 별도의 로거의 유지 관리를 정당화하지 않는 매우 가벼운 작업에 사용됩니다. 프로젝트의 더 큰 구성 요소마다 로거를 만드는 것이 좋습니다. 예를 들어 프로젝트에서 메일을 보내기 위해 SMTP 서비스를 사용하는 경우 메일 서비스에 대해 별도의 로거를 만드는 것이 좋은 생각처럼 들리므로 출력을 별도로 필터링하고 끌 수 있습니다.
- 로거를 전역 변수로 만들어야합니까?
패키지에 따라 다릅니다. 이전 메일 서비스 예에서 서비스의 각 인스턴스에 대해 하나의 로거를 사용하는 것이 좋습니다. 그래야 사용자가 Gmail 메일 서비스를 사용하는 동안 오류를 로컬 MTA를 사용하는 동안 발생한 오류 (예 : sendmail ).
답변
간단한 경우에는 로그 패키지에 정의 된 전역 로거가 log.Logger
있습니다. 이 글로벌 로거는를 통해 구성 할 수 있습니다 log.SetFlags
.
이후 하나는 바로 최상위 같은 로그 패키지의 기능을 호출 할 수 있습니다 log.Printf
및 log.Fatalf
그 글로벌 인스턴스를 사용합니다.
답변
이것은 간단한 로거입니다
package customlogger
import (
"log"
"os"
"sync"
)
type logger struct {
filename string
*log.Logger
}
var logger *logger
var once sync.Once
// start loggeando
func GetInstance() *logger {
once.Do(func() {
logger = createLogger("mylogger.log")
})
return logger
}
func createLogger(fname string) *logger {
file, _ := os.OpenFile(fname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
return &logger{
filename: fname,
Logger: log.New(file, "My app Name ", log.Lshortfile),
}
}
이런 식으로 사용할 수 있습니다
package main
import (
"customlogger"
"fmt"
"net/http"
)
func main() {
logger := customlogger.GetInstance()
logger.Println("Starting")
http.HandleFunc("/", sroot)
http.ListenAndServe(":8080", nil)
}
func sroot(w http.ResponseWriter, r *http.Request) {
logger := customlogger.GetInstance()
fmt.Fprintf(w, "welcome")
logger.Println("Starting")
}
답변
이 질문이 조금 오래 logger.go
되었다는 것을 알고 있지만 저처럼 프로젝트가 네 번째 옵션에 투표하는 여러 개의 작은 파일로 구성되어 있다면 패키지 메인의 일부인을 만들었습니다 . 이 go 파일은 로거를 생성하고 파일에 할당하고 나머지 main에 제공합니다. 참고 오류 로그를 닫는 우아한 방법을 찾지 못했습니다 …
package main
import (
"fmt"
"log"
"os"
)
var errorlog *os.File
var logger *log.Logger
func init() {
errorlog, err := os.OpenFile(logfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
fmt.Printf("error opening file: %v", err)
os.Exit(1)
}
logger = log.New(errorlog, "applog: ", log.Lshortfile|log.LstdFlags)
}
답변
이것은 오래된 질문이지만 http://github.com/romana/rlog (우리가 개발 한) 사용을 제안하고 싶습니다 . 환경 변수를 통해 구성되며, rlog를 가져올 때 로거 개체가 생성되고 초기화됩니다. 따라서 로거를 통과 할 필요가 없습니다.
rlog에는 다음과 같은 몇 가지 기능이 있습니다.
- 완전히 구성 가능한 날짜 / 시간 스탬프
- stderr 또는 stdout 및 파일에 대한 동시 출력.
- 표준 로그 수준 (디버그, 정보 등) 및 자유롭게 구성 가능한 다중 수준 로깅.
- 발신자 정보 (파일, 회선 번호, 기능)의 요청시 로깅.
- 서로 다른 소스 파일에 대해 서로 다른 로그 수준을 설정하는 기능.
표준 Golang 라이브러리를 제외하고는 매우 작고 외부 종속성이 없으며 활발하게 개발되고 있습니다. 저장소에 예제가 제공됩니다.
답변
기본 로그 패키지 ( https://golang.org/pkg/log/ )가 다소 제한적 이라는 것을 알았습니다 . 예를 들어 정보 및 디버그 로그에 대한 지원이 없습니다.
주위를 둘러 본 후 https://github.com/golang/glog 사용하기로 결정했습니다 . 이것은 https://github.com/google/glog 의 포트로 보이며 로깅에 상당한 유연성을 제공합니다. 예를 들어 로컬에서 응용 프로그램을 실행할 때 DEBUG 수준 로그를 원할 수 있지만 프로덕션에서는 INFO / ERROR 수준에서만 실행하려고 할 수 있습니다. 전체 기능 / 가이드 목록은 여기 https://google-glog.googlecode.com/svn/trunk/doc/glog.html입니다 (C ++ 모듈 용이지만 대부분의 경우 golang 포트로 번역됨).
답변
고려할 수있는 로깅 모듈 중 하나는 klog 입니다. 특정 수준에서 로깅 할 수있는 유연성을 제공하는 ‘V’로깅을 지원합니다.
klog는 glog의 포크이며 다음과 같은 단점을 극복합니다.
- glog는 많은 “문제점”을 제시하고 컨테이너화 된 환경에서 문제를 야기하며, 모두 잘 문서화되지 않았습니다.
- glog는 로그를 테스트하는 쉬운 방법을 제공하지 않아 로그를 사용하는 소프트웨어의 안정성을 떨어 뜨립니다.
- glog는 C ++ 기반이고 klog는 순수한 golang 구현입니다.
샘플 구현
package main
import (
"flag"
"k8s.io/klog"
)
type myError struct {
str string
}
func (e myError) Error() string {
return e.str
}
func main() {
klog.InitFlags(nil)
flag.Set("v", "1")
flag.Parse()
klog.Info("hello", "val1", 1, "val2", map[string]int{"k": 1})
klog.V(3).Info("nice to meet you")
klog.Error(nil, "uh oh", "trouble", true, "reasons", []float64{0.1, 0.11, 3.14})
klog.Error(myError{"an error occurred"}, "goodbye", "code", -1)
klog.Flush()
}