Golang Logging Configuration with Zap: Practical Implementation Tips
Tags: beginners, programming, tutorial, go
Published: 2024-05-17 14:03:14

Hi everybody, continuing from the previous [post](https://edulixir.com/2024/05/13/basic-setup-for-projects-with-golang/), which was a basic setup with Golang. In this part, we're setting up our logging in the app.

THE [ORIGINAL POST](https://edulixir.com/2024/05/17/golang-logging-configuration-with-zap-practical-implementation-tips/) HAS ADDITIONAL INFORMATION.

### Installing the package zap

[https://github.com/uber-go/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.

```bash
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.


```bash
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.

```Dockerfile

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`.

```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

```go

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`.

```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.


```bash
docker-compose up -d --build

docker logs -f demogolang
```