davideisinger.com

My personal website
Log | Files | Refs | README

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