exception handling - log - Atrapando el pánico en Golang
how to return an error in golang (6)
Con el siguiente código, si no se da un argumento de archivo, se genera un ataque de pánico en la línea 9 panic: runtime error: index out of range
como se esperaba.
¿Cómo puedo ''atrapar'' este pánico y manejarlo directamente cuando le paso algo ( os.Args[1]
) que causa pánico? Al igual que try / catch en PHP o try / except en Python.
He tenido una búsqueda aquí en StackOverflow pero no he encontrado nada que responda a esto como tal.
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open(os.Args[1])
if err != nil {
fmt.Println("Could not open file")
}
fmt.Printf("%s", file)
}
Primero: no querrías hacer esto. El manejo de errores al estilo Try-catch no implica el manejo de errores. En Go, debe verificar len(os.Args)
primero y acceder al elemento 1 solo si está presente.
Para los raros casos en los que necesita atrapar el pánico (¡y su caso no es uno de ellos!) Utilice el defer
en combinación con la recover
. Ver http://golang.org/doc/effective_go.html#recover
Go no es Python, debe verificar correctamente los argumentos antes de usarlo:
func main() {
if len(os.Args) != 2 {
fmt.Printf("usage: %s [filename]/n", os.Args[0])
os.Exit(1)
}
file, err := os.Open(os.Args[1])
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s", file)
}
Tenga en cuenta que el tratamiento de recuperación de un error de Ejecución de pánico (como el intento de indexar una matriz fuera del desencadenante de límites) puede cambiar con ir 1.7 después de la edición 14965
tiempo de ejecución: hacer que los valores de pánico de error de ejecución implementen la interfaz de
Error
Haga que los pánicos de ejecución implementen un error según los pánicos en tiempo de ejecución (especificaciones) , en lugar de pánico con cadenas.
Cuando recupere un error de pánico, podrá hacer:
if _, ok := recovered.(runtime.Error); !ok {
Esto todavía se está evaluando, y como Dave Cheney . menciona:
No sé lo que las personas están haciendo actualmente, pero desde mi POV esto se ha roto durante mucho tiempo y nadie se ha quejado, por lo que dependen explícitamente del comportamiento roto, o a nadie le importa. De cualquier manera, creo que es una buena idea evitar hacer este cambio.
Un programa de pánico se puede recuperar con la función de recover()
incorporada:
La función de
recover
permite que un programa administre el comportamiento de un goroutine en pánico. Supongamos que una funciónG
difiere una funciónD
que llama arecover
y se produce un pánico en una función en la misma rutina en la queG
está ejecutando. Cuando el funcionamiento de las funciones diferidas llega aD
, el valor de retorno de la llamada derecover
deD
será el valor pasado a la llamada depanic
. SiD
regresa normalmente, sin iniciar un nuevopanic
, la secuencia de pánico se detiene. En ese caso, el estado de las funciones llamadas entreG
y la llamada alpanic
se descarta y se reanuda la ejecución normal. Cualquier función diferida porG
antesD
se ejecuta y la ejecución deG
finaliza volviendo a su llamador.El valor de retorno de la recuperación es nulo si se cumple alguna de las siguientes condiciones:
panic
argumento delpanic
fuenil
;- el goroutine no está entrando en pánico;
recover
no fue llamado directamente por una función diferida.
Aquí hay un ejemplo de cómo usar esto:
// access buf[i] and return an error if that fails.
func PanicExample(buf []int, i int) (x int, err error) {
defer func() {
// recover from panic if one occured. Set err to nil otherwise.
if (recover() != nil) {
err = errors.New("array index out of bounds")
}
}()
x = buf[i]
}
Tenga en cuenta que la mayoría de las veces, el pánico no es la solución correcta. El paradigma Go es verificar los errores de forma explícita. Un programa solo debe entrar en pánico si las circunstancias bajo las cuales se produce el pánico no ocurren durante la ejecución ordinaria del programa. Por ejemplo, no ser capaz de abrir un archivo es algo que puede suceder y no debe causar pánico mientras se está quedando sin memoria, vale la pena entrar en pánico. Sin embargo, este mecanismo existe para poder atrapar incluso estos casos y quizás cerrar con gracia.
Algunos paquetes oficiales de Golang usan panic / defer + recover como throw / catch , pero solo cuando necesitan desenrollar una gran pila de llamadas. En el paquete json de Golang, usar pánico / aplazar + recuperar como lanzar / atrapar es la solución más elegante.
de http://blog.golang.org/defer-panic-and-recover
Para ver un ejemplo real de pánico y recuperación, consulte el paquete json de la biblioteca estándar Go. Descodifica datos codificados en JSON con un conjunto de funciones recursivas. Cuando se encuentra JSON malformado, el analizador llama a pánico para desenrollar la pila a la llamada de función de nivel superior, que se recupera del pánico y devuelve un valor de error apropiado (consulte los métodos ''error'' y ''unmarshal'' del tipo decodeState en decodificación .ir).
Busque d.error(
en http://golang.org/src/encoding/json/decode.go
En su ejemplo, la solución "idiomática" es verificar los parámetros antes de usarlos, como han señalado otras soluciones.
Pero, si quiere / necesito capturar algo , puede hacer lo siguiente:
package main
import (
"fmt"
"os"
)
func main() {
defer func() { //catch or finally
if err := recover(); err != nil { //catch
fmt.Fprintf(os.Stderr, "Exception: %v/n", err)
os.Exit(1)
}
}()
file, err := os.Open(os.Args[1])
if err != nil {
fmt.Println("Could not open file")
}
fmt.Printf("%s", file)
}
Tuve que atrapar el pánico en un caso de prueba. Me redirigieron aquí.
func.go
var errUnexpectedClose = errors.New("Unexpected Close")
func closeTransaction(a bool) {
if a == true {
panic(errUnexpectedClose)
}
}
func_test.go
func TestExpectedPanic() {
got := panicValue(func() { closeTransaction(true) })
a, ok := got.(error)
if a != errUnexpectedClose || !ok {
t.Error("Expected ", errUnexpectedClose.Error())
}
}
func panicValue(fn func()) (recovered interface{}) {
defer func() {
recovered = recover()
}()
fn()
return
}
Utilizado desde https://github.com/golang/go/commit/e4f1d9cf2e948eb0f0bb91d7c253ab61dfff3a59 (ref de VonC)