Prawidłowe podejście do globalnego logowania w Golang

119

Jaki jest wzorzec logowania się aplikacji w Go? Jeśli mam, powiedzmy, 5 gorutyn, z których muszę się wylogować, czy powinienem ...

  • Utworzyć singiel log.Loggeri przekazać go innym ?
  • Podaj do tego wskazówkę log.Logger?
  • Czy każdy goroutine lub funkcja powinien tworzyć rejestrator?
  • Czy powinienem utworzyć rejestrator jako zmienną globalną?
Carson
źródło

Odpowiedzi:

59
  • Stwórz pojedynczy dziennik, zarejestruj się i przekaż go innym?

To jest możliwe. Log.Logger może być stosowany równocześnie z wielu goroutines.

  • Przekaż wskaźnik do tego dziennika.

log.New zwraca a, *Loggerco zwykle wskazuje, że należy przekazać obiekt jako wskaźnik. Przekazanie go jako wartości spowodowałoby utworzenie kopii struktury (tj. Kopii Loggera), a następnie wiele goroutines mogło pisać jednocześnie do tego samego io.Writer . To może być poważny problem, w zależności od implementacji pisarza.

  • Czy każdy goroutine lub funkcja powinien tworzyć rejestrator?

Nie stworzyłbym osobnego rejestratora dla każdej funkcji lub goroutine. Gorutyny (i funkcje) są używane do bardzo lekkich zadań, które nie uzasadniają utrzymywania oddzielnego rejestratora. Prawdopodobnie dobrym pomysłem jest utworzenie rejestratora dla każdego większego komponentu projektu. Na przykład, jeśli Twój projekt korzysta z usługi SMTP do wysyłania wiadomości, utworzenie osobnego programu rejestrującego dla usługi pocztowej wydaje się dobrym pomysłem, aby można było oddzielnie filtrować i wyłączać dane wyjściowe.

  • Czy powinienem utworzyć rejestrator jako zmienną globalną?

To zależy od twojego pakietu. W poprzednim przykładzie usługi pocztowej dobrym pomysłem byłoby posiadanie jednego rejestratora dla każdej instancji Twojej usługi, aby użytkownicy mogli rejestrować awarie podczas korzystania z usługi poczty Gmail inaczej niż awarie, które wystąpiły podczas korzystania z lokalnego MTA (np. Sendmail ).

tux21b
źródło
37

W prostych przypadkach, istnieje rejestrator globalne zdefiniowane w pakiecie dziennika log.Logger. Ten globalny rejestrator można skonfigurować za pomocą log.SetFlags.

Następnie można po prostu wywołać funkcje najwyższego poziomu pakietu dziennika, takie jak log.Printfi log.Fatalf, które używają tej globalnej instancji.

zzzz
źródło
Pomyślałem, że możesz ustawić flagi, których nie możesz użyć niestandardowego rejestratora.
0xcaff
@caffinatedmonkey w rzeczywistości możesz użyć niestandardowych rejestratorów, jeśli implementują one io.Writerinterfejs i zmienisz dane wyjściowe domyślnego rejestratora za pośrednictwem SetOutput().
congusbongus
16

To jest prosty rejestrator

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),
    }
}

Możesz to wykorzystać w ten sposób

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")
}
Israel Barba
źródło
10

Wiem, że to pytanie jest trochę stare, ale jeśli, tak jak ja, twoje projekty składają się z wielu mniejszych plików, głosuję na twoją czwartą opcję - stworzyłem to, logger.goco jest częścią pakietu main. Ten plik go tworzy rejestrator, przypisuje go do pliku i udostępnia pozostałej części pliku main. Uwaga Nie znalazłem wdzięcznego sposobu na zamknięcie dziennika błędów ...

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)
}
Omortis
źródło
8
Dla wdzięku zakończenie, można prawdopodobnie defer errorlog.Close()pod koniec realizacji, lub w celu lepszego zapewnienia jego zamknięte, ustanowione obsługi sygnałów za pomocą iść za pakiet sygnał golang.org/pkg/os/signal
Anfernee
4

To jest starsze pytanie, ale chciałbym zasugerować użycie http://github.com/romana/rlog (który opracowaliśmy). Jest konfigurowany za pomocą zmiennych środowiskowych, obiekt rejestrujący jest tworzony i inicjowany podczas importowania rlog. Dlatego nie ma potrzeby omijania rejestratora.

rlog ma kilka funkcji:

  • W pełni konfigurowalne znaczniki daty / czasu
  • Jednoczesne wyjście na stderr lub stdout, a także do pliku.
  • Standardowe poziomy dziennika (debugowanie, informacje itp.), A także dowolnie konfigurowalne rejestrowanie wielopoziomowe.
  • Rejestrowanie na żądanie informacji o dzwoniącym (plik, numer linii, funkcja).
  • Możliwość ustawienia różnych poziomów dziennika dla różnych plików źródłowych.

Jest bardzo mały, nie ma żadnych zewnętrznych zależności, z wyjątkiem standardowej biblioteki Golang i jest aktywnie rozwijany. Przykłady podano w repozytorium.

Juergen Brendel
źródło
3
Dziękujemy za ujawnienie swojego powiązania z polecanym produktem! To jest doceniane.
Robert Columbia,
2

Znalazłem domyślny pakiet dziennika ( https://golang.org/pkg/log/ ) nieco ograniczający. Na przykład brak obsługi dzienników informacji i debugowania.
Po krótkich poszukiwaniach zdecydowałem się na użycie https://github.com/golang/glog . Wydaje się, że jest to port https://github.com/google/glog i zapewnia przyzwoitą elastyczność w logowaniu. Na przykład, gdy aplikacja jest uruchamiana lokalnie, możesz potrzebować dziennika poziomu DEBUG, ale możesz chcieć uruchomić tylko na poziomie INFO / ERROR w środowisku produkcyjnym. Lista pełnych funkcji / przewodnika znajduje się tutaj https://google-glog.googlecode.com/svn/trunk/doc/glog.html (dotyczy modułu c ++, ale w większości przekłada się na port golang)

totumfacki
źródło
0

Jednym z modułów rejestrowania, który możesz wziąć pod uwagę, jest klog . Obsługuje rejestrowanie „V”, co zapewnia elastyczność logowania na określonym poziomie

klog to rozwidlenie glogu i przezwycięża następujące wady

  • glog przedstawia wiele „pułapek” i wprowadza wyzwania w środowiskach kontenerowych, z których wszystkie nie są dobrze udokumentowane.
  • glog nie zapewnia łatwego sposobu testowania logów, co obniża stabilność używającego go oprogramowania
  • glog jest oparty na C ++, a klog to czysta implementacja języka golang

Przykładowe wdrożenie

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()
}
Chids
źródło