log.Fatalf will exit, and
defer func(){...}(...)
will not run 31
32 conn, _, err := sqlmock.New()
33 if err != nil {
34 log.Fatalf("Failed to get mock connection: %v", err) 35 }
36 defer func() { _ = conn.Close() }()
37
log.Fatal will exit, and
defer func(){...}(...)
will not run144
145 err = os.MkdirAll(logConf.RootPath, os.ModePerm)
146 if err != nil {
147 log.Fatal("Failed to create log directory: %v", err)148 }
149
150 for i, mode := range logConf.Modes {
Description
Calls to os.Exit
or log.Fatal
and friends made in function with defer
statements do not execute those statements if control reaches the
os.Exit
or log.Fatal
and friends.
If the os.Exit
function happens in a goroutine, all the goroutines (including
the main one) will terminate immediately without executing any deferred statement.
Internally, log.Fatal
and friends (log.Fatalln
and log.Fatalf
) internally
uses os.Exit(1)
, which results in the immediate termination of the program
without executing the deferred statements.
It is recommended to handle such cases gracefully so that deferred statements are executed to necessary operations such as closing open file descriptors, cleanup, etc.
Bad practice
defer os.Remove(filename)
if bad {
log.Fatalf("something bad happened")
}
Recommended
defer os.Remove(filename)
if bad {
log.Printf("something bad happened")
return // exits with "exit status 0"
}
retcode := 0
defer func() { os.Exit(retcode) }
defer os.Remove(filename)
if bad {
log.Printf("something bad happened")
retcode = 1
return // exits with "exit status 1" because of topmost `defer` call
}