Display File, Function, and Line Number in Go (Golang)

Years ago, I used a macro in C and C++ programs to provide information on the current line of code being executed. I used this in error handlers and exception traps in addition to other information. Displaying the file name and line number of a given exception in large projects was often helpful in the speedy remediation of issues.

I also used this technique to home brew tracing function in the code. If problems arose in environments where diagnostic tools were not available, simple execution traces were often very valuable to the troubleshooting process.

While Go has facilities to trace the execution of a program, I wanted to be able to determine the file name and line number of the code currently being executed. The runtime package contains a function named Caller() that returns four values: the program counter, the file name, the line number, and a boolean indicating if the retrieval has been a success.

I found that the current function name can be extracted from the program counter return value by using the FuncForPC() function.

The full current source code for whereami.go and the example functions ( in the src/ folder ) can be found at:

https://github.com/jimlawless/whereami

func WhereAmI(depthList ...int) string {
    var depth int
    if depthList == nil {
        depth = 1
    } else {
        depth = depthList[0]
    }
    function, file, line, _ := runtime.Caller(depth)
    return fmt.Sprintf("File: %s  Function: %s Line: %d", chopPath(file), runtime.FuncForPC(function).Name(), line)
}

WhereAmI() is a variadic function that accepts any number of integer arguments. If no arguments are specified, we assume that the calling function wants to specify one level of lookup on the call stack.

Otherwise, parameter number zero would contain the integer representing the call stack depth that the calling function has supplied.

Note that the call in whereami_example1.go uses a simple reference to WhereAmI() as an argument to Printf().

// Copyright 2016 - by Jim Lawless
// License: MIT / X11
// See: http://www.mailsend-online.com/license2016.php
//
// This code may not conform to popular Go coding idioms
 
package main
 
import (
    "fmt"
    "github.com/jimlawless/whereami"
)
 
func main() {
    fmt.Printf("%s\n", whereami.WhereAmI())
}

The output of the Printf() invocation is:

File: whereami_example1.go  Function: main.main Line: 15

whereami_example2.go shows the use of the WhereAmI() call in main() and in a factorial function called fact().

whereami_example3.go contains a usage error. Since we have now wrapped our call to WhereAmI() in a function called trace(), the only information returned for WhereAmI() will indicate the call from the trace() function.

We correct this problem in whereami_example4.go by supplying a depth of 2 to the WhereAmI() function. This allows us to get information from trace()’s caller instead of trace() itself.

It occurs to me that I had failed to process the bool that is returned to determine if the runtime.Caller() function works correctly. I’ll have to fix that.