A Hands-On Approach to Zap - Structured Logging in Go
Introduction
Zap is a high-performance library for Go that offers a fast and efficient way to log messages in Go applications. It is a structured logger, which means that it logs messages in a format, such as JSON, which can be easily parsed by other tools. With its fast performance, it allows simple logging without impacting the performance of your application.
Zap has two main components: the core library and the sugared logger. The core library is a low-level API that provides a fast and efficient way to log messages. The sugared logger is a high-level API that provides a more flexible and convenient way to log messages. It is a wrapper around the core library that provides a more familiar API to developers coming from other languages.
In this article, we will explore logging with zap, starting from the basics and moving on to more advanced topics. We will take a hands-on approach, providing step-by-step instructions, and real-world examples to help you get started with zap. Let’s get started!
Getting Started
To use Zap, you need to install the package into your project. You can do this by running the following command:
Once you have installed the package, you can import it into your project by adding the following line to your code:
Next, you need to initialize Zap in your code. This can be done using the zap.New
function, which returns a new logger instance. By default, it will log messages to the standard output and uses a JSON encoder.
Some predifined configurations are available to help you get started quickly. These configurations are:
zap.NewDevelopment()
- This is used to initialize a development logger.zap.NewProduction()
- This is used to initialize a production logger.zap.NewExample()
- This is used to initialize an example logger.zap.NewNop()
- This is used to initialize a no-op logger. It does not log any messages.
Here is an example of how to initialize Zap:
This code snippet instantiates a new logger and checks for any errors. If any errors are found, the program will log the error and exit. The defer
keyword ensures that the logger is synced when the program exits. With this code in place, you can now start logging messages.
Logging Basics
With the setup complete, you can now start logging messages. Logging in zap is straightforward to use. The example below shows how to log a message with the Info
level:
The Info
method takes a string as an argument, which is the message to be logged. The message will be logged with the Info
level, which is the default level in zap.NewProduction()
.
Adding Fields to Log Messages
The Info
method also takes a variadic list of key-value pairs as an argument. The key-value pairs are used to add additional information to the log message. In the example above, the key-value pair zap.String("level", "info")
is added to the log message. The zap.String
function is used to create a key-value pair with a string value. The first argument is the key, and the second argument is the value.
Some other functions that can be used to create key-value pairs are:
zap.Bool
zap.Int
zap.Float64
Logging Levels
Just like other logging libraries, zap supports different logging levels. The logging levels are used to indicate the severity of the log message. The logging levels are:
-
Debug
This is the default logging level, and it is used to log debug messages.
-
Info
This is the most basic logging level. It is used to log informational messages.
-
Warn
This is used to log warning messages. It is used to indicate that something unexpected happened, but the application can continue to run.
-
Error
This is used to log error messages. It is used to indicate that something unexpected happened, and the application cannot continue to run. In its default configuration, it will also log a stack trace.
-
DPanic
This is used to log messages that are only logged in development mode. It is used to indicate that something unexpected happened, and the application cannot continue to run. Just like the
Error
level, it will also log a stack trace. -
Panic
This is used to log panic messages. It is used to indicate that something unexpected happened, and the application cannot continue to run. It will also panic the application.
-
Fatal
This is used to log fatal messages. It is used to indicate that something unexpected happened, and the application cannot continue to run. It will also exit the application with a non-zero exit code.
The logging levels are ordered from least to most severe. The Debug
level is the least severe, and the Fatal
level is the most severe. The Fatal
level is the highest, and it will exit the program after logging the message.
Setting the Logging Level
By default, the logging level is set to Info
. This means that only messages with the Info
level and above will be logged. You can change the logging level by using the SetLevel
method. The SetLevel
method is a member of the AtomicLevel
type and can be accessed using the Level
field of the logger instance.
The SetLevel
method takes a zapcore.Level
enum as an argument. The zapcore.Level
type is an alias for the int8
type. The zapcore.Level
type is used to represent the logging levels. The zapcore.Level
type has the following constants:
DebugLevel
- ‘-1’InfoLevel
- ‘0’WarnLevel
- ‘1’ErrorLevel
- ‘2’DPanicLevel
- ‘3’PanicLevel
- ‘4’FatalLevel
- ‘5’
Example:
In this example, we declare a new AtomicLevel
instance and set the logging level to ErrorLevel
. We then create a new logger instance and pass the configuration to it. The Info
method will not be logged, but the Error
method will be logged.
Customizing the Log Format
The zap.NewProductionEncoderConfig
function returns a zapcore.EncoderConfig
instance. This instance is used to configure the log format. The zapcore.EncoderConfig
type has the following fields:
TimeKey
- The key for the time field. The default value ists
.LevelKey
- The key for the level field. The default value islevel
.NameKey
- The key for the logger name field. The default value islogger
.CallerKey
- The key for the caller field. The default value iscaller
.MessageKey
- The key for the message field. The default value ismsg
.StacktraceKey
- The key for the stack trace field. The default value isstacktrace
.
among others.
We could change the time format by setting the TimeKey
field to time
and the EncodeTime
field to zapcore.ISO8601TimeEncoder
. The EncodeTime
field is a function that takes a time.Time
instance and returns a string
instance. The zapcore.ISO8601TimeEncoder
function is used to encode the time in the ISO8601 format.
Logging to a File
Logging to a file is very similar to logging to the console. The only difference is that you need to create a file and pass it to the logger. The example below shows how to log to a file:
As you can see instead of passing the os.Stdout
to the zapcore.Lock
function, we pass the file instance. The zapcore.Lock
function is used to lock the file so that only one goroutine can write to the file at a time.
Logging to a File and the Console
You can log to both a file and the console at the same time. To log to both file and the console, we use the zapcore.NewTee
function. This function takes a list of zapcore.Core
instances as arguments. The zapcore.NewTee
function returns a new zapcore.Core
instance. We then pass this instance to the zap.New
function.
Defining Configurations with JSON
Zap allows you to define configurations in different formats such as JSON or YAML. This is passed to a zap.Config
instance.
In the example above, we define a JSON string that contains the configuration for the logger. The JSON string is unmarshalled into a zap.Config
instance. The zap.Config
instance is then passed to the zap.Config.Build
function. This function returns a *zap.Logger
instance.
Using these alternative formats of passing configurations to the logger, allows you to create your configuration files and pass them to the logger.
Using the Suggered Logger
The suggered logger is a wrapper around the *zap.Logger
instance. It provides a simpler API for logging. The suggered logger is created by calling the zap.Suggered
function. This can be used in applications where performance is not a concern though it is faster than its other structured logging counterparts.
Suggered logging is more convenient than zapcore logging, It is great when you want to quickly add logging to your application.
Conclusion
In this article, we covered the basics of logging with Zap, including creating a logger, logging to a file/console, defining configurations with JSON, and using the sugared logger. With this hands-on guide, you now have a solid understanding of how to use Zap logging in your Go projects. Whether you opt for default configurations or customize, Zap is an excellent choice for efficient logging in Go applications.