Print Function, File Name, Line Number in GO [SOLVED]


GO, GOLANG Solutions

Author: Tuan Nguyen
Reviewer: Deepak Prasad

In the previous post, we already discussed some examples of working with logrus in Golang. In today's post, we will show you how to include the function name, filename, and line number using logrus. Logrus is a structured logger for Go and is compatible with the standard log package. The standard logging package is limited in some ways, leading to the development of other logging packages like Logrus, oklog and zerolog. Logging is an important  aspect of modern programming and is used to :

  1. Log Errors or Warning messages that developers need to know about.
  2. Log performance metrics, memory consumption, etc
  3. Log Debug messages to help developers troubleshoot their software.

 

Example 1: Set the option SetReportCaller to true

At the end of 2018, this option is available within the library itself. If you want to log the function name, filename, and line number, simply set the SetReportCaller to true. Here is an example of how to print the function name, filename, and line number using logrus:

func SetReportCaller(include bool): SetReportCaller sets whether the standard logger will include the calling method as a field.

package main

import (
	"time"

	log "github.com/sirupsen/logrus"
)

func main() {
	// set this to true to print the function name, file name and line number
	log.SetReportCaller(true)

	log.Println("Hello Golinux cloud members!")
	log.Println("Now is:", time.Now())
}

Output:

time="2023-01-03T23:02:34+07:00" level=info msg="Hello Golinux cloud members!" func=main.main file="C:/Users/nguye/OneDrive/Desktop/golang/main/logrus.go:13"
time="2023-01-03T23:02:34+07:00" level=info msg="Now is: 2023-01-03 23:02:34.9017448 +0700 +07 m=+0.002107101" func=main.main file="C:/Users/nguye/OneDrive/Desktop/golang/main/logrus.go:14"

 

Example 2: Customize the logrus.JSONFormatter format

In logrus, the built-in logging formatters are:

logrus.TextFormatter. Logs the event in colors if stdout is a tty, otherwise without colors.

NOTE:

To force colored output when there is no TTY, set the ForceColors field to true. To force no colored output even if there is a TTY set the DisableColors field to true. For Windows, see github.com/mattn/go-colorable.

When colors are enabled, levels are truncated to 4 characters by default. To disable truncation set the DisableLevelTruncation field to true.

When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the PadLevelText field to true enables this behavior, by adding padding to the level text.

All options are listed in the generated docs.

logrus.JSONFormatter. Logs fields as JSON.

All options are listed in the generated docs.

In this example, we will set the log.SetReportCaller to true and then use the CallerPrettyfier() function to customize the logrus.JSONFormatter

CallerPrettyfier(): CallerPrettyfier can be set by the user to modify the content of the function and file keys in the JSON data when ReportCaller is activated. If any of the returned values is an empty string the corresponding key will be removed from JSON fields.

Let's take a look at the example below to see how we can display the function name, file name, and line number in a customized format:

package main

import (
	"path"
	"runtime"
	"strconv"
	"time"

	log "github.com/sirupsen/logrus"
)

func main() {
	log.SetReportCaller(true)

	//customize the formatter
	log.SetFormatter(&log.JSONFormatter{
		CallerPrettyfier: func(frame *runtime.Frame) (function string, file string) {
			fileName := path.Base(frame.File) + ", lineNumber:" + strconv.Itoa(frame.Line)
			return frame.Function, fileName
		},
	})

	log.Println("Hello Golinux cloud members!")
	log.Println("Now is:", time.Now())
}

Output:

{"file":"logrus.go, lineNumber:22","func":"main.main","level":"info","msg":"Hello Golinux cloud members!","time":"2023-01-03T23:16:16+07:00"}
{"file":"logrus.go, lineNumber:23","func":"main.main","level":"info","msg":"Now is: 2023-01-03 23:16:16.8059898 +0700 +07 m=+0.002165001","time":"2023-01-03T23:16:16+07:00"}

 

Example 3: Create custom logrus Formatter

Instead of customizing the existing formatter we can also create our own formatter to be used with logrus. Here we have used SetReportCaller to get the line number of the call which has been executed. This will capture all the success and failed event's line number called by logger function.

package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/sirupsen/logrus"
)

type MyFormatter struct{}

var levelList = []string{
	"panic",
	"fatal",
	"error",
	"warn",
	"info",
	"debug",
	"trace",
}

var logFile = "/tmp/app.log"

func (mf *MyFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	var b *bytes.Buffer
	if entry.Buffer != nil {
		b = entry.Buffer
	} else {
		b = &bytes.Buffer{}
	}

	// define supported log levels
	level := levelList[int(entry.Level)]
	strList := strings.Split(entry.Caller.File, "/")
	// get the go file name
	fileName := strList[len(strList)-1]
	b.WriteString(fmt.Sprintf("%28s, %5s, [%s:%d] - %s\n",
		// Custom Time Format
		entry.Time.Format("2006-01-02T15:04:05.9999999Z"),
		level, fileName, entry.Caller.Line, entry.Message))
	return b.Bytes(), nil
}

func configureLogging() *logrus.Logger {
	// read the logfile
	f, err := os.OpenFile(logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		panic(err)
	}

	logger := logrus.New()

	// Log the messages on console as well as log file
	logger.SetOutput(io.MultiWriter(f, os.Stdout))

	logger.SetLevel(logrus.DebugLevel)
	// Required to get line number
	logger.SetReportCaller(true)

	// Set custom formatter
	logger.SetFormatter(&MyFormatter{})
	return logger
}

func main() {
	logger := configureLogging()
	logger.Info("Hello World")
}

Output:

# go run main.go 
2023-01-06T00:01:59.8623697Z,  info, [main.go:70] - Hello World

# cat /tmp/app.log 
2023-01-04T19:13:51.8677253Z,  info, auth, [<nil>:main.go:65] - Hello World
2023-01-04T19:16:01.4347405Z,  info, [main.go:65] - Hello World
2023-01-05T23:59:57.1855156Z,  info, [main.go:63] - Hello World
2023-01-06T00:01:59.8623697Z,  info, [main.go:70] - Hello World

 

Example 4: Using a third-party logging formatter

We can use powerful-logrus-formatter to get fileName, the log's line number, and the latest function's name when printing the log. If you want to use this package, first install it by running the below command:

github.com/zput/zxcTool/ztLog/zt_formatter

This package allows us to:

  1. Print the name and line about the file and the latest function name while this log
  2. Save the log to file

Here is an example of using the zt_formatter along with the logrus to print out the function name, file name, and line number:

package main

import (
	"fmt"
	"path"
	"runtime"

	nested "github.com/antonfisher/nested-logrus-formatter"
	"github.com/sirupsen/logrus"
	"github.com/zput/zxcTool/ztLog/zt_formatter"
)

func main() {
        // use the specific formatter
	var exampleFormatter = &zt_formatter.ZtFormatter{
		CallerPrettyfier: func(f *runtime.Frame) (string, string) {
			filename := path.Base(f.File)
			return fmt.Sprintf("%s()", f.Function), fmt.Sprintf("%s:%d", filename, f.Line)
		},
		Formatter: nested.Formatter{
			//HideKeys: true,
			FieldsOrder: []string{"component", "category"},
		},
	}
	l := logrus.New()
        // set the SetReportCaller to true
	l.SetReportCaller(true)
	if exampleFormatter != nil {
		l.SetFormatter(exampleFormatter)
	}

	l.Infof("this is %v demo", "Hello world!")
}

Output:

[logrus.go:30::main.main()] Jan  3 23:28:56.705 [I] this is Hello world! demo

 

Example 5: Write your own function to log the file name, function name, and line number

In the example below, we will use the runtime.Caller() to get the file name, function name, and line number. Then we use the logrus.WithFields to export the logger:

func Caller(skip int) (pc uintptr, file string, line int, ok bool): Caller reports file and line number information about function invocations on the calling goroutine's stack. The argument skip is the number of stack frames to ascend, with 0 identifying the caller of the Caller.

package main

import (
	"fmt"
	"runtime"
	"strings"

	"github.com/sirupsen/logrus"
)

func main() {
	Warn("this is a warning!")
}

func Warn(msg ...interface{}) {
	if pc, file, line, ok := runtime.Caller(1); ok {
		//get the file name
		file = file[strings.LastIndex(file, "/")+1:]
		//get the function name
		funcName := runtime.FuncForPC(pc).Name()
		// export the logger
		logrus.WithFields(
			logrus.Fields{
				"src": fmt.Sprintf("%s:%s:%d", file, funcName, line),
			}).Warn(msg...)
	}
}

Output:

time="2023-01-03T23:38:50+07:00" level=warning msg="this is a warning!" src="logrus.go:main.main:12"

 

Summary

In this tutorial, I have given you some examples of printing the function name, file name, and line number using the logrus. We can easily get the SetReportCaller to true which means including the calling method as a field. In the next step, we can use the default, a customized format, or a third-party formatter to print the log to the console. Another way is using the runtime.Caller() function to get all the information about the file name, and function name and parse it as a field of the logger.

 

References

https://github.com/sirupsen/logrus
https://github.com/zput/zxcTool
https://pkg.go.dev/runtime#Caller

 

Tuan Nguyen

Tuan Nguyen

He is proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise spanning these technologies, he develops robust solutions and implements efficient data processing and management strategies across various projects and platforms. You can connect with him on his LinkedIn profile.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

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 send mail to admin@golinuxcloud.com

Thank You for your support!!

Leave a Comment