DEV Community

Cover image for Cómo escribir archivos de log en Go
Fabián Karaben
Fabián Karaben

Posted on

Cómo escribir archivos de log en Go

Logging en sistemas UNIX

UNIX tiene sus propios archivos de log para escribir información que viene de los servidores y programas en ejecución. En la mayoría de los sistemas UNIX estos archivos se pueden encontrar en el directorio/var/log.Sin embargo los archivos de log de algunos servicios populares comoApacheoNginxse pueden encontrar en cualquier lugar, dependiendo de su configuración.

Registrar y almacenar información de log en archivos es una forma práctica de examinar datos e información de tu software de forma asincrónica, ya sea localmente, en un servidor de log central o utilizando otros software como Elasticsearch, Beats y Grafana Loki.

El logging service de UNIX admite dos propiedades denominadaslogging levelylogging facility.Ellogging leveles un valor que especifica la gravedad de la entrada de logging. Hay varios niveles, incluidosdebug,info,notice,warning,err,crit,alert,yemerg,en orden inverso de gravedad.

El paquete de logging de la biblioteca estándar de Go no admite trabajar conlogging levels.

Lalogging facilityes como una categoría utilizada para registrar información. El valor de la parte delogging facilitypuede serauth,authpriv,cron,daemon,kern,lpr,mail,mark,news,syslog,user,
UUCP,local0,local1,local2,local3,local4,local5,local6olocal7,y se define dentro de/etc/syslog.conf,/etc/rsyslog.confu otro archivo apropiado dependiendo del proceso del servidor utilizado para el logging del sistema en tu máquina UNIX. Esto significa que si una logging facility no se define correctamente, no se manejará; por lo tanto, los mensajes de logging que le envíe podrían ignorarse y, por lo tanto, perderse.

Logging al trabajar en Go

El paquetelogenvía mensajes de log a la salida de error estándar del sistema. Parte del paqueteloges el paquetelog/syslog,que te permite enviar mensajes de log al servidorsyslogde tu máquina. Aunque de forma predeterminada el log se escribe en la salida de error estándar, el uso delog.SetOutput()modifica ese comportamiento. La lista de funciones para enviar datos de registro incluyelog.Printf(),log.Print(),log.Println(),log.Fatalf(),log.Fatalln(),log.Panic(),log.Panicln()ylog.Panicf().

El logging es para el código de la aplicación, no para el código de bibliotecas. Si estás desarrollando bibliotecas, no realices logging en ellas.

Escribir en el archivo de log principal del sistema es tan fácil como llamar asyslog.New()con la opciónsyslog.LOG_SYSLOG.Después de eso, debes decirle a tu programa en Go que toda la información de log va al nuevo registrador; esto se implementa con una llamada a la funciónlog.SetOutput().El proceso se ilustra en el siguiente código.

packagemain

import(
"log"
"log/syslog"
)

funcmain(){
sysLog,err:=syslog.New(syslog.LOG_SYSLOG,"systemLog.go")

iferr!=nil{
log.Println(err)
return
}else{
log.SetOutput(sysLog)
log.Print("Everything is fine!")
}
}
Enter fullscreen mode Exit fullscreen mode

La ejecución de este código no genera ningún resultado. Sin embargo, si ejecutasjournalctl -xeen una máquina Linux, podrás ver entradas como las siguientes:

Jun 08 20:46:05 thinkpad systemLog.go[4412]: 2023/06/08 20:46:05
Everything is fine!
Jun 08 20:46:51 thinkpad systemLog.go[4822]: 2023/06/08 20:46:51
Everything is fine!

Enter fullscreen mode Exit fullscreen mode

Funcioneslog.Fatal()ylog.Panic()

La funciónlog.Fatal()se utiliza cuando sucede algo erróneo y solo deseas salir de tu programa lo antes posible después de informar esa mala situación. La llamada alog.Fatal()finaliza un programa Go en el punto donde se llamó alog.Fatal()después de imprimir un mensaje de error.

Hay situaciones en las que un programa está a punto de fallar definitivamente y deseas tener la mayor cantidad de información posible sobre la falla;log.Panic()implica que algo realmente inesperado y desconocido, como no poder encontrar un archivo, ha sucedido. De manera análoga a la funciónlog.Fatal(),log.Panic()imprime un mensaje personalizado e inmediatamente finaliza el programa Go.

Veamos un ejemplo de la utilización de ambas funciones.

packagemain

import(
"log"
"os"
)

funcmain(){
iflen(os.Args)!=1{
log.Fatal("Fatal: Hello World!")
}
log.Panic("Panic: Hello World!")
}
Enter fullscreen mode Exit fullscreen mode

La ejecución sin argumentos del código anterior produce la siguiente salida:

$ go run logs.go
2023/06/08 20:48:42 Panic: Hello World!
panic: Panic: Hello World!
goroutine 1 [running]:
log.Panic({0xc000104f60?, 0x0?, 0x0?})
/usr/lib/go/src/log/log.go:384 +0x65
main.main()
/home/mtsouk/code/mGo4th/ch01/logs.go:12 +0x85
exit status 2
Enter fullscreen mode Exit fullscreen mode

Escribir en un archivo de los personalizado

La mayoría de las veces, y especialmente en aplicaciones y servicios que se implementan en producción, es necesario escribir los datos de log en un archivo.

Veamos un ejemplo.

packagemain

import(
"fmt"
"log"
"os"
"path"
)

funcmain(){
LOGFILE:=path.Join(os.TempDir(),"mGo.log")
fmt.Println(LOGFILE)
f,err:=os.OpenFile(LOGFILE,os.O_APPEND|os.O_CREATE|os.O_WRONLY,0644)
iferr!=nil{
fmt.Println(err)
return
}
deferf.Close()

iLog:=log.New(f,"iLog",log.LstdFlags)
iLog.Println("Hello there!")
iLog.Println("Mastering Go 4th edition!")
}
Enter fullscreen mode Exit fullscreen mode

La llamada aos.OpenFile()crea el archivo de log para escritura, si aún no existe, o lo abre para escritura agregando nuevos datos al final (os.O_APPEND).

Las últimas tres declaraciones crean un nuevo archivo de log basado en un archivo abierto (f) y escriben dos mensajes en él, usandoPrintln().

Añadir el número de línea a las entradas de log

La funcionalidad deseada se implementa con el uso delog.Lshortfileen los parámetros delog.New()oSetFlags().El indicadorlog.Lshortfileagrega el nombre del archivo así como el número de línea de la instrucción Go que imprimió la entrada de log en la propia entrada del registro. Si usaslog.Llongfileen lugar delog.Lshortfile,obtendrás la ruta completa del archivo fuente de Go.

Veamos un ejemplo.

packagemain

import(
"fmt"
"log"
"os"
"path"
)

funcmain(){
LOGFILE:=path.Join(os.TempDir(),"mGo.log")
fmt.Println(LOGFILE)
f,err:=os.OpenFile(LOGFILE,os.O_APPEND|os.O_CREATE|os.O_WRONLY,0644)
iferr!=nil{
fmt.Println(err)
return
}
deferf.Close()

LstdFlags:=log.Ldate|log.Lshortfile
iLog:=log.New(f,"LNum",LstdFlags)
iLog.Println("Mastering Go, 4th edition!")
iLog.SetFlags(log.Lshortfile|log.LstdFlags)
iLog.Println("Another log entry!")
}
Enter fullscreen mode Exit fullscreen mode

Escribir en múltiples salidas de log

La funciónio.MultiWriter()es la que nos permite escribir en múltiples destinos, que en este caso son un archivo llamadomyLog.logy la salida de error estándar.

packagemain

import(
"fmt"
"io"
"log"
"os"
)

funcmain(){
flag:=os.O_APPEND|os.O_CREATE|os.O_WRONLY
file,err:=os.OpenFile("myLog.log",flag,0644)
iferr!=nil{
fmt.Println(err)
os.Exit(0)
}
deferfile.Close()

w:=io.MultiWriter(file,os.Stderr)
logger:=log.New(w,"myApp:",log.LstdFlags)
logger.Printf("BOOK %d",os.Getpid())
}
Enter fullscreen mode Exit fullscreen mode

Conclusión

Si bien existen paquetes de la comunidad para todo tipo de funcionalidades extra para enriquecer nuestro logging, almacenar las entradas remotamente, etc.; el paquetelogde la biblioteca estándar es muy interesante y en muchas ocaciones es todo lo que necesitamos para nuestros desarrollos en Go.

Top comments(0)