6 min read

[Jornada do DevOps] #6 - Loggings em Golang

[Jornada do DevOps] #6 - Loggings em Golang
Photo by Chris Liverani / Unsplash

No último artigo, estudamos sobre ORMs com Golang - [Jornada do DevOps] #5 - ORMs em Golang.

Neste artigo vamos estudar sobre os Loggins no Golang. E seguindo o roadmap do Go, em relação a Loggins são indicados Logrus e o Zap.

Quando se trata de desenvolvimento de produtos, o registro desempenha um papel vital na identificação de problemas, avaliação de desempenho e conhecimento do status do processo dentro do aplicativo.

Na maioria das vezes, esperamos que o registrador nos forneça informações como nível de log, registro de data e hora, mensagem de erro, rastreamento de pilha que possa identificar a linha do código que atinge a exceção e assim por diante.

Logrus

O Logrus é um registrador estruturado para Go (golang), API totalmente compatível com o registrador de biblioteca padrão.

Logrus está em modo de manutenção. No repositório oficial é informado que não sera apresentado novos recursos.

Vendo isto, já vamos direto para o ZAP.

Zap

Zap é um pacote de registro dos desenvolvedores da Uber. Ele é anunciado como 'Logging rápido, estruturado e nivelado em Go'.

Este pacote supostamente resolve todos os problemas que tivemos com o pacote de log padrão do Go.

Ele não apenas fornece uma maneira flexível de registrar mensagens, mas também é o pacote de registro mais rápido disponível para aplicativos Golang.

Aqui está como ele se compara aos outros pacotes de log do Golang.

Log estruturado em Golang com Zap

O Zap vem com 2 tipos diferentes de registradores, dependendo do seu caso de uso.

Em aplicativos em que o desempenho não é tão crítico, você pode usar o SuggeredLogger (que ainda é 4 a 10 vezes mais rápido que os outros registradores estruturados).

Ele suporta tanto o registro estruturado quanto o registro normal do printf também!

Nos casos em que o desempenho da aplicação é crítico, é utilizado o Logger da Zap. É muito mais rápido que o SuggeredLogger, mas suporta apenas o registro estrutural.

Vamos ver alguns exemplos de código sobre como implementar o log de estrutura em Golang com Zap e fazer algumas modificações para personalizar um pouco mais esse incrível registrador!

Pacote de registro padrão do Golang – explicado

Por padrão, o Golang vem com um pacote de registro padrão que é bastante simples de usar.

Ele pode registrar as mensagens no console e também em um arquivo externo.

Você pode encontrar o pacote de registro aqui – https://pkg.go.dev/log

Introdução ao registro estruturado em Golang com Zap

Para esta implementação, usarei o VSCode como meu IDE para desenvolver o protótipo Golang. Vamos começar criando uma nova pasta e abrindo a pasta com o VSCode.

Inicialize o aplicativo Go criando um novo arquivo main.go e execute o comando a seguir.

go mod init logging-exemplo

Aqui estou usando dentro do repositório aprendendo-go/loggings.

Isso criaria o arquivo go.mod para você no diretório.

Em seguida, vamos instalar o pacote Zap executando o seguinte comando go get.

go get -u go.uber.org/zap

Antes de começar com a implementação, este artigo também se concentra em práticas limpas durante o desenvolvimento do aplicativo Golang.

Dito isto, em termos de desenvolvimento de aplicativos, os pacotes de log geralmente são algo externo e podem ser tratados como um utilitário para todo o aplicativo que pode ser alternado, se necessário.

Isso pode ser feito movendo o código de inicialização e uso do registrador para um pacote diferente do pacote principal.

Então, vamos criar uma nova pasta chamada utils e criar um novo arquivo chamado logger.go nela.

Logando no console com Zap em Golang

Em primeiro lugar, é assim que o arquivo logger.go ficaria com o mínimo de código de inicialização.

Agora, nosso requisito é bastante simples, registrar as mensagens no console de maneira estruturada.

package utils
import (
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)
var Logger *zap.Logger
func InitializeLogger() {
	logger, _ = zap.NewProduction()
}

Observe que o nome do pacote é 'utils'. Isso garante que a instância do registrador possa ser usada em todo o aplicativo, uma vez inicializada a partir do ponto de entrada do pacote principal, que é a função principal.

Linha 7 a 9, esta função será chamada pela função main que inicializaria a instância do Zap logger.

Agora, navegue até a função principal no arquivo go e cole algo semelhante. Observe que sua seção de importação será diferente da que eu tenho.

package main
import (
	"github.com/iammukeshm/structured-logging-golang-zap/utils"
	"go.uber.org/zap"
)
func main() {
	utils.InitializeLogger()
	utils.Logger.Info("Hello World")
	utils.Logger.Error("Não habilitado.",zap.String("url", "codewithmukesh.com"))
}

A linha 7 chama a função Initialization do pacote logger que escrevemos anteriormente.

A linha 8 adiciona um log de nível de informação simples.

A linha 9 adiciona um log de nível de erro. Observe que o método Error pode receber um número n de argumentos. O primeiro argumento deve ser sempre a mensagem de erro, seguida por vários argumentos zap que denotarão campos no JSON de log resultante. Neste caso, vamos retornar uma exceção simples que diz que o aplicativo não consegue acessar uma determinada URL. Para entender melhor, vamos executar o aplicativo Golang e verificar.

Esta é a saída que você veria no terminal.

Você pode ver que obtemos o nível de log, timestamp (que está em uma codificação diferente), o arquivo que está chamando o método de log, a mensagem, o rastreamento de pilha e os outros parâmetros que passamos ao chamar essa função de log. Além disso, observe que as mensagens estão em formatos JSON.

Agora, vamos adicionar algumas personalizações no código e torná-lo mais real para produção. Primeiro, vamos fazer o logger gravar em um arquivo em vez de no console.

Logar no arquivo com Zap em Golang

Você teria que fazer essas modificações no método Initialize.

func Initialize() {
	config := zap.NewProductionEncoderConfig()
	config.EncodeTime = zapcore.ISO8601TimeEncoder
	fileEncoder := zapcore.NewJSONEncoder(config)
	logFile, _ := os.OpenFile("log.json", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	writer := zapcore.AddSync(logFile)
	defaultLogLevel := zapcore.DebugLevel
	core := zapcore.NewTee(
		zapcore.NewCore(fileEncoder, writer, defaultLogLevel),
	)
	logger = zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))
}

A linha 2 cria uma nova configuração para o codificador zap.

A linha 3 define o codificador de hora para seguir o formato de hora ISO padrão.

A linha 4 cria um codificador de arquivo que segue a codificação JSON padrão.

A linha 5 abre o log.json no modo Create and Append.

A linha 6 cria um gravador de arquivos que será usado posteriormente pelo zap para gravar as mensagens no arquivo.

Na linha 7, simplesmente criamos uma variável que diz que o nível de log padrão estará em Debug.

A linha 8-10 adiciona o gravador de arquivo, codificador e nível de log padrão à instância principal do zap.

A linha 11 cria uma nova instância do Zap passando as configurações criadas anteriormente. Ele também adiciona outras opções, como incluir o chamador de log e o rastreamento de stack (para os erros).

Vamos executar e verificar.

Você pode ver que ele cria um arquivo log.json e adiciona o JSON correspondente ao erro que acionamos junto com o rastreamento completo do erro.

Registrando no arquivo e no console com ZAP em Golang

Mas agora, se você notar, o aplicativo não registra mais no console. Existe uma maneira de fazer o Zap logar em várias áreas, tanto no arquivo quanto no console.

Para fazer isso, talvez tenhamos que modificar a função Initialize novamente da seguinte maneira, adicionando as linhas de código:

func Initialize() {
	config := zap.NewProductionEncoderConfig()
        config.EncodeTime = zapcore.ISO8601TimeEncoder
	fileEncoder := zapcore.NewJSONEncoder(config)
	consoleEncoder := zapcore.NewConsoleEncoder(config)
	logFile, _ := os.OpenFile("text.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	writer := zapcore.AddSync(logFile)
	defaultLogLevel := zapcore.DebugLevel
	core := zapcore.NewTee(
		zapcore.NewCore(fileEncoder, writer, defaultLogLevel),
		zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), defaultLogLevel),
	)
	logger = zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))
}

Execute o aplicativo novamente e veja o console.

Pode haver alguns casos de uso real como, preciso registrar apenas os erros no arquivo e registrar todo o resto no console.

Como você faria isso? Basta alterar o nível de log padrão e passar valores diferentes para diferentes zapcores que você criou nas linhas 10 e 11. Entendeu?

Resumo

Então, neste artigo da Jornada do DevOps, aprendemos sobre Logging Estruturado em Golang com Zap.

Também aprendemos algumas coisas sobre como organizar o Golang Project para garantir que o logger também seja facilmente acessível por outros pacotes.

Fora isso, aprendemos sobre como escrever logs no console, arquivos e adicionamos algumas personalizações a ele.

Se você tiver sugestão de Loggins (registradores) no Golang, deixe nos comentários! PLEASE!

Compartilhe este artigo com seus colegas e círculos de desenvolvedores se você achou isso interessante.

Lembrando que nosso próximo estudo é sobre Real Time Comunication, aguarde pelo [Jornada do DevOps] #7 - Real Time Communication (Melody e Centrifugo) em Golang.

Até a próxima!