[Jornada do DevOps] #6 - Loggings em Golang
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.

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!