Golang Zap Logger Tutorial with Examples

Introduction to golang zap logger

In computing , logging is an act of keeping logs or messages of events that occur in a software.These messages are logged/written in a log file for developers to read and debug their code. The log file contains logs that make the bug spotting process easier. A standard log message can contain information such as timestamp, error messages.

 

Golang default logger

Go ships with a standard logging package that can log events in our software. It is quite simple to use and it can log messages on the console and a log file. In your working directory, create a main.file and add the below code.

Advertisement

Example

package main
 
import (
   "log"
   "os"
)
 
package main
 
import (
   "log"
   "os"
)
 
func main() {
   // Log to the console
   log.Println("Message to log in the console!")
   // Log in a log file
   file, err := os.OpenFile("log.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
   if err != nil {
       log.Fatal(err)
   }
   defer file.Close()
   log.SetOutput(file)
   log.Println("Message to log in a log file!")
}

Output in the console

$ go run main.go
2022/10/15 08:36:54 Message to log in the console!

Output in the log file

2022/10/15 08:36:54 Message to log in a log file!

Explanation

In the above example, we demonstrate how to log messages to both a log file and on the console. To log to the console, we use log.Println() and at this point the output location will be the console. To log to a log file, we first need to create  a log file with the correct permissions. Use the log.SetOutput() method to define the destination where messages will be logged. In our example, we set the log file (log.txt) as our output destination using the log.SetOutput(file) command. Any messages printed after this method call will be printed on the log file. When this code is run, a new log.txt file will be created next to the main.go file, and it will have the log messages generated in the main.go file.

As much as the Go standard logger is easy to implement , it has limited log levels.

Advertisement

 

Golang zap logger

Why Go Zap? Go Zap was developed and is being maintained by developers at Uber and it is fast, structured and provides leveled logging. Zap has two flavors for logging events namely zap and zap(sugared). The first flavor zap, it is mostly used in applications that prioritize performance because it is fast. It tasks 2900ns/op. On the other hand, zap(sugared) can be used in applications that do not prioritize performance. It takes 3475 ns/op and it is 20% faster compared with other logging packages, with the closest one Zerolog taking 10639 ns/op.

You can get more information about the performance.

Golang Zap Logger Tutorial with Examples

 

Implementing Zap logger

In this section we are going to prepare our work space.

  1. Create a working directory.
  2. Create a main.go file in your working directory.
  3. Initialize a go module.
  4. Install Go Zap package
$ mkdir zapLogger
$ cd zapLogger/
$ touch main.go
$ go mod init zapLogger
$ go get -u go.uber.org/zap

 

Configuring Zap

Zap supports 7 types of log levels namely Debug, Info, Warning, Error, DPanic, Panic and Fatal. Most of the time, you might want to define logger configuration , and zap allows you to define these configuration in a YAML or JSON file. These configurations can then be passed to the zap logger.

Example

package main
 
import (
   "encoding/json"
 
   "go.uber.org/zap"
)
 
func main() {
   sampleJSON := []byte(`{
       "level" : "info",
       "encoding": "json",
       "outputPaths":["stdout", "log.log"],
       "errorOutputPaths":["stderr"],
       "encoderConfig": {
           "messageKey":"message",
           "levelKey":"level",
           "levelEncoder":"lowercase"
       }
   }`)
 
   var cfg zap.Config
 
   if err := json.Unmarshal(sampleJSON, &cfg); err != nil {
       panic(err)
   }
 
   logger, err := cfg.Build()
 
   if err != nil {
       panic(err)
   }
   defer logger.Sync()
 
 
   logger.Info("INFO log level message")
   logger.Warn("Warn log level message")
   logger.Error("Error log level message")
}

Output in the console  and in the log.log file

{"level":"info","message":"INFO log level message"}
{"level":"warn","message":"Warn log level message"}
{"level":"error","message":"Error log level message"}

Explanation

In the above example, we define our logger configuration and store it in the sampeJSON variable. One thing to note is the outPutpaths key, which defines the “stdout” and “log.log” , which means the destination of the log messages. When you run this code a new log.log file will be created with log messages inside it.

 

Logging to console

It is possible to log on the console without defining your configuration.Logging to the console is quite simple. First of all you need to create a log object from the zap package and start using, as shown in the code sample below. Add this code sample in your main.go file.

Example

package main
 
import "go.uber.org/zap"
 
func main() {
   logger, _ := zap.NewProduction()
 
   logger.Info("INFO log level message")
   logger.Warn("Warn log level message")
   logger.Error("Error log level message")
  }

Output

{"level":"info","ts":1665814987.2550712,"caller":"zapLogger/main.go:8","msg":"INFO log level message"}
{"level":"warn","ts":1665814987.2551033,"caller":"zapLogger/main.go:9","msg":"Warn log level message"}
{"level":"error","ts":1665814987.255109,"caller":"zapLogger/main.go:10","msg":"Error log level message","stacktrace":"main.main\n\t/home/GolinuxCloud/Go Zap/zapLogger/main.go:10\nruntime.main\n\t/usr/local/go/src/runtime/proc.go:250"}

Explanation

Advertisement

In the above example, we initialize the zap object using the logger, _ := zap.NewProduction() command. The logger object is returned back and log levels can be used to log messages to the console by calling methods that correspond to the log level. For example, we used logger.Info() to log info log level message to the console.

The default log message contains the log level, timestamp, and the caller (our main.go file)

 

Logging to a log file

In this next example, we are going to use the zapcore package that implements a low-level interface upon which zap is built.

Example

package main
 
import (
   "os"
 
   "go.uber.org/zap"
   "go.uber.org/zap/zapcore"
)
 
func main() {
   filename := "logs.log"
   logger := fileLogger(filename)
 
   logger.Info("INFO log level message")
   logger.Warn("Warn log level message")
   logger.Error("Error log level message")
 
}
 
func fileLogger(filename string) *zap.Logger {
   config := zap.NewProductionEncoderConfig()
   config.EncodeTime = zapcore.ISO8601TimeEncoder
   fileEncoder := zapcore.NewJSONEncoder(config)
   logFile, _ := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
   writer := zapcore.AddSync(logFile)
   defaultLogLevel := zapcore.DebugLevel
   core := zapcore.NewTee(
       zapcore.NewCore(fileEncoder, writer, defaultLogLevel),
   )
 
   logger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))
 
   return logger
}

Output in a log file

{"level":"info","ts":"2022-10-15T11:41:22.310+0300","caller":"zapLogger/main.go:14","msg":"INFO log level message"}
{"level":"warn","ts":"2022-10-15T11:41:22.310+0300","caller":"zapLogger/main.go:15","msg":"Warn log level message"}
{"level":"error","ts":"2022-10-15T11:41:22.310+0300","caller":"zapLogger/main.go:16","msg":"Error log level message","stacktrace":"main.main\n\t/Golinuxcloud/Go Zap/zapLogger/main.go:16\nruntime.main\n\t/usr/local/go/src/runtime/proc.go:250"}

 

Logging to console and Log File (Both)

To be able to print on the console and write into log file simultaneously, we must add two encoders as shown below:

Advertisement
package main

import (
	"os"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

func main() {
	filename := "logs.log"
	logger := fileLogger(filename)

	logger.Info("INFO log level message")
	logger.Warn("Warn log level message")
	logger.Error("Error log level message")

}

func fileLogger(filename string) *zap.Logger {
	config := zap.NewProductionEncoderConfig()
	config.EncodeTime = zapcore.ISO8601TimeEncoder
	fileEncoder := zapcore.NewJSONEncoder(config)
	consoleEncoder := zapcore.NewConsoleEncoder(config)
	logFile, _ := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	writer := zapcore.AddSync(logFile)
	defaultLogLevel := zapcore.DebugLevel
	core := zapcore.NewTee(
		zapcore.NewCore(fileEncoder, writer, defaultLogLevel),
		zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), defaultLogLevel),
	)

	logger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))

	return logger
}

Output:

# go run main.go 
2022-10-17T23:41:08.984+0530    info    code1/main.go:14        INFO log level message
2022-10-17T23:41:08.987+0530    warn    code1/main.go:15        Warn log level message
2022-10-17T23:41:08.988+0530    error   code1/main.go:16        Error log level message
main.main
        /root/goexamples/code1/main.go:16
runtime.main
        /root/go/src/runtime/proc.go:250

 

Using SugaredLogger

In contexts where performance is nice, but not critical, use the SugaredLogger. It's 4-10x faster than other structured logging packages and includes both structured and printf-style APIs.

package main

import (
	"log"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

func main() {
	config := zap.NewProductionConfig()
	config.EncoderConfig.TimeKey = "timestamp"
	config.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout("Jan 02 15:04:05.000000000")
	config.EncoderConfig.StacktraceKey = "" // to hide stacktrace info

	logger, err := config.Build()
	if err != nil {
		log.Fatal(err)
	}

	log := logger.Sugar()

	log.Info("INFO log level message")
	log.Warn("Warn log level message")
	log.Error("Error log level message")

}

Output:

{"level":"info","timestamp":"Oct 17 23:56:18.273471588","caller":"code1/main.go:24","msg":"INFO log level message"}
{"level":"warn","timestamp":"Oct 17 23:56:18.273849048","caller":"code1/main.go:25","msg":"Warn log level message"}
{"level":"error","timestamp":"Oct 17 23:56:18.274870735","caller":"code1/main.go:26","msg":"Error log level message"}

In this example we are defining a custom time format output and we have also disabled the stacktrace info from the output.

 

Summary

This article discusses the Go Zap logger from the Uber development team. Go zap is super fast compared to other loggers like the standard logger , logrus, and zero log.Zap logger is well recommended for low and high performance applications Has different log levels  such as INFO, DEBUG, WARN and they can be configured to logged in the console or in a log file.

 

References

https://pkg.go.dev/go.uber.org/zap/zapcore
https://pkg.go.dev/go.uber.org/zap#hdr-Configuring_Zap

Advertisement

 

Categories GO

Didn't find what you were looking for? Perform a quick search across GoLinuxCloud

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can either use the comments section or contact me form.

Thank You for your support!!

Leave a Comment

X