[logging] Golang의 글로벌 로깅에 대한 올바른 접근 방식

Go에서 애플리케이션 로깅의 패턴은 무엇입니까? 로그해야하는 고 루틴 5 개가있는 경우 …

  • 싱글을 log.Logger만들고 전달 하시겠습니까?
  • 그것에 대한 포인터를 전달 log.Logger하시겠습니까?
  • 각 고 루틴 또는 함수가 로거를 만들어야합니까?
  • 로거를 전역 변수로 만들어야합니까?


답변

  • 단일 로그를 만들고 로거를 전달 하시겠습니까?

가능합니다. log.Logger는 여러 goroutines에서 동시에 사용할 수 있습니다.

  • 해당 로그에 대한 포인터를 전달합니다.

log.New*Logger 는 일반적으로 객체를 포인터로 전달해야 함을 나타내는를 반환합니다 . 값으로 전달하면 구조체의 복사본 (즉, Logger의 복사본)이 생성되고 여러 고 루틴이 동일한 io.Writer에 동시에 쓸 수 있습니다 . 작성자의 구현에 따라 심각한 문제가 될 수 있습니다.

  • 각 고 루틴 또는 함수가 로거를 만들어야합니까?

각 기능이나 고 루틴에 대해 별도의 로거를 만들지 않을 것입니다. 고 루틴 (및 기능)은 별도의 로거의 유지 관리를 정당화하지 않는 매우 가벼운 작업에 사용됩니다. 프로젝트의 더 큰 구성 요소마다 로거를 만드는 것이 좋습니다. 예를 들어 프로젝트에서 메일을 보내기 위해 SMTP 서비스를 사용하는 경우 메일 서비스에 대해 별도의 로거를 만드는 것이 좋은 생각처럼 들리므로 출력을 별도로 필터링하고 끌 수 있습니다.

  • 로거를 전역 변수로 만들어야합니까?

패키지에 따라 다릅니다. 이전 메일 서비스 예에서 서비스의 각 인스턴스에 대해 하나의 로거를 사용하는 것이 좋습니다. 그래야 사용자가 Gmail 메일 서비스를 사용하는 동안 오류를 로컬 MTA를 사용하는 동안 발생한 오류 (예 : sendmail ).


답변

간단한 경우에는 로그 패키지에 정의 된 전역 로거가 log.Logger있습니다. 이 글로벌 로거는를 통해 구성 할 수 있습니다 log.SetFlags.

이후 하나는 바로 최상위 같은 로그 패키지의 기능을 호출 할 수 있습니다 log.Printflog.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()
}