A iniciar sessão: Escolhendo um sistema e usando-o
Você é relativamente novo na linguagem Go. Você provavelmente está usando-o para escrever uma aplicação web ou um servidor, e você precisa criar um arquivo de log. Então, você faz uma rápida pesquisa na web e descobre que há uma tonelada de opções para o log in go. Como você sabe qual deles escolher? Este artigo irá equipá-lo para responder a essa pergunta.
Daremos uma olhada no pacote incorporado log
e determinaremos para que projetos ele é adequado antes de explorar outras soluções de registro que são predominantes no ecossistema Go.
O que registrar
Não preciso lhe dizer o quão importante é o registro. Os logs são usados por cada aplicação web de produção para ajudar os programadores e operações:
- Busas de ponto no código da aplicação
- Descobrir problemas de desempenho
- Fazer análise post-mortem de interrupções e incidentes de segurança
Os dados que realmente regista dependerão do tipo de aplicação que está a construir. Na maioria das vezes, você terá alguma variação no seguinte:
- A marcação temporal para quando um evento ocorreu ou um log foi gerado
- Níveis de log como depuração, erro ou informação
- Dados contextuais para ajudar a compreender o que aconteceu e tornar possível reproduzir facilmente a situação
O que não deve registar
Em geral, não deve registar qualquer forma de dados comerciais sensíveis ou informação pessoalmente identificável. Isto inclui, mas não está limitado a:
- Nomes
- Endereços IP
- Números de cartões de crédito
Estas restrições podem tornar os registos menos úteis do ponto de vista da engenharia, mas tornam a sua aplicação mais segura. Em muitos casos, regulamentos como GDPR e HIPAA podem proibir o registro de dados pessoais.
Introduzindo o pacote de log
A biblioteca padrão Go tem um pacote incorporado log
que fornece a maioria das características básicas de log. Embora não tenha níveis de registo (como depuração, aviso ou erro), ainda fornece tudo o que precisa para obter uma estratégia básica de registo configurada.
Aqui está o exemplo mais básico de registo:
package mainimport "log"func main() { log.Println("Hello world!")}
O código acima imprime o texto “Olá mundo!” para o erro padrão, mas também inclui a data e hora, que é útil para filtrar mensagens de log por data.
2019/12/09 17:21:53 Hello world!
Por padrão, o pacote
log
imprime para o fluxo de saída do erro padrão (stderr
), mas você pode fazê-lo escrever em arquivos locais ou qualquer destino que suporte a interfaceio.Writer
. Ele também adiciona um carimbo de tempo à mensagem de log sem qualquer configuração adicional.
Logging to a file
Se você precisar armazenar mensagens de log em um arquivo, você pode fazer isso criando um novo arquivo ou abrindo um arquivo existente e definindo-o como a saída do log. Aqui está um exemplo:
package mainimport ( "log" "os")func main() { // If the file doesn't exist, create it or append to the file file, err := os.OpenFile("logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) if err != nil { log.Fatal(err) } log.SetOutput(file) log.Println("Hello world!")}
Quando executamos o código, o seguinte é escrito para logs.txt.
2019/12/09 17:22:47 Hello world!
Como mencionado anteriormente, você pode basicamente emitir seus logs para qualquer destino que implemente a interface io.Writer
, para que você tenha muita flexibilidade ao decidir onde registrar mensagens na sua aplicação.
Criar loggers personalizados
Embora o pacote log
implemente um logger
pré-definido que escreve para o erro padrão, nós podemos criar tipos de logger personalizados usando o método log.New()
.
Ao criar um novo logger, você precisa passar em três argumentos para log.New()
:
-
out
: Qualquer tipo que implemente a interfaceio.Writer
, que é onde os dados de log serão escritos para -
prefix
: Uma string que é anexada ao início de cada linha de log -
flag
: Um conjunto de constantes que nos permitem definir quais propriedades de log a incluir em cada entrada de log gerada pelo logger (mais sobre isso na próxima seção)
Podemos tirar proveito dessa característica para criar loggers personalizados. Aqui está um anexo que implementa Info
, Warning
e Error
loggers:
package mainimport ( "log" "os")var ( WarningLogger *log.Logger InfoLogger *log.Logger ErrorLogger *log.Logger)func init() { file, err := os.OpenFile("logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) if err != nil { log.Fatal(err) } InfoLogger = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) WarningLogger = log.New(file, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile) ErrorLogger = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)}func main() { InfoLogger.Println("Starting the application...") InfoLogger.Println("Something noteworthy happened") WarningLogger.Println("There is something you should know about") ErrorLogger.Println("Something went wrong")}
Após criar ou abrir o arquivo logs.txt
no topo da função init
, nós então inicializamos os três loggers definidos fornecendo o destino de saída, a string de prefixo e as flags de log.
Na função main
, os registradores são utilizados chamando a função Println
, que escreve uma nova entrada de log para o arquivo de log. Quando você executar este programa, o seguinte será escrito em logs.txt
.
INFO: 2019/12/09 12:01:06 main.go:26: Starting the application...INFO: 2019/12/09 12:01:06 main.go:27: Something noteworthy happenedWARNING: 2019/12/09 12:01:06 main.go:28: There is something you should know aboutERROR: 2019/12/09 12:01:06 main.go:29: Something went wrong
Note que neste exemplo, nós estamos registrando em um único arquivo, mas você pode usar um arquivo separado para cada logger passando um arquivo diferente ao criar o logger.
Log flags
Você pode usar constantes de log flags para enriquecer uma mensagem de log fornecendo informações adicionais de contexto, como o arquivo, número de linha, data e hora. Por exemplo, passando a mensagem “Algo deu errado” através de um logger com uma combinação de flag mostrada abaixo:
log.Ldate|log.Ltime|log.Lshortfile
Imprimir
2019/12/09 12:01:06 main.go:29: Something went wrong
Felizmente, não há controle sobre a ordem em que elas aparecem ou o formato em que são apresentadas.
Introduzindo frameworks de logging
Usar o pacote log
é ótimo para o desenvolvimento local quando obter feedback rápido é mais importante do que gerar logs ricos e estruturados. Além disso, você provavelmente estará melhor usando um framework de logging.
Uma grande vantagem de usar um framework de registro é que ele ajuda a padronizar os dados de registro. Isto significa que:
- É mais fácil de ler e compreender os dados de log.
- É mais fácil reunir logs de várias fontes e alimentá-los com uma plataforma central para serem analisados.
Além disso, o log é praticamente um problema resolvido. Porquê reinventar a roda?
Escolher um framework de logging
Decidir qual framework usar pode ser um desafio, pois existem várias opções para escolher de.
Os dois frameworks de logging mais populares para Go aparecem para beglog e logrus. A popularidade do glog é surpreendente, já que não é atualizado há vários anos. logrus é melhor mantido e usado em projetos populares como o Docker, por isso vamos nos concentrar nele.
Começar com logrus
Instalar logrus é tão simples como executar o comando abaixo no seu terminal:
go get "github.com/Sirupsen/logrus"
Uma coisa ótima sobre logrus é que ele é completamente compatível com o pacote log
da biblioteca padrão, então você pode substituir suas importações de log em qualquer lugar por log "github.com/sirupsen/logrus"
e ele só vai funcionar!
Vamos modificar o nosso exemplo anterior de “olá mundo” que usava o pacote logrus em vez disso:
package mainimport ( log "github.com/sirupsen/logrus")func main() { log.Println("Hello world!")}
Executar este código produz a saída:
INFO Hello world!
Não podia ser mais fácil!
Logging in JSON
logrus
é bem adequado para logs estruturados no JSON o que – como o JSON é um padrão bem definido – torna fácil para os serviços externos analisarem seus logs e também torna a adição de contexto a uma mensagem de log relativamente simples através do uso de campos, como mostrado abaixo:
package mainimport ( log "github.com/sirupsen/logrus")func main() { log.SetFormatter(&log.JSONFormatter{}) log.WithFields( log.Fields{ "foo": "foo", "bar": "bar", }, ).Info("Something happened")}
A saída de log gerada será um objeto JSON que inclui a mensagem, nível de log, carimbo da hora e campos incluídos.
{"bar":"bar","foo":"foo","level":"info","msg":"Something happened","time":"2019-12-09T15:55:24+01:00"}
Se você não estiver interessado em emitir seus logs como JSON, esteja ciente de que existem vários formatadores de terceiros para logrus, que você pode visualizar em sua página do Github. Você pode até mesmo escrever seu próprio formatador se preferir.
Níveis de log
Não como o pacote de log padrão, logrus suporta níveis de log.
logrus tem sete níveis de log: Trace, Debug, Info, Warn, Error, Fatal, and Panic. A severidade de cada nível aumenta conforme você vai descendo na lista.
log.Trace("Something very low level.")log.Debug("Useful debugging information.")log.Info("Something noteworthy happened!")log.Warn("You should probably take a look at this.")log.Error("Something failed but I'm not quitting.")// Calls os.Exit(1) after logginglog.Fatal("Bye.")// Calls panic() after logginglog.Panic("I'm bailing.")
Ao definir um nível de log em um logger, você pode registrar apenas as entradas necessárias dependendo do seu ambiente. Por padrão, logrus irá registrar qualquer coisa que seja Info ou acima (Warn, Error, Fatal, ou Panic).
package mainimport ( log "github.com/sirupsen/logrus")func main() { log.SetFormatter(&log.JSONFormatter{}) log.Debug("Useful debugging information.") log.Info("Something noteworthy happened!") log.Warn("You should probably take a look at this.") log.Error("Something failed but I'm not quitting.")}
Executar o código acima irá produzir a seguinte saída:
{"level":"info","msg":"Something noteworthy happened!","time":"2019-12-09T16:18:21+01:00"}{"level":"warning","msg":"You should probably take a look at this.","time":"2019-12-09T16:18:21+01:00"}{"level":"error","msg":"Something failed but I'm not quitting.","time":"2019-12-09T16:18:21+01:00"}
Note que a mensagem de nível de Debug não foi impressa. Para incluí-la nos logs, defina log.Level
para igualar log.DebugLevel
:
log.SetLevel(log.DebugLevel)
Embrulhar
Neste post, exploramos o uso do pacote de log embutido e estabelecemos que ele só deve ser usado para aplicações triviais ou na construção de um protótipo rápido. Para tudo o resto, o uso de um framework de log mainstream é uma necessidade.
>
Também examinamos formas de garantir que as informações contidas nos yourlogs sejam consistentes e fáceis de analisar, especialmente ao agregá-las em uma plataforma centralizada.
>
Pancelas para leitura!
Leave a Reply