Enfoque correcto del registro global en Golang

119

¿Cuál es el patrón para el registro de aplicaciones en Go? Si tengo, digamos, 5 rutinas de gor de las que necesito iniciar sesión, ¿debería ...

  • ¿Crear uno log.Loggery compartirlo?
  • ¿Pasar un puntero a eso log.Logger?
  • ¿Cada gorutina o función debe crear un registrador?
  • ¿Debo crear el registrador como una variable global?
Carson
fuente

Respuestas:

59
  • Cree un solo registro. ¿Logger y distribuirlo?

Eso es posible. Un log.Logger se puede utilizar simultáneamente desde múltiples goroutines.

  • Pase un puntero a ese registro.

log.New devuelve un *Loggerque suele ser una indicación de que debe pasar el objeto como puntero. Pasarlo como valor crearía una copia de la estructura (es decir, una copia del Logger) y luego múltiples goroutines podrían escribir en el mismo io.Writer simultáneamente. Eso podría ser un problema serio, dependiendo de la implementación del escritor.

  • ¿Cada gorutina o función debe crear un registrador?

No crearía un registrador separado para cada función o goroutine. Goroutines (y funciones) se utilizan para tareas muy ligeras que no justificarán el mantenimiento de un registrador separado. Probablemente sea una buena idea crear un registrador para cada componente más grande de su proyecto. Por ejemplo, si su proyecto utiliza un servicio SMTP para enviar correos, la creación de un registrador separado para el servicio de correo parece una buena idea para que pueda filtrar y apagar la salida por separado.

  • ¿Debo crear el registrador como una variable global?

Eso depende de tu paquete. En el ejemplo de servicio de correo anterior, probablemente sería una buena idea tener un registrador para cada instancia de su servicio, de modo que los usuarios puedan registrar fallas mientras usan el servicio de correo de Gmail de manera diferente a las fallas que ocurrieron al usar el MTA local (por ejemplo, sendmail ).

tux21b
fuente
37

Para los casos simples, hay un registrador global definida en el paquete de registro, log.Logger. Este registrador global se puede configurar mediante log.SetFlags.

Luego, uno puede simplemente llamar a las funciones de nivel superior del paquete de registro como log.Printfy log.Fatalf, que usan esa instancia global.

zzzz
fuente
Aunque puede configurar las banderas, no puede usar un registrador personalizado.
0xcaff
@caffinatedmonkey en realidad, puede usar registradores personalizados si implementan la io.Writerinterfaz y cambia la salida del registrador predeterminado a través de SetOutput().
congusbongus
16

Este es un registrador simple

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

Puedes usarlo de esta manera

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

Sé que esta pregunta es un poco antigua, pero si, como yo, sus proyectos están formados por varios archivos más pequeños, voto por su cuarta opción: he creado un logger.goque es parte del paquete principal. Este archivo go crea el registrador, lo asigna a un archivo y lo proporciona al resto de main. Tenga en cuenta que no he encontrado una forma elegante de cerrar el registro de errores ...

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
fuente
8
Para un cierre elegante, probablemente podría defer errorlog.Close()al final de la ejecución, o para asegurarse mejor de que esté cerrado, configurar manejadores de señales usando el paquete de señales de Go golang.org/pkg/os/signal
Anfernee
4

Esta es una pregunta anterior, pero me gustaría sugerir el uso de http://github.com/romana/rlog (que desarrollamos). Se configura a través de variables de entorno, el objeto del registrador se crea e inicializa cuando se importa rlog. Por lo tanto, no es necesario pasar un registrador.

rlog tiene bastantes características:

  • Sellos de fecha / hora totalmente configurables
  • Salida simultánea a stderr o stdout, así como a archivo.
  • Niveles de registro estándar (depuración, información, etc.), así como registro de varios niveles configurable libremente.
  • Registro bajo demanda de la información de la persona que llama (archivo, número de línea, función).
  • Posibilidad de establecer diferentes niveles de registro para diferentes archivos de origen.

Es muy pequeño, no tiene dependencias externas, excepto la biblioteca estándar de Golang y se está desarrollando activamente. Se proporcionan ejemplos en el repositorio.

Juergen Brendel
fuente
3
¡Gracias por revelar su afiliación con el producto que está recomendando! Es apreciado.
Robert Columbia
2

Encontré el paquete de registro predeterminado ( https://golang.org/pkg/log/ ) algo limitante. Por ejemplo, no se admite la información frente a los registros de depuración.
Después de hurgar un poco, decidió usar https://github.com/golang/glog . Este parece ser un puerto de https://github.com/google/glog y brinda una flexibilidad decente en el registro. Por ejemplo, al ejecutar una aplicación localmente, es posible que desee un registro de nivel DEBUG, pero es posible que desee ejecutar solo en el nivel INFO / ERROR en producción. La lista de funciones / guía completas está aquí https://google-glog.googlecode.com/svn/trunk/doc/glog.html (es para el módulo c ++, pero en su mayor parte se traduce al puerto golang)

factótum
fuente
0

Uno de los módulos de registro que puede considerar es klog . Admite el registro en 'V', que brinda la flexibilidad de registrar en cierto nivel

klog es una bifurcación de glog y supera los siguientes inconvenientes

  • glog presenta muchos "errores" e introduce desafíos en entornos en contenedores, los cuales no están bien documentados.
  • glog no proporciona una manera fácil de probar los registros, lo que resta estabilidad al software que lo usa
  • glog está basado en C ++ y klog es una implementación pura de golang

Implementación de muestra

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
fuente