Golang Logging Configuration with Zap: Practical Implementation Tips

RMAG news

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

Leave a Reply

Your email address will not be published. Required fields are marked *