Como utilizar logs estruturados com log/slog

Lançado recentemente, o Go 1.21 trouxe um novo package padrão na linguagem, o log/slog. Embora Go tenha um package de logs desde seu lançamento, ao longo dos anos, ficou claro que as aplicações Go precisavam de logs estruturados.

Diferente do formato padrão em texto puro, logs estruturados utilizam o formato chave=valor, facilitando muito na hora de fazer parse, buscar e analisar os logs.

Embora existam vários packages open source que desempenham esse papel, sendo o logrus um dos mais famosos, todos os anos, a comunidade continuava pedindo ao time do Go que essa funcionalidade fosse adicionada ao package oficial da linguagem.

Nesse post, vou explorar um pouco desse package, mostrando as principais funções e formas de utilização.

Leia mais »

Como utilizar o framework Echo

Mesmo sem saber ainda se será uma série ou não, nesse post, trago uma visão geral sobre um dos frameworks mais populares do mundo Go, o Echo.

Em sua documentação, o framework Echo se autodenomina como de alta performance, extensível e minimalista. Além dessas características, podemos dizer também que é de fácil implementação. Veja o exemplo abaixo.

package main

import (
    "net/http"
    
    "github.com/labstack/echo/v4"
)

func main() {
    e := echo.New()
    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello, World!")
    })
    e.Logger.Fatal(e.Start(":1323"))
}
Leia mais »

O que é e como utilizar protocol buffers

Protocol buffers, protobuf ou simplesmente proto, é uma linguagem criada pela Google para serialização de dados. Para facilitar o entendimento, podemos comparar o protobuf com XML ou JSON. No entanto, dados serializados com proto, são MUITO menores quando comparados com as outras duas tecnologias.

Outro ponto importante é que, após escrever um arquivo proto, utilizamos um programa chamado protoc para gerar código em Go, Java, Python, C#, C, C++, entre outras linguagens. Código esse que contém todas as classes e métodos que estiverem declarados dentro dos arquivos proto.

Mas calma! Antes de falar mais sobre esse código gerado, vamos entender o que compõe e como criar um arquivo proto.

Syntax

A primeira coisa que precisamos fazer em um arquivo proto é definir sua syntax. Essa syntax nada mais é do que a versão do protobuf que iremos utilizar.

Leia mais »
man love people woman

Como criar templates para arquivos JSON, YAML e TXT

Um package padrão do Go e que, no meu ponto de vista, é pouco explorado, é o text/template. Com a ajuda desse package, podemos preencher os valores em qualquer template no formato texto, como por exemplo, arquivos JSON, YAML e TXT.

Para exemplificar melhor, vamos criar um arquivo YAML com o nome template.yaml e o seguinte conteúdo.

name: {{ .Name }}
age: {{ .Age }}

Com o template criado, podemos iniciar nosso main.go.

package main

func main() {
}
Leia mais »
hanging decor lot

O que são e como utilizar tags em structs

Uma feature pouco comentada porém muito útil na linguagem Go, pelo menos no meu ponto de vista, é a tag.

As tags são marcações que colocamos nas propriedades de uma struct. Essas marcações funcionam como metadata para outros packages poderem realizar operações.

Para ficar mais claro, vamos criar um struct comum.

type Pessoa struct {
  Nome      string
  Documento string
  Idade     uint8
}
Leia mais »

Escrevendo logs em formato JSON com zerolog

Algum tempo atrás fizemos um post falando sobre como escrever logs em arquivos. No post em questão, logamos algumas mensagens de forma bem simples. Normalmente esses logs em texto puro são um pouco complicados para filtrar mensagens, já que é preciso muito regex.

Nesse post vamos abordar os logs de uma forma diferente, pois vamos escreve-los em formato JSON.

Para nos auxiliar nessa tarefa, vamos utilizar um package chamado zerolog. Esse package foi criado pelo Olivier Poitrey, atual diretor de engenharia do Netflix.

Gosto muito desse package pois ele é fácil de usar e tem uma performance muito boa.

Sem mais enrolação, vamos instalar o package e começar a brincar com ele.

$ go get -u github.com/rs/zerolog/log
Leia mais »

Implementando uma API com Fiber

Embora eu não tenha dito no post anterior, semana passada iniciamos uma nova série aqui no blog onde vamos implementar uma API bem simples em alguns frameworks web.

Dando continuidade a essa série, hoje vamos implementar uma API utilizando o Fiber, um framework inspirado no Express do Node e que trabalha com Fasthttp ao invés do net/http.

Como podemos ver no gráfico abaixo, uma de suas características mais marcantes é a performance.

Leia mais »

Implementando uma API com gorilla/mux

Sem dúvidas um dos routers mais famosos e utilizados no mundo Golang, hoje vamos ver como implementar uma API utilizando gorilla/mux.

Para quem não conhecer, gorilla/mux ajuda na hora de fazer o match da URL que está sendo chamada com a função que vai tratar aquela URL. Além dessa facilidade, um dos principais benefícios de se utilizar gorilla/mux é que ele implementa a interface http.Handler nativa do Go.

Sem mais delongas, vamos começar implementar nossa API instalando o gorilla/mux.

$ go get -u github.com/gorilla/mux
Leia mais »

Carregando configurações com Viper

Na semana passada, falamos sobre como carregar configurações de um arquivo TOML.

Hoje vamos ver como carregar configurações de arquivos JSON, TOML, YAML, HCL, envfile e Java properties usando uma única dependência, Viper.

Viper é um package utilizado por grande projetos como Hugo, Docker Notary e doctl.

Além de ler todos os tipos de arquivo mencionado acima, ele também consegue ler variáveis de ambiente, etcd, Consul, flags de linhas de comando e buffers.

Sem mais delongas, vamos iniciar nosso tutorial instalando essa dependência em nosso projeto.

$ go get github.com/spf13/viper

Agora que já temos a dependência em nossa máquina, vamos criar um arquivo JSON com nome configs.json para armazenar nossas configurações.

{
  "databases": {
    "mysql": {
      "host": "localhost",
      "port": 3306,
      "user": "admin",
      "pass": "1234abc?"
    },
    "mongodb": {
      "host": "localhost",
      "port": 27017,
      "user": "logs",
      "pass": "log123"
    }
  },
  "environment": "production"
}

Feito isso, vamos criar um arquivo main.go para carregar as configurações. Como queremos carregar as configurações assim que o programa iniciar, vamos adicionar uma função init com as funções do Viper dentro dela.

func init() {
    viper.SetConfigName("configs") // nome do arquivo que queremos carregar
    viper.SetConfigType("json") // extensão do arquivo
    viper.AddConfigPath(".") // caminho onde está o arquivo
    viper.AddConfigPath("/etc/") // caminho alternativo onde está o arquivo
    err := viper.ReadInConfig() // lê o arquivo e carrega seu conteúdo
    if err != nil {
	panic(fmt.Errorf("Erro fatal no arquivo de configuração: %w \n", err))
    }
}

Essa função init é uma função reservado do Go que é chamada durante a inicialização do programa, ou seja, antes da função main.

E por falar em main, vamos criar nossa função main e printar o valor que temos na chave environment.

func main() {
    fmt.Println(viper.GetString("environment"))
}

Para recuperar valores que estão encadeados, tudo que precisamos fazer é chamar todas as chaves usando um . entre elas, como podemos ver no exemplo abaixo, onde vamos printar o host do mysql.

func main() {
    fmt.Println(viper.GetString("environment"))
    fmt.Println(viper.GetString("databases.mysql.host"))
}

No entanto, ficar acessando item por item de uma configuração de banco de dados ou algo mais complexo pode ser bem chato e repetitivo. Para mitigar esse problema, podemos criar uma struct e carregar toda a configuração direto para ela.

type MysqlCfg struct {
    Host string `json:"host"`
    Port int `json:"port"`
    User string `json:"user"`
    Pass string `json:"pass"`
}

func main() {
    fmt.Println(viper.GetString("environment"))
    fmt.Println(viper.GetString("databases.mysql.host"))

    b, err := json.Marshal(viper.Get("databases.mysql"))
    if err != nil {
	panic(err)
    }

    var mysql MysqlCfg
    err = json.Unmarshal(b, &mysql)
    if err != nil {
	panic(err)
    }

    fmt.Println(mysql)
}

Simples não?!

Nos dois exemplos, usamos funções do Viper para retornar valores carregados do arquivo de configuração, sendo o primeiro o GetString que retorna o dado como string e o segundo o Get, que retorna o dado como uma interface{}.

Mas é claro que não existem somente essas duas funções, por isso, abaixo vou deixar a lista de todas as funções que o Viper diponibiliza para recuperar valores do arquivo carregado.

  • Get(key string) : interface{}
  • GetBool(key string) : bool
  • GetFloat64(key string) : float64
  • GetInt(key string) : int
  • GetIntSlice(key string) : []int
  • GetString(key string) : string
  • GetStringMap(key string) : map[string]interface{}
  • GetStringMapString(key string) : map[string]string
  • GetStringSlice(key string) : []string
  • GetTime(key string) : time.Time
  • GetDuration(key string) : time.Duration
  • IsSet(key string) : bool
  • AllSettings() : map[string]interface{}

Para encerrar, vou mostrar uma função que nos ajuda a deixar valores padrão já configurado na aplicação, para caso a configuração não seja colocada no arquivo.

Vamos adicionar as seguintes linhas no final da nossa função init.

viper.SetDefault("environment", "development")
viper.SetDefault("databases.mysql.host", "localhost")
viper.SetDefault("databases.mongodb.host", "localhost")

Feito isso, remova as entradas do arquivo de configurações e execute o go run main.go.

Como você pode notar, ao executar o go run main.go os dois primeiros prints funcionaram normal, já o print da struct não exibiu o host.

Isso acontece pois só definimos um valor padrão para a chave databases.mysql.host e não para databases.mysql.

Espero que tenham gostado.

Deixem suas dúvidas nos comentários.

Até a próxima


Se inscreva na nossa newsletter

* indicates required