

Power-Up Your Golang Logging: Idiomatic Log Strategies in Go
source link: https://hackernoon.com/logging-for-gophers-idiomatic-log-strategies-in-go-golang-ocr3tz3
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.


In this article, I’m going to cover some rules of thumb for logging in go, as well as some functions you may not have heard of that can make your debugging life easier.
Rule #1 – Use Errors Where Appropriate, Not Strings
Go has a built-in error type, which allows developers to easily differentiate errors from “normal” strings, as well as check if there are no errors in a more succinct way. The error type is an interface, that simply requires the type in question to define an “Error()” function that prints itself as a string.
type error interface { Error() string }
Never use a normal string where an error is more appropriate! Strings imply to users of your function that “business as usual” is going on. Errors make it clear that something is wrong.
For example, let’s pretend we are building a REST API. We may want a function that takes a response writer, a message, and a code that can be used to return error codes on erroneous API calls. Here is our first attempt:
func respondWithError(w http.ResponseWriter, code int, msg string) { payload := map[string]string{"error": msg} response, _ := json.Marshal(payload) w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) w.Write(response) }
This will work perfectly. In fact, anywhere where an error type works a string could be used instead. However, if we are interested in writing code that other developers can more quickly understand and make contributions to, we should use an Error type:
func respondWithError(w http.ResponseWriter, code int, msg error) { payload := map[string]string{"error": msg.Error()} response, _ := json.Marshal(payload) w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) w.Write(response) }
Rule #2 – Wrap Errors
Often times, we simply pass errors up a chain, and it can be quite convenient. For example, let’s look at this function that formats hours and minutes into a time message:
func formatTimeWithMessage(hours, minutes int) (string, error) { formatted, err := formatTime(hours, minutes) if err != nil { return "", err } return "It is " + formatted + " o'clock", nil }
The problem here is that the formatTime function can be called many other places within our application or library. If all we do is pass along the raw error, it gets really hard to tell where the error was called. Instead, let’s do the following:
func formatTimeWithMessage(hours, minutes int) (string, error) { formatted, err := formatTime(hours, minutes) if err != nil { return "", fmt.Errorf("formatTimeWithMessage: %v", err) } return "It is " + formatted + " o'clock", nil }
Additionally, if you are working in Go 1.13 or later, then you can look into the more explicit “Unwrap()” method for error chains: https://blog.golang.org/go1.13-errors#TOC_3.1.
fmt.Errorf()
fmt.Errorf() is similar to fmt.Printf(), but returns an error instead of a string. You may have done this in the past:
err := errors.New("Bad thing happened! " + oldErr.Error())
This can be accomplished more succinctly using fmt.Errorf():
err := fmt.Errorf("Bad thing happened! %v", oldError)
The difference in readability becomes even more obvious when the formatting in question is more complicated and includes more variables.
Formatting Structs
Printing structs can be quite ugly and unreadable. For example, the following code:
func main() { make := "Toyota" myCar := Car{year:1996, make: &make} fmt.Println(myCar) }
Will print something like:
{1996 0x40c138}
We may want to get the value in the pointer, and we probably want to see the keys of the struct. So we can implement a default String() method on our struct. If we do so, then the Go compiler will use that method when printing.
func (c Car)String() string{ return fmt.Sprintf("{make:%s, year:%d}", *c.make, c.year) } func main() { make := "Toyota" myCar := Car{year:1996, make: &make} fmt.Println(myCar) }
Which will print something like:
{make:Toyota, year:1996}
fmt.Println()
In the past, I’ve often done the following when logging:
fmt.Printf("%s beat %s in the game\n", playerOne, playerTwo)
Turns out, it is much easier to just use the fmt.Println() function’s ability to add spacing:
fmt.Printf(playerOne, "beat", playerTwo, "in the game")
Thanks For Reading!
- Lane on Twitter: @wagslane
- Lane on Dev.to: wagslane
- Download Qvault: https://qvault.io
- Star our Github: https://github.com/q-vault/qvault
Recommend
-
208
Idiomatic Go client package generation from OpenAPI documents Introduction Introduction |
-
13
‘git log’ Graphing Recently I’ve become responsible for a large project with lots of branches and a complex history I frequently need to dig into. I prefer to work on the command line, so ended up exploring various git log options.
-
12
Strategies to Power Your Business: 3 Lessons from the Mercedes-AMG Petronas F1 Team January 7, 2021
-
6
Tauseef Posted on Feb 21...
-
5
Not FoundYou just hit a route that doesn't exist... the sadness.LoginRadius empowers businesses to deliver a delightful customer experience and win customer trust. Using the LoginRadius Identity...
-
9
Burpsuite http request logging and log parser 發表於 2018-04-22 ...
-
5
Power Up Your Logging in Node.jsMay 9th 2022 new story0Node...
-
4
Microsoft Technologies, Power BILog in to Power BI Desktop as an External (B2B) User
-
15
Efficient Kafka Logging: Strategies, Tools and Techniques for Log AnalysisDive deep into Kafka log management and discover how to optimize Kafka partitions to streamline the efficiency of Kafka logging to eliminate unnecess...
-
6
This blog post will serve as my long-t...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK