Pular para o conteúdo principal

victorstein.dev

38/100 Dias de Golang - Logging

Table of Contents

# Logging

Outro biblioteca padrão da linguagem Go! Muito simples de usar e cobre a maior parte dos casos de uso comuns — sem precisar instalar nada! Vamos ver o nosso primeiro log

package main

import (
	"log"
)

func main() {
	log.Println("Olá, mundo!")
}

Veja que a saída é automaticamente formatada com o timestamp atual

2025/04/19 15:36:27 Olá, mundo!

Uma funcionalidade muito legal que eu encontrei é o log para no caso de um erro. A função log.Fatal imprime a mensagem e depois chama os.Exit(1) — ou seja, o programa termina imediatamente

if err != nil {
	log.Fatal("Algo deu errado:", err)
}

Podemos fazer várias customizações no formato do log

log.SetPrefix("[meuapp] ")
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

log.Println("Rodando...")
[meuapp] 2025/04/19 15:56:27 main.go:10: Rodando...

Vamos ver um exemplo de como imprimir o log em um arquivo. Veja como é simples, criamos um arquivo nesse caso o app.log. E quando criarmos o log com a função New passamos esse arquivo, e depois é tudo igual. A função log.New cria um novo logger personalizado.

package main

import (
	"log"
	"os"
)

func main() {
	file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatal("Erro ao abrir arquivo de log:", err)
	}
	defer file.Close()

	logger := log.New(file, "[APP] ", log.LstdFlags|log.Lshortfile)
	logger.Println("Aplicação iniciada")
}

Uma das grandes vantagens da biblioteca padrão log em Go é que todos os métodos do tipo *log.Logger são seguros para uso simultâneo por múltiplas goroutines.

Isso significa que você pode chamar logger.Println, de várias goroutines ao mesmo tempo sem precisar de mutexes ou canais para sincronização.

Veja esse exemplo:

package main

import (
	"log"
	"os"
	"sync"
)

func main() {
	logger := log.New(os.Stdout, "[LOG] ", log.LstdFlags|log.Lmicroseconds)
	var wg sync.WaitGroup

	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			for j := 0; j < 3; j++ {
				logger.Printf("Goroutine %d - Mensagem %d", id, j)
			}
		}(i)
	}

	wg.Wait()
}
[LOG] 2025/04/19 23:46:42.831649 Goroutine 4 - Mensagem 0
[LOG] 2025/04/19 23:46:42.831831 Goroutine 4 - Mensagem 1
[LOG] 2025/04/19 23:46:42.831837 Goroutine 4 - Mensagem 2
[LOG] 2025/04/19 23:46:42.831683 Goroutine 1 - Mensagem 0
[LOG] 2025/04/19 23:46:42.831849 Goroutine 1 - Mensagem 1
[LOG] 2025/04/19 23:46:42.831852 Goroutine 1 - Mensagem 2
[LOG] 2025/04/19 23:46:42.831697 Goroutine 0 - Mensagem 0
[LOG] 2025/04/19 23:46:42.831860 Goroutine 0 - Mensagem 1
[LOG] 2025/04/19 23:46:42.831864 Goroutine 0 - Mensagem 2
[LOG] 2025/04/19 23:46:42.831691 Goroutine 2 - Mensagem 0
[LOG] 2025/04/19 23:46:42.831875 Goroutine 2 - Mensagem 1
[LOG] 2025/04/19 23:46:42.831880 Goroutine 2 - Mensagem 2
[LOG] 2025/04/19 23:46:42.831713 Goroutine 3 - Mensagem 0
[LOG] 2025/04/19 23:46:42.831911 Goroutine 3 - Mensagem 1
[LOG] 2025/04/19 23:46:42.831916 Goroutine 3 - Mensagem 2

Internamente, o pacote log protege o acesso ao writer com um mutex. Sempre que chamamos Println, Printf, Fatal, o logger adquire esse mutex, escreve, e libera. Isso evita o race condition e não precisamos nos preocupar em chamar mutex.