When we develop a project, maybe sometimes we need to code a log package ourselves, and we can use the popular open source log project such as the standard golang log package, glog, zap which is open source project development by uber, logrus etc. So let’s dive into these packages now!
Table of contents
Open Table of contents
Youtube Video
Logrus
Logrus is the famous log package which gets the most star in the Github. It is powerful, efficient and highly flexible, and provides the custom plugins feature as well. So many open source project use it for logging, such as docker
, prometheus
etc.
In addition to the basic functions of logging, logrus also has the following features:
Supports common log levels
. such asDebug
,Info
,Warn
,Error
,Fatal
,Panic
.Scalable
. Logrus’shook
mechanism allows users to distribute logs to any place through hooks. such aslocal file
,standard output
,elasticsearch
,logstash
,kafka
etc.Supports custom logging format
. It has two built-in formats:JSONFormatter
andTextFormatter
. In addition, logrus allows users to customize the log format by implementing theFormatter
interface.Structured logging
. Logrus’sField
mechanism allows users to use customized log fields instead of recording logs through lengthy messages.Preset Log Fields
. Logrus’s default Fields mechanism allows users to add common log fields to some or all logs, for example, add theX-Request-ID
field to all logs of a certainHTTP
request.Fatal handlers
. Logrus allows you to register one or more handlers that are invoked when a fatal level log occurs. This feature is very useful when our program needs to shut down gracefully.
Logrus Usage
Basic Usage
Logrus can define output, format or log level through simple configuration, as shown below:
package main
import (
"os"
"github.com/sirupsen/logrus"
)
func main() {
// logrus configuration
logrus.SetFormatter(&logrus.JSONFormatter{})
logrus.SetOutput(os.Stdout)
logrus.SetLevel(logrus.WarnLevel)
// logrus usage
logrus.Debug("Useful debugging information")
logrus.Info("Somthing noteworthy happened!")
logrus.Warn("You should probably take a look at this.")
logrus.Error("Somthing failed but I'm not quitting.")
logrus.WithFields(logrus.Fields{
"animal": "walrus",
"size": 11,
}).Info("A group of walrus emerges from the ocean")
logrus.WithFields(logrus.Fields{
"omg": true,
"number": 123,
}).Warn("The group's number increased tremendously!")
}
We can define the log output format by logrus.SetFormatter
, logrus has built-in formatter: JSONFormatter
and TextFormatter
, define the log level by logrus.SetLevel
, and define the log output place by logrus.SetOutput
.
run this command in terminal,
go run main.go
It’s the output:
{"level":"warning","msg":"You should probably take a look at this.","time":"2025-07-08T14:06:00+04:00"}
{"level":"error","msg":"Somthing failed but I'm not quitting.","time":"2025-07-08T14:06:00+04:00"}
{"level":"warning","msg":"The group's number increased tremendously!","number":123,"omg":true,"time":"2024-07-08T14:06:00+04:00"}
Default Fields
Often, it is helpful to always have some fixed record fields in an application or part of an application. For example, when processing user HTTP requests, all logs in the context will have request_id. To avoid having to use it every time you log:
logrus.WithFields(logrus.Fields{"request_id",request_id})
We can create a logrus.Entry
instance, set default Fields for this instance, set the instance to the Logger. and then each time we record a log, these default fields will be attached.
package main
import (
"github.com/sirupsen/logrus"
)
// Create a new instance of the logger.
var log = logrus.New()
func main() {
// logrus configuration
logger := log.WithFields(logrus.Fields{"request_id": "12346"})
// logrus usage
logger.Info("Somthing noteworthy happened!")
logger.Warn("You should probably take a look at this.")
}
run this command in terminal,
go run main.go
It’s the output:
INFO[0001] Somthing noteworthy happened! request_id=12345
WARN[0001] You should probably take a look at this. request_id=12345
Hook Interface
Logrus has the hook capability, allowing us to customize some log processing logic. Logrus will trigger HOOK when recording the message of the log level returned by Levels()
, modify logrus.Entry
according to the content defined by the Fire
method. To implement a hook, we only need to implement the following interface:
type Hook interface {
Levels() []Level
Fire(*Entry) error
}
The following is a simple example:
package main
import (
"github.com/sirupsen/logrus"
)
type DefaultFieldHook struct{}
func (hook *DefaultFieldHook) Fire(entry *logrus.Entry) error {
entry.Data["myHook"] = "MyHookTest"
return nil
}
func (hook *DefaultFieldHook) Levels() []logrus.Level {
return logrus.AllLevels
}
func main() {
// Create a new instance of the logger.
log := logrus.New()
log.AddHook(&DefaultFieldHook{})
// logrus usage
log.Info("Somthing noteworthy happened!")
log.Warn("You should probably take a look at this.")
}
The DefaultFieldHook
definition adds the default field myHook="MyHookTest"
to all log messages at all levels. After implementing the hook, you only need to call log.AddHook(hook)
to register the custom hook in logrus. Many powerful log processing functions can be implemented through the hook. The more common usage is that when a log of a specified level is generated, an email notification or an alarm is sent to the relevant person in charge.
run this command in terminal,
go run main.go
It’s the output:
INFO[0001] Somthing noteworthy happened! myHook=MyHookTest
WARN[0001] You should probably take a look at this. myHook=MyHookTest