A Dive Into the `fmt` Package
We usually use the fmt
package without giving it much thought. A fmt.Printf
here, a fmt.Sprintf
there and on we go. However, if you’ll take a closer look,
you’ll be able to get much more out of it.
Since Go is used a lot to write servers or services, our main debugging tool
is the logging system. The log
package provides log.Printf
which has the
same semantics as fmt.Printf
. Good and informative log messages are worth
their weight in gold and adding some formatting support to your data structure
will add valuable information to your log messages.
Formatting Output
Go fmt
functions supports several verbs, the most common ones are %s
for
strings, %d
for integers and %f
for floats. Let’s explore some more verbs.
%v & %T
%v
will print any Go value and %T
will print the type of the variable. I
use these verbse when debugging.
|
|
Width
You can specify the width of a printed number, for example
|
|
You can also specify the width as a parameter to Printf
by specifying the
width as *
. For example:
|
|
This is useful when you print list of numbers and would like to align them to the right for comparison.
|
|
will print
1 2 3 4 |
00 12 01 237 02 3878 03 3 |
Making it easier for us to compare the numbers.
Reference by Position
If you’re referencing a variable several times inside a format string, you can
reference by position using %[n]
where n is the index of the parameter (1
based).
|
|
will print
1
|
The price of carrot was $23. $23! imagine that. |
%v
%v
will print a Go value, it can be modified with +
prefix to print field
names in a struct and with #
prefix to print field names and type.
|
|
will print
1
|
&{1 2} &{X:1 Y:2} &main.Point{X:1, Y:2} |
I tend to use the %+v
verb most.
fmt.Stringer & fmt.Formatter
Sometimes you’d like a finer control on how your objects are printed. For example you’d like one string representation for an error when it is shown to the user and another, more detailed, when it is written to log.
To control how your objects are printed, you need to implement
fmt.Formatter
interface and
optionally fmt.Stringer
.
One good example is how the excellent
github.com/pkg/errors
makes use of
fmt.Formatter
. Say you’d like to load our configuration file with and you have
an error. You can print a short error to the user (or return it in API …) and
print a more detailed error to the log.
|
|
this will emit to the user
1
|
error: can't open config file: open /no/such/file.toml: no such file or directory |
and to the log file
1 2 3 4 5 6 7 8 9 10 11 |
2018/11/28 10:43:00 can't load config open /no/such/file.toml: no such file or directory can't open config file main.loadConfig /home/miki/Projects/gopheracademy-web/content/advent-2018/fmt.go:101 main.main /home/miki/Projects/gopheracademy-web/content/advent-2018/fmt.go:135 runtime.main /usr/lib/go/src/runtime/proc.go:201 runtime.goexit /usr/lib/go/src/runtime/asm_amd64.s:1333 |
Here’s a small example. Say you have an AuthInfo
struct for a user
|
|
You’d like to limit the chances that the APIKey
will be printed out (say when
you log). You can print a mask (*****
) instead of the key
1 2 3 |
const ( keyMask = "*****" ) |
First the easy case fmt.Stringer
.
|
|
And now fmt.Formatter
which gets a
fmt.State
and a rune for the verb.
fmt.State
implements io.Writer
,
enabling you to write directly to it.
To know all the fields available in a struct, you can use the
reflect
. package. This will make sure
your code works even when AuthInfo
changes.
|
|
And now you’re ready to implement fmt.Formatter
|
|
Let’s try it out:
|
|
which will emit
1 2 3 4 5 6 |
Login:daffy, ACL:00000011, APIKey: ***** ai %s: Login:daffy, ACL:00000011, APIKey: ***** ai %q: "Login:daffy, ACL:00000011, APIKey: *****" ai %v: {3 ***** daffy} ai %+v: {ACL:3 APIKey:***** Login:daffy} ai %#v: *main.AuthInfo{ACL:3 APIKey:***** Login:daffy} |
Conculsion
The fmt
package has many capabilities other than the trivial use. Once you’ll
familiarize yourself with these capabilities, I’m sure you find many
interesting uses for them.
You can view the code for this post here.
About the Author
Hi there, I’m Miki, nice to e-meet you ☺. I’ve been a long time developer and have been working with Go for about 8 years now. I write code professionally as a consultant and contribute a lot to open source. Apart from that I’m a book author, an author on LinkedIn learning, one of the organizers of GopherCon Israel and an instructor. Feel free to drop me a line and let me know if you learned something new or if you’d like to learn more.