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 source code for whereami.go and the example functions ( in the src/ folder ) can be found at:

https://github.com/jimlawless/whereami

The WhereAmI() function looks like this:

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.

Advertisements

About Jim Lawless

I've been programming computers for about 36 years ... 30 of that professionally. I've been a teacher, I've worked as a consultant, and have written articles here and there for publications like Dr. Dobbs Journal, The C/C++ Users Journal, Nuts and Volts, and others.
This entry was posted in Programming and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s