Documentation
¶
Overview ¶
Package ctrl manages passage of control through a main function.
In case of error main programs typically call os.Exit or log.Fatal. However, this causes the process to terminate immediately and deferred calls are not invoked. Calling log.Panic allows deferred calls to run, but makes a noisy log trace.
This package provides a Run function that performs the main action of a program. Within its dynamic extent, calls to ctrl.Exit and ctrl.Exitf will panic back to Run, which will handle logging and exiting from the process as specified.
Example:
import "github.com/creachadair/ctrl" // A stub main to set up the control handlers. func main() { ctrl.Run(realMain) } // The real program logic goes into this function. func realMain() error { ... }
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Exit ¶
Exit returns control to the most recent invocation of Run, instructing it to exit the process silently with the specified exit status.
Control does not return from a call to Exit. The return type is error so that it can be called in an error return statement.
Example ¶
package main import ( "fmt" "github.com/creachadair/ctrl" ) func catchPanic(f func()) (val interface{}) { ctrl.SetPanic(true) ctrl.SetHook(func(code int, err error) { if code != 0 || err != nil { fmt.Printf("[exit] code=%d err=%v\n", code, err) } }) defer func() { val = recover() }() f() return } func main() { // N.B. catchPanic prevents ctrl.Run from terminating the example // program. You do not need this in production. catchPanic(func() { ctrl.Run(func() error { fmt.Println("Hello") return ctrl.Exit(0) }) fmt.Println("You don't see this") }) }
Output: Hello
func Exitf ¶
Exitf returns control to the most recent invocation of Run, instructing it to exit the process with the specified exit status and formatted log.
Control does not return from a call to Exitf. The return type is error so that it can be called in an error return statement.
Example ¶
package main import ( "fmt" "github.com/creachadair/ctrl" ) func catchPanic(f func()) (val interface{}) { ctrl.SetPanic(true) ctrl.SetHook(func(code int, err error) { if code != 0 || err != nil { fmt.Printf("[exit] code=%d err=%v\n", code, err) } }) defer func() { val = recover() }() f() return } func main() { // N.B. catchPanic prevents ctrl.Run from terminating the example // program. You do not need this in production. catchPanic(func() { ctrl.Run(func() error { fmt.Println("Hello") return ctrl.Exitf(5, "everything is bad") }) fmt.Println("You don't see this") }) }
Output: Hello [exit] code=5 err=everything is bad
func Fatalf ¶
Fatalf is a shorthand for ctrl.Exitf(1, ...), that can be used as a drop-in replacement for calls to log.Fatalf.
Example ¶
package main import ( "fmt" "github.com/creachadair/ctrl" ) func catchPanic(f func()) (val interface{}) { ctrl.SetPanic(true) ctrl.SetHook(func(code int, err error) { if code != 0 || err != nil { fmt.Printf("[exit] code=%d err=%v\n", code, err) } }) defer func() { val = recover() }() f() return } func main() { // N.B. catchPanic prevents ctrl.Run from terminating the example // program. You do not need this in production. catchPanic(func() { ctrl.Run(func() error { fmt.Println("Hello") return ctrl.Fatalf("badness: %d", 25) }) fmt.Println("You don't see this") }) }
Output: Hello [exit] code=1 err=badness: 25
func Run ¶
func Run(main func() error)
Run invokes main. If main returns without error, control returns from Run normally. If main reports an error, it is logged and Run calls os.Exit(1).
During the execution of main a call to ctrl.Exit or ctrl.Exitf returns control to Run, which logs a message and calls os.Exit with the specified return code. Control is handled via the panic mechanism, so deferred calls within main will be executed normally. Panics not generated by this library are propagated normally.
Example (Failure) ¶
package main import ( "errors" "fmt" "github.com/creachadair/ctrl" ) func catchPanic(f func()) (val interface{}) { ctrl.SetPanic(true) ctrl.SetHook(func(code int, err error) { if code != 0 || err != nil { fmt.Printf("[exit] code=%d err=%v\n", code, err) } }) defer func() { val = recover() }() f() return } func main() { // N.B. catchPanic prevents ctrl.Run from terminating the example // program. You do not need this in production. catchPanic(func() { ctrl.Run(func() error { fmt.Println("Hello") return errors.New("goodbye") }) fmt.Println("You do not see this") }) }
Output: Hello [exit] code=1 err=goodbye
Example (Panic) ¶
package main import ( "fmt" "github.com/creachadair/ctrl" ) func catchPanic(f func()) (val interface{}) { ctrl.SetPanic(true) ctrl.SetHook(func(code int, err error) { if code != 0 || err != nil { fmt.Printf("[exit] code=%d err=%v\n", code, err) } }) defer func() { val = recover() }() f() return } func main() { // N.B. catchPanic prevents ctrl.Run from terminating the example // program. You do not need this in production. v := catchPanic(func() { ctrl.Run(func() error { fmt.Println("Hello") panic("omgwtfbbq") }) fmt.Println("You do not see this") }) fmt.Println("panic:", v) }
Output: Hello panic: omgwtfbbq
Example (Success) ¶
package main import ( "fmt" "github.com/creachadair/ctrl" ) func main() { ctrl.Run(func() error { fmt.Println("This is main") return nil }) fmt.Println("That was main") }
Output: This is main That was main
func SetHook ¶
SetHook sets f as an exit hook.
Before Run exits the program, it will call f synchronously with the code and error that motivated the exit. Once the hook returns, Run will exit.
If f == nil, the hook is removed. There is only one exit hook; subsequent calls to SetHook will replace any previous values.
Types ¶
This section is empty.