Skip to content
Santekno.com | Tech Tutorials and Trends
EN
📖 0%
30 Apr 2024 · 4 min read ·Article 118 / 119
Go

04 Learning Logrus Field, Entry, and Hook in Golang

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

This time we will explore Logrus more deeply, specifically its Field, Entry, and Hook features. The features that Logrus provides for our logging needs are extremely useful whenever we require capabilities that support our business. For example, field is used to attach data or information to a log, along with several other features that we will discuss in detail.

Field in Logrus

When we need certain Fields to appear or be attached to our log file, we can use these Fields to embed data or information required to support our business needs or to help with tracing when problems occur in our system.

We could actually insert that information manually into the message itself, but Logrus provides a better way through the Field feature. With this feature, we can add information to the log we send according to our needs.

The functions we will use in Logrus are:

go
1logger.WithField()
2// or
3logger.WithFields()

Let’s go ahead and try it by creating a function like the one below.

go
 1func TestField(t *testing.T) {
 2	logger := logrus.New()
 3	logger.SetFormatter(&logrus.JSONFormatter{})
 4
 5	logger.WithField("user_id", 12345).Info("Hello World")
 6
 7	fields := map[string]interface{}{
 8		"user_id": 12345,
 9		"shop_id": 123,
10	}
11	logger.WithFields(fields).Infof("this is information about shop")
12}

This will produce the following output in the file.

txt
1{"level":"info","msg":"Hello World","time":"2024-04-30T16:57:07+07:00","user_id":12345}
2{"level":"info","msg":"this is information about shop","shop_id":123,"time":"2024-04-30T16:57:07+07:00","user_id":12345}

As we can see, the output contains the user_id field, and the last line contains both the user_id and shop_id fields, which we defined ourselves.

Entry in Logrus

An Entry is a struct that represents a log so that it can be sent by Logrus. Every log we send is actually created from an Entry object. For instance, when we build our own Formatter, the parameter used to perform the formatting is not a string but an Entry object.

You can read more details in this file . To create an entry, we can use the function:

go
1logrus.NewEntry()

In practice, Entry is rarely used directly, but to better understand this logging topic we will try creating one. First, let’s write a function like the one below.

go
 1func TestEntry(t *testing.T) {
 2	logger := logrus.New()
 3	logger.SetFormatter(&logrus.JSONFormatter{})
 4
 5	logger.WithField("user_id", 12345).Info("Hello World")
 6
 7	entry := logrus.NewEntry(logger)
 8	entry.WithField("user_id", 1235)
 9	entry.Info("log info using entry")
10}

The result is the same as when we add a Field, because fundamentally, adding a field is the same as initializing an Entry.

Hook in Logrus

A Hook in Logrus is a struct that can be added to the Logger to act as a callback whenever a log is executed or something happens in our system at a certain level.

For example, we might want to send a chat notification to a Slack Channel whenever there is a log with the Error level or another level. We can also add a custom Hook using the function:

go
1logger.AddHook()

To better understand how Hooks work, let’s try using one. First, we’ll write the code below.

go
 1type SimpleHook struct{}
 2
 3func (s *SimpleHook) Levels() []logrus.Level {
 4	return []logrus.Level{logrus.ErrorLevel, logrus.FatalLevel}
 5}
 6
 7func (s *SimpleHook) Fire(entry *logrus.Entry) error {
 8	fmt.Println("Simple Hook", entry.Level, entry.Message)
 9	return nil
10}

This is a simple example of creating a Hook before we register it into Logrus. The Levels() method is used to specify which levels will trigger the Hook we have initialized.

And the Fire() method is the one that Logrus will execute as the Hook mechanism after a log is performed.

Next, let’s create a unit test function to make sure the Hook we built works properly.

go
1func TestHook(t *testing.T) {
2	logger := logrus.New()
3	logger.AddHook(&SimpleHook{})
4
5	logger.Info("Hello info")
6	logger.Warn("Hello warn")
7	logger.Error("Hello error")
8	logger.Fatal("Hello fatal")
9}

After we run that unit test function, the output will look like this.

txt
1time="2024-04-30T17:44:58+07:00" level=info msg="Hello info"
2time="2024-04-30T17:44:58+07:00" level=warning msg="Hello warn"
3Simple Hook error Hello error
4time="2024-04-30T17:44:58+07:00" level=error msg="Hello error"
5Simple Hook fatal Hello fatal
6time="2024-04-30T17:44:58+07:00" level=fatal msg="Hello fatal"

As you can see, the log file contains Simple Hook error Hello error and Simple Hook fatal Hello fatal after logging. This indicates that after a log is performed, the Hook is executed based on the levels we specified. Meanwhile, when we log at the Info and Warn levels, the Hook we added is not executed.

In summary, we can conclude that this Hook is very useful for alerting us ASAP (as soon as possible) about problems in our system, so that we become more aware of what is happening and can acknowledge issues more quickly.

Related Articles

💬 Comments