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 :
- Log Errors or Warning messages that developers need to know about.
- Log performance metrics, memory consumption, etc
- 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:
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:
- Print the name and line about the file and the latest function name while this log
- 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