Approche correcte de l'exploitation forestière mondiale à Golang


120

Quel est le modèle de journalisation des applications dans Go? Si j'ai, disons, 5 goroutines dont je dois me connecter, devrais-je ...

  • Créer un single log.Loggeret le faire circuler?
  • Passer un pointeur à cela log.Logger?
  • Chaque goroutine ou fonction doit-il créer un enregistreur?
  • Dois-je créer l'enregistreur en tant que variable globale?

Réponses:


59
  • Créer un seul journal. Logger et le faire circuler?

C'est possible. Un log.Logger peut être utilisé simultanément à partir de plusieurs goroutines.

  • Faites passer un pointeur vers ce journal.

log.New renvoie un *Loggerqui indique généralement que vous devez passer l'objet en tant que pointeur. Le passer en tant que valeur créerait une copie de la structure (c'est-à-dire une copie du Logger) et plusieurs goroutines pourraient écrire simultanément dans le même io.Writer . Cela peut être un problème sérieux, selon l'implémentation de l'écrivain.

  • Chaque goroutine ou fonction doit-il créer un enregistreur?

Je ne créerais pas un enregistreur séparé pour chaque fonction ou goroutine. Les goroutines (et fonctions) sont utilisées pour des tâches très légères qui ne justifieront pas la maintenance d'un enregistreur séparé. C'est probablement une bonne idée de créer un enregistreur pour chaque composant plus important de votre projet. Par exemple, si votre projet utilise un service SMTP pour envoyer des e-mails, la création d'un enregistreur distinct pour le service de messagerie semble être une bonne idée afin que vous puissiez filtrer et désactiver la sortie séparément.

  • Dois-je créer l'enregistreur en tant que variable globale?

Cela dépend de votre colis. Dans l'exemple de service de messagerie précédent, ce serait probablement une bonne idée d'avoir un enregistreur pour chaque instance de votre service, afin que les utilisateurs puissent enregistrer les échecs lors de l'utilisation du service de messagerie gmail différemment des échecs survenus lors de l'utilisation du MTA local (par exemple, sendmail ).


37

Pour les cas simples, il y a un enregistreur global défini dans le package journal, log.Logger. Cet enregistreur global peut être configuré via log.SetFlags.

Ensuite, on peut simplement appeler les fonctions de niveau supérieur du package de journaux comme log.Printfet log.Fatalf, qui utilisent cette instance globale.


Je pensais que vous pouvez définir les indicateurs, vous ne pouvez pas utiliser un enregistreur personnalisé.
0xcaff

@caffinatedmonkey en fait, vous pouvez utiliser des enregistreurs personnalisés s'ils implémentent l' io.Writerinterface et que vous modifiez la sortie de l'enregistreur par défaut via SetOutput().
congusbongus

16

Ceci est un simple enregistreur

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

Vous pouvez l'utiliser de cette manière

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

Je sais que cette question est un peu ancienne, mais si, comme moi, vos projets sont constitués de plusieurs fichiers plus petits, je vote pour votre 4ème option - j'ai créé un logger.goqui fait partie du package main. Ce fichier go crée le logger, l'assigne à un fichier et le fournit au reste de main. Notez que je n'ai pas trouvé de moyen élégant de fermer le journal des erreurs ...

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
Pour une fermeture gracieuse, vous pourriez probablement defer errorlog.Close()à la fin de l'exécution, ou pour mieux assurer sa fermeture, configurer des gestionnaires de signaux en utilisant le paquetage de signaux de Go golang.org/pkg/os/signal
Anfernee

4

C'est une question plus ancienne, mais je voudrais suggérer l'utilisation de http://github.com/romana/rlog (que nous avons développé). Il est configuré via des variables d'environnement, l'objet logger est créé et initialisé lorsque rlog est importé. Par conséquent, pas besoin de faire circuler un enregistreur.

rlog a pas mal de fonctionnalités:

  • Horodatage entièrement configurable
  • Sortie simultanée vers stderr ou stdout ainsi que fichier.
  • Niveaux de journalisation standard (Debug, Info, etc.) ainsi que journalisation à plusieurs niveaux librement configurable.
  • Enregistrement à la demande des informations de l'appelant (fichier, numéro de ligne, fonction).
  • Possibilité de définir différents niveaux de journalisation pour différents fichiers source.

Il est très petit, n'a pas de dépendances externes, à l'exception de la bibliothèque standard de Golang et est en cours de développement. Des exemples sont fournis dans le repo.


3
Merci de révéler votre affiliation avec le produit que vous recommandez! C'est apprécié.
Robert Columbia

2

J'ai trouvé le package de journaux par défaut ( https://golang.org/pkg/log/ ) quelque peu limitatif. Par exemple, aucune prise en charge des journaux d'informations par rapport aux journaux de débogage.
Après quelques fouilles, j'ai décidé d'utiliser https://github.com/golang/glog . Cela semble être un port de https://github.com/google/glog et donne une flexibilité décente dans la journalisation. Par exemple, lors de l'exécution d'une application localement, vous souhaiterez peut-être un journal de niveau DEBUG, mais vous souhaiterez peut-être l'exécuter uniquement au niveau INFO / ERROR en production. La liste complète des fonctionnalités / guide est, ici https://google-glog.googlecode.com/svn/trunk/doc/glog.html (C'est pour le module c ++, mais pour la plupart se traduit par le port golang)


0

L'un des modules de journalisation que vous pouvez envisager est klog . Il prend en charge la journalisation en `` V '' qui donne la flexibilité de se connecter à un certain niveau

klog est un fork de glog et surmonte les inconvénients suivants

  • glog présente beaucoup de «pièges» et introduit des défis dans les environnements conteneurisés, qui ne sont pas tous bien documentés.
  • glog ne fournit pas un moyen facile de tester les journaux, ce qui nuit à la stabilité du logiciel qui l'utilise
  • glog est basé sur C ++ et klog est une implémentation pure de golang

Exemple d'implémentation

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()
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.