Preface:
Today I introduced it throughAlthough the method of backtracking the call stack to obtain the caller's information is powerful, frequent acquisition of this information will also have an impact on the program's performance.
background
When we add business logs to the application code, no matter what level of log it is, in addition to the information we actively pass it to Logger for it to record, which function is printed and where this log is located is also very important. Otherwise, it is very likely to be like looking for a needle in a haystack when troubleshooting problems.
For information such as function name and line number of the caller calling the Logger method when recording the log. Some log libraries support, such as Zap:
func main() { logger, _ := (()) defer () ("hello world") }
Output:
{"level":"info","ts":1587740198.9508286,"caller":"caller/:9","msg":"hello world"}
However, if you want to develop a robust development framework, you should not force yourself to bind to a certain log library. A better way is to develop a log storefront, and directly use the log storefront in the program, and then the storefront calls the log library to complete the log recording. Typical Java slf4j is this idea. The program uses slf4j directly, and the logger behind can be logback, log4j or even any log library implementation that meets the slf4j convention.
If we use Go to design a Log Facade, we need to obtain the caller's function name and file location in the storefront. How to implement this function in Go? This requires the Caller function provided by the runtime standard library.
This article mainly introduces the use of . I said so much above just to lay the groundwork, learn how to use it, and where to apply it.
The function signature is as follows:
func Caller(skip int) (pc uintptr, file string, line int, ok bool)
The Caller function reports the file and line number information of the function executed by the current Go program call stack. The parameter skip is the number of stack frames to be traced up, 0 represents the caller of Caller (the call stack where Caller is located), 1 represents the caller who calls the Caller caller, and so on. Isn't it a little dizzy?
Here is an example:
func CallerA() { //What you get is the call stack of CallerA function pc, file, lineNo, ok := (0) //What is obtained is the call stack of the caller of the CallerA function pc1, file1, lineNo1, ok1 := (1) }
The return value of the function is the call stack identifier, the complete file name with the path, and the line number of the call in the file. If information cannot be obtained, the return value ok will be set to false.
Get the caller's function name
The first return value in the return value is a call stack identifier. Through it, we can get the function information of the call stack * and further obtain the caller's function name. The functions and methods that will be used here are as follows.
func FuncForPC(pc uintptr) *Func func (*Func) Name
The function returns a *Func representing the call stack corresponding to the call stack identifier pc; if the call stack identifier does not have a corresponding call stack, the function returns nil.
The Name method returns the name of the function called on the call stack. As mentioned above, it may return nil. However, the Name method makes this judgment when implementing it to avoid the possibility of panic, so we can use it with confidence.
func (f *Func) Name() string { if f == nil { return "" } fn := () if () { // inlined version fi := (*funcinl)((fn)) return } return funcname(()) }
Example of usage
Here is a simple example of using and using a simple example to obtain caller information:
package main import ( "fmt" "path" "runtime" ) func getCallerInfo(skip int) (info string) { pc, file, lineNo, ok := (skip) if !ok { info = "() failed" return } funcName := (pc).Name() fileName := (file) // Base function returns the last element of the path return ("FuncName:%s, file:%s, line:%d ", funcName, fileName, lineNo) } func main() { // Print out the information about the getCallerInfo function itself (getCallerInfo(0)) // Print out the caller information of the getCallerInfo function (getCallerInfo(1)) }
Notice:Here we demonstrate it relatively simple. You can get the caller's information by tracing upwards a call stack. When you really want to implement a class library such as a log screen, there may be several layers of encapsulation. The caller information you want to record in the log should be the location of the log in the business code. At this time, the number of layers you need to trace back up is definitely not as simple as 1. The specific number of layers you skip depends on the specific encapsulation of the implemented log screen.
Summarize
The article introduces the method of backtracking the call stack to obtain the caller's information. Although it is powerful, frequent acquisition of this information will also have an impact on the program's performance. Our business code should not be implemented by it. Its role is more about the fact that some class libraries that are transparent to the business will be used when recording information.
This is the article about getting the caller's function name, file name and line number in Go functions. For more related Go to get function name content, please search for my previous article or continue browsing the related articles below. I hope everyone will support me in the future!