Golang Logging Configuration with Zap: Practical Implementation Tips
Ronny Medina
Posted on May 17, 2024
Hi everybody, continuing from the previous post, which was a basic setup with Golang. In this part, we're setting up our logging in the app.
THE ORIGINAL POST HAS ADDITIONAL INFORMATION.
Installing the package zap
https://github.com/uber-go/zap
In our project, we need to install the zap package. To do this, put the following command in our terminal.
go get -u go.uber.org/zap
If you're using Docker like me, you need to enter the container. So, if you're running the app in the background, execute this command.
docker exec -it demogolang bash
To run the app in the background, use the command: docker-compose up -d.
The name 'demogolang' is declared inside the docker-compose.yml file.
Setting up the logging
Dockerfile
In our Dockerfile add 2 environment variables and created a folder to save the logs.
APP_LOG_LEVEL: The app log level could be debug or error. We use the debug level when we're in development and error when the app is in production mode. So, if you're in error mode when you print logs Info or Debug, these are not shown in the terminal. If you want to use more levels, you can edit the function to get the current level.
APP_LOG_FOLDER: This is the path where you place the log file. You can set whatever path you want.
ENV APP_LOG_LEVEL debug
ENV APP_LOG_FOLDER /tmp/logs/
RUN mkdir -p ${APP_LOG_FOLDER}
Reading environment variables
Inside our app
folder, I created a folder called config. Inside config, I put the file called envs.go
.
package config
import "os"
var APP_ENV = os.Getenv("APP_ENV")
var IS_DEVELOP_MODE = APP_ENV == "develop"
var APP_LOG_LEVEL = os.Getenv("APP_LOG_LEVEL")
var APP_LOG_FOLDER = os.Getenv("APP_LOG_FOLDER")
Logger file
package utils
import (
"example/demogo/config"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var Logger *zap.SugaredLogger
func getLevelLogger(level string) zapcore.Level {
if level == "debug" {
return zap.DebugLevel
}
return zap.ErrorLevel
}
func init() {
var err error
level := zap.NewAtomicLevelAt(getLevelLogger(config.APP_LOG_LEVEL))
encoder := zap.NewProductionEncoderConfig()
zapConfig := zap.NewProductionConfig()
zapConfig.EncoderConfig = encoder
zapConfig.Level = level
zapConfig.Development = config.IS_DEVELOP_MODE
zapConfig.Encoding = "json"
zapConfig.InitialFields = map[string]interface{}{"idtx": "999"}
zapConfig.OutputPaths = []string{"stdout", config.APP_LOG_FOLDER + "app_log.log"}
zapConfig.ErrorOutputPaths = []string{"stderr"}
logger, err := zapConfig.Build()
if err != nil {
panic(err)
}
Logger = logger.Sugar()
}
I created a global variable called Logger
; this will be set when the package is initialized. The function getLevelLogger
is used to get the current level from the environment variable.
The init function is automatically called before the main function.
- encoder: Is the log format, how you want to apply the format.
- InitialFields: The values that you want to always be in the log.
- OutputPaths: The output of the file.
- ErrorOutputPaths: To print error log in the terminal.
Finally our main.go
.
package main
import (
"example/demogo/utils"
"time"
)
func main() {
defer utils.Logger.Sync()
for {
time.Sleep(5 * time.Second)
utils.Logger.Infoln("hello world")
}
}
It's important to call defer utils.Logger.Sync() in the entry point of our app. Normally ensures that all buffered log messages are sent to their final destination if the application crashes.
docker-compose up -d --build
docker logs -f demogolang
Posted on May 17, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.