Approccio corretto alla registrazione globale a Golang


119

Qual è il modello per l'accesso dell'applicazione in Go? Se ho, diciamo, 5 goroutine da cui devo accedere, dovrei ...

  • Creare un singolo log.Loggere passarlo in giro?
  • Passare un puntatore a quello log.Logger?
  • Ogni goroutine o funzione dovrebbe creare un logger?
  • Devo creare il logger come variabile globale?

Risposte:


59
  • Creare un unico log logger e passarlo in giro?

È possibile. Un log.Logger può essere utilizzato contemporaneamente da più goroutine.

  • Passa un puntatore a quel registro.

log.New restituisce un *Loggerche di solito è un'indicazione che dovresti passare l'oggetto come puntatore. Passarlo come valore creerebbe una copia della struttura (cioè una copia del Logger) e quindi più goroutine potrebbero scrivere contemporaneamente sullo stesso io.Writer . Potrebbe essere un problema serio, a seconda dell'implementazione di chi scrive.

  • Ogni goroutine o funzione dovrebbe creare un logger?

Non creerei un logger separato per ogni funzione o goroutine. Le Goroutine (e le funzioni) sono usate per compiti molto leggeri che non giustificherebbero la manutenzione di un logger separato. Probabilmente è una buona idea creare un logger per ogni componente più grande del tuo progetto. Ad esempio, se il progetto utilizza un servizio SMTP per l'invio di posta, la creazione di un logger separato per il servizio di posta sembra una buona idea in modo da poter filtrare e disattivare separatamente l'output.

  • Devo creare il logger come variabile globale?

Dipende dal tuo pacco. Nell'esempio del servizio di posta precedente, sarebbe probabilmente una buona idea avere un logger per ogni istanza del servizio, in modo che gli utenti possano registrare gli errori durante l'utilizzo del servizio di posta Gmail in modo diverso rispetto agli errori che si sono verificati durante l'utilizzo dell'MTA locale (ad esempio sendmail ).


37

Per i casi semplici, c'è un logger globale definito nel pacchetto di registro, log.Logger. Questo logger globale può essere configurato tramite log.SetFlags.

Successivamente si possono semplicemente chiamare le funzioni di primo livello del pacchetto di log come log.Printfe log.Fatalf, che usano quell'istanza globale.


Pensando che puoi impostare i flag non puoi usare un logger personalizzato.
0xcaff

@caffinatedmonkey in realtà, puoi utilizzare logger personalizzati se implementano l' io.Writerinterfaccia e modifichi l'output del logger predefinito tramite SetOutput().
congusbongus

16

Questo è un semplice 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),
    }
}

Puoi usarlo in questo modo

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

10

So che questa domanda è un po 'vecchia, ma se, come me, i tuoi progetti sono costituiti da più file più piccoli, voto per la tua quarta opzione: ne ho creata una logger.goche fa parte del pacchetto main. Questo file go crea il logger, lo assegna a un file e lo fornisce al resto di main. Nota non ho trovato un modo grazioso per chiudere il registro degli errori ...

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

8
Per una chiusura aggraziata, potresti probabilmente defer errorlog.Close()alla fine dell'esecuzione, o per assicurarti meglio che sia chiusa, impostare i gestori di segnale utilizzando il pacchetto di segnali di Go golang.org/pkg/os/signal
Anfernee

4

Questa è una vecchia domanda, ma vorrei suggerire l'uso di http://github.com/romana/rlog (che abbiamo sviluppato). Viene configurato tramite variabili di ambiente, l'oggetto logger viene creato e inizializzato quando viene importato rlog. Pertanto, non è necessario passare in giro un logger.

rlog ha alcune caratteristiche:

  • Indicatori di data / ora completamente configurabili
  • Output simultaneo su stderr o stdout e file.
  • Livelli di registro standard (debug, informazioni, ecc.) E registrazione multilivello liberamente configurabile.
  • Registrazione su richiesta delle informazioni sul chiamante (file, numero di riga, funzione).
  • Possibilità di impostare diversi livelli di registro per diversi file di origine.

È molto piccolo, non ha dipendenze esterne, eccetto la libreria Golang standard ed è attivamente in fase di sviluppo. Gli esempi sono forniti nel repo.


3
Grazie per aver rivelato la tua affiliazione con il prodotto che stai raccomandando! È apprezzato.
Robert Columbia

2

Ho trovato il pacchetto di log predefinito ( https://golang.org/pkg/log/ ) piuttosto limitante. Ad esempio, nessun supporto per info e log di debug.
Dopo un po 'di ricerche, ho deciso di utilizzare https://github.com/golang/glog . Questo sembra essere un port di https://github.com/google/glog e offre una discreta flessibilità nella registrazione. Ad esempio, quando si esegue un'applicazione in locale, è possibile che si desideri un log a livello di DEBUG ma che venga eseguita solo a livello di INFO / ERRORE in produzione. L'elenco delle funzionalità / guida complete è qui https://google-glog.googlecode.com/svn/trunk/doc/glog.html (è per il modulo c ++, ma per la maggior parte si traduce nella porta golang)


0

Uno dei moduli di registrazione che puoi considerare è klog . Supporta la registrazione 'V' che offre la flessibilità di accedere a un certo livello

klog è un fork di glog e supera i seguenti inconvenienti

  • glog presenta molti "trucchi" e introduce sfide in ambienti containerizzati, che non sono tutti ben documentati.
  • glog non fornisce un modo semplice per testare i log, il che sminuisce la stabilità del software che lo utilizza
  • glog è basato su C ++ e klog è una pura implementazione di golang

Implementazione di esempio

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()
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.