Richtiger Ansatz für die globale Protokollierung in Golang

118

Wie sieht das Muster für die Anwendungsprotokollierung in Go aus? Wenn ich zum Beispiel 5 Goroutinen habe, von denen ich mich anmelden muss, sollte ich ...

  • Single erstellen log.Loggerund weitergeben?
  • Einen Zeiger darauf herumgeben log.Logger?
  • Sollte jede Goroutine oder Funktion einen Logger erstellen?
  • Soll ich den Logger als globale Variable erstellen?
Carson
quelle

Antworten:

59
  • Erstellen Sie ein einzelnes Protokoll. Logger und geben Sie es weiter?

Das lässt sich machen. Ein log.Logger kann von mehreren Goroutinen gleichzeitig verwendet werden.

  • Übergeben Sie einen Zeiger auf dieses Protokoll. Logger?

log.New gibt a zurück, *Loggerwas normalerweise ein Hinweis darauf ist, dass Sie das Objekt als Zeiger weitergeben sollten. Wenn Sie es als Wert übergeben, wird eine Kopie der Struktur (dh eine Kopie des Loggers) erstellt, und dann schreiben möglicherweise mehrere Goroutinen gleichzeitig auf denselben io.Writer . Dies kann je nach Implementierung des Autors ein ernstes Problem sein.

  • Sollte jede Goroutine oder Funktion einen Logger erstellen?

Ich würde nicht für jede Funktion oder Goroutine einen eigenen Logger erstellen. Goroutinen (und Funktionen) werden für sehr leichte Aufgaben verwendet, die die Wartung eines separaten Loggers nicht rechtfertigen. Es ist wahrscheinlich eine gute Idee, einen Logger für jede größere Komponente Ihres Projekts zu erstellen. Wenn Ihr Projekt beispielsweise einen SMTP-Dienst zum Senden von E-Mails verwendet, empfiehlt es sich, einen separaten Protokollierer für den E-Mail-Dienst zu erstellen, damit Sie die Ausgabe separat filtern und deaktivieren können.

  • Soll ich den Logger als globale Variable erstellen?

Das hängt von Ihrem Paket ab. Im vorherigen Beispiel für einen Mail-Dienst ist es wahrscheinlich eine gute Idee, einen Logger für jede Instanz Ihres Dienstes zu haben, damit Benutzer Fehler bei der Verwendung des Google Mail-Mail-Dienstes anders protokollieren können als bei der Verwendung des lokalen MTA (z. B. sendmail) ).

tux21b
quelle
37

Für einfache Fälle ist im Protokollpaket ein globaler Logger definiert log.Logger. Dieser globale Logger kann über konfiguriert werden log.SetFlags.

Danach kann man einfach die Funktionen der obersten Ebene des Protokollpakets wie log.Printfund aufrufen log.Fatalf, die diese globale Instanz verwenden.

zzzz
quelle
Dachte, Sie können die Flags setzen, Sie können keinen benutzerdefinierten Logger verwenden.
0xcaff
@caffinatedmonkey Tatsächlich können Sie benutzerdefinierte Logger verwenden, wenn diese die io.WriterSchnittstelle implementieren und Sie die Ausgabe des Standardloggers über ändern SetOutput().
Congusbongus
16

Dies ist ein einfacher Logger

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

Sie können es auf diese Weise verwenden

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
quelle
10

Ich weiß, dass diese Frage etwas alt ist, aber wenn Ihre Projekte wie ich aus mehreren kleineren Dateien bestehen, stimme ich für Ihre vierte Option - ich habe eine erstellt logger.go, die Teil des Pakets main ist. Diese go-Datei erstellt den Logger, weist ihn einer Datei zu und stellt ihn dem Rest von main zur Verfügung. Hinweis Ich habe keine elegante Möglichkeit gefunden, das Fehlerprotokoll zu schließen ...

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
quelle
8
Zum ordnungsgemäßen Schließen könnten Sie wahrscheinlich defer errorlog.Close()am Ende der Ausführung oder um das Schließen besser zu gewährleisten, Signalhandler mithilfe des Signalpakets von Go einrichten
Anfernee
4

Dies ist eine ältere Frage, aber ich möchte die Verwendung von http://github.com/romana/rlog (das wir entwickelt haben) vorschlagen . Es wird über Umgebungsvariablen konfiguriert. Das Logger-Objekt wird beim Importieren von rlog erstellt und initialisiert. Daher müssen Sie keinen Logger weitergeben.

rlog hat einige Funktionen:

  • Vollständig konfigurierbare Datums- / Zeitstempel
  • Gleichzeitige Ausgabe an stderr oder stdout sowie an die Datei.
  • Standardprotokollstufen (Debug, Info usw.) sowie frei konfigurierbare mehrstufige Protokollierung.
  • On-Demand-Protokollierung von Anruferinformationen (Datei, Zeilennummer, Funktion).
  • Möglichkeit, verschiedene Protokollebenen für verschiedene Quelldateien festzulegen.

Es ist sehr klein, hat keine externen Abhängigkeiten außer der Standard-Golang-Bibliothek und wird aktiv weiterentwickelt. Beispiele finden Sie im Repo.

Jürgen Brendel
quelle
3
Vielen Dank, dass Sie Ihre Zugehörigkeit zu dem von Ihnen empfohlenen Produkt offengelegt haben! Es wird geschätzt.
Robert Columbia
2

Ich fand das Standardprotokollpaket ( https://golang.org/pkg/log/ ) etwas einschränkend. Zum Beispiel keine Unterstützung für Info- oder Debug-Protokolle.
Nach einigem Stöbern haben Sie sich für https://github.com/golang/glog entschieden . Dies scheint ein Port von https://github.com/google/glog zu sein und bietet eine angemessene Flexibilität bei der Protokollierung. Wenn Sie beispielsweise eine Anwendung lokal ausführen, möchten Sie möglicherweise ein Protokoll auf DEBUG-Ebene, das jedoch in der Produktion nur auf INFO / ERROR-Ebene ausgeführt werden soll. Die Liste der vollständigen Funktionen / Anleitungen finden Sie hier: https://google-glog.googlecode.com/svn/trunk/doc/glog.html (Dies gilt für das c ++ - Modul, wird jedoch größtenteils in den Golang-Port übersetzt.)

Faktotum
quelle
0

Eines der Protokollierungsmodule, das Sie berücksichtigen können, ist klog . Es unterstützt die V-Protokollierung, die die Flexibilität bietet, auf einer bestimmten Ebene zu protokollieren

Klog ist eine Gabel aus Glog und überwindet folgende Nachteile

  • Glog stellt viele "Fallstricke" dar und bringt Herausforderungen in containerisierten Umgebungen mit sich, die alle nicht gut dokumentiert sind.
  • glog bietet keine einfache Möglichkeit, Protokolle zu testen, was die Stabilität der Software beeinträchtigt, die es verwendet
  • glog basiert auf C ++ und klog ist eine reine Golang-Implementierung

Beispielimplementierung

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
quelle