Más información sobre cómo iniciar sesión en Go

Completado

Los registros desempeñan un papel importante en un programa, ya que se convierten en un origen de información que se puede comprobar cuando se produce algún problema. Normalmente, cuando se produce un error, los usuarios finales solo ven un mensaje que indica un problema en el programa. Desde el punto de vista del desarrollador, necesitamos más información que un mensaje de error simple. Esto se debe principalmente a que queremos reproducir el problema para escribir una corrección adecuada. En este módulo, aprenderá cómo funciona el registro en Go. También aprenderá algunas prácticas que siempre debe implementar.

Paquete log

En el caso de los principiantes, Go ofrece un sencillo paquete estándar para trabajar con registros. Se puede usar de forma similar a cómo se usa el paquete fmt. El paquete estándar no proporciona niveles de registro y no permite configurar registradores independientes para cada paquete. Si tiene que escribir configuraciones de registro más complejas, puede hacerlo mediante el uso de una plataforma de registro. Más adelante trataremos las plataformas de registro.

Esta es la forma más sencilla de usar registros:

import (
    "log"
)

func main() {
    log.Print("Hey, I'm a log!")
}

Al ejecutar el código anterior, se obtiene esta salida:

2020/12/19 13:39:17 Hey, I'm a log!

De forma predeterminada, la función log.Print() incluye la fecha y la hora como prefijo del mensaje de registro. Puede obtener el mismo comportamiento mediante fmt.Print(), pero puede hacer otras cosas con el paquete log, como el envío de registros a un archivo. Más adelante veremos otra funcionalidad del paquete log.

Puede usar la función log.Fatal() para registrar un error y finalizar el programa como si hubiera usado os.Exit(1). Para probarlo, utilicemos este fragmento de código:

package main

import (
    "fmt"
    "log"
)

func main() {
    log.Fatal("Hey, I'm an error log!")
    fmt.Print("Can you see me?")
}

Al ejecutar el código anterior, se obtiene esta salida:

2020/12/19 13:53:19  Hey, I'm an error log!
exit status 1

Observe que la última línea, fmt.Print("Can you see me?"), no se ejecuta. Esto se debe a que la llamada a la función log.Fatal() detiene el programa. Se obtiene un comportamiento similar al usar la función log.Panic(), que también llama a la función panic() de la siguiente manera:

package main

import (
    "fmt"
    "log"
)

func main() {
    log.Panic("Hey, I'm an error log!")
    fmt.Print("Can you see me?")
}

Al ejecutar el código anterior, se obtiene esta salida:

2020/12/19 13:53:19  Hey, I'm an error log!
panic: Hey, I'm an error log!

goroutine 1 [running]:
log.Panic(0xc000060f58, 0x1, 0x1)
        /usr/local/Cellar/go/1.15.5/libexec/src/log/log.go:351 +0xae
main.main()
        /Users/christian/go/src/helloworld/logs.go:9 +0x65
exit status 2

Todavía recibe el mensaje de registro, pero ahora también obtiene el seguimiento de la pila de errores.

Otra función esencial es log.SetPrefix(). Puede utilizarla para agregar un prefijo a los mensajes de registro del programa. Por ejemplo, podría utilizar este fragmento de código:

package main

import (
    "log"
)

func main() {
    log.SetPrefix("main(): ")
    log.Print("Hey, I'm a log!")
    log.Fatal("Hey, I'm an error log!")
}

Al ejecutar el código anterior, se obtiene esta salida:

main(): 2021/01/05 13:59:58 Hey, I'm a log!
main(): 2021/01/05 13:59:58 Hey, I'm an error log!
exit status 1

Establezca el prefijo una vez y los registros incluirán información como el nombre de la función de la que procede el registro.

Puede explorar otras funciones en el sitio web de Go.

Registro en un archivo

Además de imprimir registros en la consola, es posible que quiera enviar registros a un archivo para poder procesarlos más adelante o en tiempo real.

¿Por qué quiere enviar los registros a un archivo? En primer lugar, es posible que quiera ocultar información específica a los usuarios finales. Puede que no les interesen o que se exponga información confidencial. Si tiene registros en archivos, puede centralizar todos los registros en una sola ubicación y correlacionarlos con otros eventos. Este patrón es típico: tener aplicaciones distribuidas que puedan ser efímeras, como los contenedores.

Vamos a usar el código siguiente para probar el envío de registros a un archivo:

package main

import (
    "log"
    "os"
)

func main() {
    file, err := os.OpenFile("info.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
    if err != nil {
        log.Fatal(err)
    }

    defer file.Close()

    log.SetOutput(file)
    log.Print("Hey, I'm a log!")
}

Al ejecutar el código anterior, no verá nada en la consola. En el directorio, debería ver un nuevo archivo llamado info.log que contiene los registros que envió mediante la función log.Print(). Observe que debe empezar por crear o abrir un archivo y luego configurar el paquete log para enviar toda la salida a un archivo. Después, puede seguir usando la función log.Print() como lo haría normalmente.

Marcos de registro

Por último, puede haber ocasiones en las que las funciones del paquete log no sean suficientes. Puede que le resulte útil usar una plataforma de registro en lugar de escribir sus propias bibliotecas. Algunas plataformas de registro para Go son Logrus, Zerolog, Zap y Apex.

Vamos a explorar lo que podemos hacer con Zerolog.

En primer lugar, debe instalar el paquete. Si ha estado trabajando en esta serie, probablemente ya use los módulos de Go, por lo que no tiene que hacer nada. Por si acaso, puede ejecutar este comando en la estación de trabajo para instalar las bibliotecas de Zerolog:

go get -u github.com/rs/zerolog/log

Ahora, use este fragmento de código para probarlo:

package main

import (
    "github.com/rs/zerolog"
    "github.com/rs/zerolog/log"
)

func main() {
    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
    log.Print("Hey! I'm a log message!")
}

Al ejecutar el código anterior, se obtiene esta salida:

{"level":"debug","time":1609855453,"message":"Hey! I'm a log message!"}

Observe que solo tiene que incluir los nombres de importación correctos y luego puede seguir usando la función log.Print() como lo haría normalmente. Además, observe cómo la salida cambia al formato JSON. JSON es un formato útil para los registros cuando se ejecutan búsquedas en una ubicación centralizada.

Otra característica útil es que puede incluir los datos de contexto rápidamente de la siguiente manera:

package main

import (
    "github.com/rs/zerolog"
    "github.com/rs/zerolog/log"
)

func main() {
    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix

    log.Debug().
        Int("EmployeeID", 1001).
        Msg("Getting employee information")

    log.Debug().
        Str("Name", "John").
        Send()
}

Al ejecutar el código anterior, se obtiene esta salida:

{"level":"debug","EmployeeID":1001,"time":1609855731,"message":"Getting employee information"}
{"level":"debug","Name":"John","time":1609855731}

Observe que se ha agregado como contexto el identificador de empleado. Se convierte en parte de la línea de registro como otra propiedad. Además, es importante destacar que los campos que se incluyen están fuertemente tipados.

Puede implementar otras características con Zerolog, como el uso del registro nivelado, el uso de seguimientos de la pila con formato y el uso de más de una instancia del registrador para administrar diferentes salidas. Para obtener más información, consulte el sitio de GitHub.