Using log.Fatalf in main can prevent deferred functions from running.
log.Fatalf logs the message and then calls os.Exit(1). Unlike a normal return, os.Exit terminates the process immediately, so Go does not unwind the stack and pending defer statements are not executed.
For example:
package main
import (
"fmt"
"log"
)
func main() {
defer fmt.Println("cleanup executed")
fmt.Println("starting")
log.Fatalf("something went wrong")
}
Go playground example.
Output
starting
something went wrong
The “cleanup executed” message is never printed because log.Fatalf terminates the process before the deferred function can run.
A better pattern is to move the application logic into a function that returns an error:
package main
import (
"errors"
"fmt"
"log"
)
func main() {
if err := run(); err != nil {
log.Fatalf("application failed: %v", err)
}
}
func run() error {
defer fmt.Println("cleanup executed")
fmt.Println("starting")
return errors.New("something went wrong")
}
Go playground example
Output
starting
cleanup executed
2009/11/10 23:00:00 application failed: something went wrong
Because there is no log.Fatal or os.Exit, the functions return normally and Go executes their deferred functions during stack unwinding.
In general, application code should return errors rather than terminate the process directly. This makes cleanup with defer reliable and also makes the code easier to test.