1

One of the things I love about Go is how they encourage passing errors as return values, but when it comes to logging what is the most maintainable solution: passing the error as far back down the execution stack as possible and logging everything there, or logging it in the function it occurred there and then?

So the former:

func main() {
    err := DoSomething()
    if err != nil {
        log.Printf(err)
    }
}

func DoSomething() error {
    return DoSomethingElse()
}

func DoSomethingElse() error {
    return ThisFunctionMayCauseAnErrorAndIsHorriblyNamed()
}

Or

func main() {
    _ := DoSomething()
}

func DoSomething() error {
    return DoSomethingElse()
}

func DoSomethingElse() error {
    err := ThisFunctionMayCauseAnErrorAndIsHorriblyNamed()
    if err != nil {
        log.Printf(err)
    }
}
3

The main concern I have found with logging close to the source is that you are in danger of logging the same errors twice. In the second case we might imagine someone calling DoSomethingElse() without being sure whether it would log the error. These cases become increasingly common as the size and complexity of the component increases, and the number of times an error is logged scales with size.

When errors are logged multiple times in multiple places, the density of useful information in the logs decreases and so the value of those logs also decreases.

When programmers instead follow the first example and log from a higher level there is much less danger of logs being repeated. Furthermore, at higher levels logs can include much more system meaning. DoSomethingElse() may only be trying to send a SQL string to a Postgres database, while main is trying to purge all expired user accounts.

The logs from DoSomethingElse can only include very low level information -- the SQL query that was sent, the connection string tried, etc. -- while main can provide all of these (because the error was transmitted upwards) but also provide a high level description, e.g. "Could not connect to the database while attempting to purge stale accounts.".

These high level descriptions make logs more valuable by providing useful glosses on the errors encountered. So in general I would recommend logging from the highest level possible.

2

Main adventage of second solution ( pass errors up ) is: when you run mutiple functions in row that can throw errors, you don't have to make if for each one, but you can just do one if on higher level. And this solution is also encouraged on Go lang blog ( Simplifying repetitive error handling section ).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.