stephenn-com-kbiijs.txt (8594B)
1 [1]Stephen's Tech Blog 2 3 • [2]Webhook Wizard 🧙♂️ 4 5 Gopher Wrangling. Effective error handling in Go 6 7 June 19, 2023 · 4 min · Stephen Nancekivell 8 Table of Contents 9 10 □ [3]Guiding principle 11 • [4]1. Always handle errors 12 • [5]2. Log errors in one layer 13 • [6]3. Returning async errors 14 • [7]4. Wrapping errors 15 • [8]5. Downgrade errors Warnings 16 17 When programming in Go, the amount of error handling is something that slaps 18 you in the face. Most API’s you deal with will expose errors. It can become 19 overwhelming, but with a few tips and a guiding principle we can make handling 20 errors easy, keep our code clean and give you the confidence that nothing is 21 breaking in production. 22 23 A cartoon of a crazy stressed programmer pulling their hair out in front of 24 lots of screens showing a error exclamation marks 25 26 A cartoon of a crazy stressed programmer pulling their hair out in front of 27 lots of screens showing a error exclamation marks 28 29 Guiding principle[9]# 30 31 The goal for our error handling strategy is that it should require minimal 32 effort and provide an easy way to debug any errors that do occur. 33 34 We wont cover strategies like retrying because they are less common and also 35 expose errors. 36 37 1. Always handle errors[10]# 38 39 Always handle errors. Sometimes it’s tempting to skip one, you might not expect 40 that error to ever happen. But that’s why it’s an exception! You need to handle 41 it so that you can find out clearly if it ever does happen. 42 43 If you don’t handle the error, the expected value will be something else and 44 just lead to another error that will be harder to debug, or worse it could lead 45 to data corruption. 46 47 In most cases to handle the error all you need to do is return it to the caller 48 of your method, where they can log it. 49 50 For example, when refreshing some data you might load it, then save it. If you 51 skip the error handling it could overwrite potentially useful data with corrupt 52 data. 53 54 👎 Bad error handling 55 56 func refresh() { 57 bytes, _ := loadData() 58 saveData(bytes) 59 } 60 61 👍 Good error handling 62 63 func refresh() error { 64 bytes, err := loadData() 65 if err != nil { 66 return err 67 } 68 saveData(bytes) 69 } 70 71 2. Log errors in one layer[11]# 72 73 You always want to log your errors, ideally to something that will notify you 74 about the error, so you can fix it. There is no point logging the error 75 multiple times at every layer. Make it the top layer’s responsibility and don’t 76 log in any services or lower level code. 77 78 Make sure your logging framework is including stack traces so you can trace the 79 error to its cause. 80 81 For example in a web app you would log the error in the http handler when 82 returning the Internal Server status code. 83 84 👍 Good error handling 85 86 func refresh() error { 87 bytes, err := loadData() 88 if err != nil { 89 return err 90 } 91 saveData(bytes) 92 } 93 94 func (h *handlers) handleRefreshRequest(w http.ResponseWriter, r *http.Request) { 95 err := refresh() 96 if err != nil { 97 log.Error("unexpected error processing request %w", err) 98 w.WriteHeader(http.StatusInternalServerError) 99 return 100 } 101 102 w.WriteHeader(http.StatusOK) 103 } 104 105 3. Returning async errors[12]# 106 107 When processing data concurrently using a go-func’s, it can be annoying to 108 return the error. But if you don’t your app will be less maintainable. To 109 handle async errors, return them via a channel to the calling thread. 110 111 👎 Bad error handling 112 113 func refreshManyConcurrently() { 114 go func(){ 115 refresh(1) 116 }() 117 118 go func(){ 119 refresh(2) 120 }() 121 } 122 123 👍 Good error handling 124 125 func refreshManyConcurrently() error { 126 errors := make(chan error, 2) 127 go func(){ 128 errors <- refresh(1) 129 }() 130 131 go func(){ 132 errors <- refresh(2) 133 }() 134 return multierror.Combine(<-errors, <- errors) 135 } 136 137 When calling functions that return a value and a possible error using a type 138 like Result[T], to wrap the response to pass on the channel. 139 140 type Result[T any] struct { 141 Value T 142 Error error 143 } 144 145 4. Wrapping errors[13]# 146 147 Sometimes you want to add additional context to an error message. Eg to include 148 the id of the request that caused the error. You can use fmt.error for this. 149 150 err := saveToDb(user) 151 if err != nil { 152 return fmt.errorf("unexpected error saving user. userId=%v error=%w", user.Id, err) 153 } 154 155 Usually this isn’t necessary and its better to just return the error unwrapped. 156 157 5. Downgrade errors Warnings[14]# 158 159 There are types of errors that regularly occur during normal operation. The 160 system might not be able to prevent them all the time, but they don’t need to 161 investigate every time. It is better to treat them as warnings rather than 162 errors. These might be for things like timeouts or intermittent connection 163 errors. 164 165 👍 Good error handling 166 167 func (h *handlers) handleRefreshRequest(w http.ResponseWriter, r *http.Request) { 168 err := refresh() 169 if err != nil { 170 if err == context.DeadlineExceeded { 171 log.Warn("Timeout error processing request %w", err) 172 } else { 173 log.Error("unexpected error processing request %w", err) 174 } 175 176 w.WriteHeader(http.StatusInternalServerError) 177 return 178 } 179 180 w.WriteHeader(http.StatusOK) 181 } 182 183 [15]« Prev 184 PDFs on the Fly: Programmatically Transforming Webpages into PDFs [16]Next » 185 How to Serve Web Sockets with Http4s 186 [17][18][19][20][21][22] 187 [23]© 2023 [24]Stephen's Tech Blog 188 [25][26][27][28] 189 190 References: 191 192 [1] https://stephenn.com/ 193 [2] https://webhookwizard.com/ 194 [3] https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/#guiding-principle 195 [4] https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/#1-always-handle-errors 196 [5] https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/#2-log-errors-in-one-layer 197 [6] https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/#3-returning-async-errors 198 [7] https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/#4-wrapping-errors 199 [8] https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/#5-downgrade-errors-warnings 200 [9] https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/#guiding-principle 201 [10] https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/#1-always-handle-errors 202 [11] https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/#2-log-errors-in-one-layer 203 [12] https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/#3-returning-async-errors 204 [13] https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/#4-wrapping-errors 205 [14] https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/#5-downgrade-errors-warnings 206 [15] https://stephenn.com/2023/06/pdfs-on-the-fly-programmatically-transforming-webpages-into-pdfs/ 207 [16] https://stephenn.com/2022/07/web-sockets-with-http4s/ 208 [17] https://twitter.com/intent/tweet/?text=Gopher%20Wrangling.%20Effective%20error%20handling%20in%20Go&url=https%3a%2f%2fstephenn.com%2f2023%2f06%2fgopher-wrangling.-effective-error-handling-in-go%2f&hashtags= 209 [18] https://www.linkedin.com/shareArticle?mini=true&url=https%3a%2f%2fstephenn.com%2f2023%2f06%2fgopher-wrangling.-effective-error-handling-in-go%2f&title=Gopher%20Wrangling.%20Effective%20error%20handling%20in%20Go&summary=Gopher%20Wrangling.%20Effective%20error%20handling%20in%20Go&source=https%3a%2f%2fstephenn.com%2f2023%2f06%2fgopher-wrangling.-effective-error-handling-in-go%2f 210 [19] https://reddit.com/submit?url=https%3a%2f%2fstephenn.com%2f2023%2f06%2fgopher-wrangling.-effective-error-handling-in-go%2f&title=Gopher%20Wrangling.%20Effective%20error%20handling%20in%20Go 211 [20] https://facebook.com/sharer/sharer.php?u=https%3a%2f%2fstephenn.com%2f2023%2f06%2fgopher-wrangling.-effective-error-handling-in-go%2f 212 [21] https://api.whatsapp.com/send?text=Gopher%20Wrangling.%20Effective%20error%20handling%20in%20Go%20-%20https%3a%2f%2fstephenn.com%2f2023%2f06%2fgopher-wrangling.-effective-error-handling-in-go%2f 213 [22] https://telegram.me/share/url?text=Gopher%20Wrangling.%20Effective%20error%20handling%20in%20Go&url=https%3a%2f%2fstephenn.com%2f2023%2f06%2fgopher-wrangling.-effective-error-handling-in-go%2f 214 [23] https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/#top 215 [24] https://stephenn.com/ 216 [25] https://github.com/stephennancekivell 217 [26] https://twitter.com/hi_stephen_n 218 [27] https://www.linkedin.com/in/stephen-nancekivell-77003039 219 [28] https://stackoverflow.com/users/893854/stephen