Abordagem correta para registro global em Golang

119

Qual é o padrão para o login do aplicativo no Go? Se eu tenho, digamos, 5 goroutines de que preciso fazer login, devo ...

  • Criar um single log.Loggere distribuí-lo?
  • Passar um ponteiro para isso log.Logger?
  • Cada goroutine ou função deve criar um logger?
  • Devo criar o logger como uma variável global?
Carson
fonte

Respostas:

59
  • Criar um único log.Logger e distribuí-lo?

Isso é possível. Um log.Logger pode ser usado simultaneamente a partir de vários goroutines.

  • Passe um ponteiro para esse log.Logger?

log.New retorna um *Loggerque geralmente é uma indicação de que você deve passar o objeto como um ponteiro. Passá-lo como valor criaria uma cópia da estrutura (ou seja, uma cópia do Logger) e, em seguida, várias goroutines poderiam gravar no mesmo io.Writer simultaneamente. Isso pode ser um problema sério, dependendo da implementação do escritor.

  • Cada goroutine ou função deve criar um logger?

Eu não criaria um logger separado para cada função ou goroutine. Goroutines (e funções) são usadas para tarefas muito leves que não justificam a manutenção de um logger separado. Provavelmente é uma boa ideia criar um registrador para cada componente maior de seu projeto. Por exemplo, se o seu projeto usa um serviço SMTP para enviar e-mails, criar um registrador separado para o serviço de e-mail parece uma boa ideia para que você possa filtrar e desligar a saída separadamente.

  • Devo criar o logger como uma variável global?

Isso depende do seu pacote. No exemplo de serviço de e-mail anterior, provavelmente seria uma boa ideia ter um registrador para cada instância do seu serviço, de modo que os usuários possam registrar falhas ao usar o serviço de e-mail do gmail de forma diferente das falhas que ocorreram durante o uso do MTA local (por exemplo, sendmail )

tux21b
fonte
37

Para casos simples, existe um logger global definido no pacote de log log.Logger,. Este logger global pode ser configurado por meio log.SetFlags.

Depois disso, pode-se apenas chamar as funções de nível superior do pacote de log como log.Printfe log.Fatalf, que usam essa instância global.

zzzz
fonte
Pensei que você pode definir os sinalizadores, você não pode usar um logger personalizado.
0xcaff
@caffinatedmonkey na verdade, você pode usar loggers customizados se eles implementarem a io.Writerinterface e você alterar a saída do logger padrão via SetOutput().
congusbongus
16

Este é um registrador simples

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

Você pode usá-lo desta forma

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

Sei que essa questão é um pouco antiga, mas se, como eu, seus projetos são compostos de vários arquivos menores, eu voto na sua 4ª opção - criei um logger.goque faz parte do pacote principal. Este arquivo go cria o logger, atribui-o a um arquivo e o fornece ao resto do main. Observação: não criei uma maneira elegante de fechar o log de erros ...

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
fonte
8
Para um fechamento harmonioso, você provavelmente poderia defer errorlog.Close()no final da execução, ou para melhor garantir seu fechamento, configurar manipuladores de sinal usando o pacote de sinal de Go golang.org/pkg/os/signal
Anfernee
4

Esta é uma pergunta antiga, mas gostaria de sugerir o uso de http://github.com/romana/rlog (que desenvolvemos). Ele é configurado através de variáveis ​​de ambiente, o objeto logger é criado e inicializado quando o rlog é importado. Portanto, não há necessidade de passar um logger.

rlog tem alguns recursos:

  • Carimbos de data / hora totalmente configuráveis
  • Saída simultânea para stderr ou stdout, bem como arquivo.
  • Níveis de log padrão (Debug, Info, etc.), bem como log de vários níveis configurável livremente.
  • Registro sob demanda de informações do chamador (arquivo, número de linha, função).
  • Capacidade de definir diferentes níveis de log para diferentes arquivos de origem.

É muito pequeno, não tem dependências externas, exceto a biblioteca Golang padrão e está sendo desenvolvido ativamente. Exemplos são fornecidos no repo.

Juergen Brendel
fonte
3
Obrigado por divulgar sua afiliação com o produto que você está recomendando! É apreciado.
Robert Columbia
2

Achei o pacote de log padrão ( https://golang.org/pkg/log/ ) um tanto limitante. Por exemplo, nenhum suporte para informações vs. logs de depuração.
Depois de algumas pesquisas, decida-se em usar https://github.com/golang/glog . Esta parece ser uma porta de https://github.com/google/glog e oferece uma flexibilidade decente no registro. Por exemplo, ao executar um aplicativo localmente, você pode desejar o nível de log DEBUG, mas pode querer executar apenas no nível INFO / ERROR na produção. A lista completa de recursos / guia está aqui https://google-glog.googlecode.com/svn/trunk/doc/glog.html (é para o módulo c ++, mas na maioria das vezes se traduz na porta golang)

faz-tudo
fonte
0

Um dos módulos de registro que você pode considerar é o klog . Suporta registro 'V' que dá a flexibilidade de registrar em certo nível

klog é um fork do glog e supera as seguintes desvantagens

  • glog apresenta muitas "pegadinhas" e desafios em ambientes em contêineres, todos os quais não estão bem documentados.
  • O glog não fornece uma maneira fácil de testar os logs, o que diminui a estabilidade do software que o usa
  • glog é baseado em C ++ e klog é uma implementação de golang pura

Implementação de amostra

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
fonte
-1

Convido você a revisar meus blogs, onde explico o uso de LOG com GOlang: https://su9.co/9BAE74B

Exemplo: // Se declara la variable Log. Esta será usada para registrar los eventos. var (Log * log.Logger = Loggerx ())

func Loggerx() *log.Logger {
    LOG_FILE_LOCATION := os.Getenv("LOG_FILE_LOCATION")
        //En el caso que la variable de entorno exista, el sistema usa la configuración del docker.
    if LOG_FILE_LOCATION == "" {
        LOG_FILE_LOCATION = "../logs/" + APP_NAME + ".log"
    } else {
        LOG_FILE_LOCATION = LOG_FILE_LOCATION + APP_NAME + ".log"
    }
    flag.Parse()
        //Si el archivo existe se rehusa, es decir, no elimina el archivo log y crea uno nuevo.
    if _, err := os.Stat(LOG_FILE_LOCATION); os.IsNotExist(err) {
        file, err1 := os.Create(LOG_FILE_LOCATION)
        if err1 != nil {
            panic(err1)
        }
                //si no existe,se crea uno nuevo.
        return log.New(file, "", log.Ldate|log.Ltime|log.Lshortfile)
    } else {
                //si existe se rehusa.
        file, err := os.OpenFile(LOG_FILE_LOCATION, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
        if err != nil {
            panic(err)
        }
        return log.New(file, "", log.Ldate|log.Ltime|log.Lshortfile)
    }
}
Jose G. Mejía
fonte