{"record":{"id":"9d3ec39ced90da70","repo":"unknwon/the-way-to-go_ZH_CN","slug":"bad-end","errorCode":null,"errorMessage":"bad end","messagePattern":"bad end","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"eBook/13.3.md","lineNumber":44,"sourceCode":"\n`log` 包实现了简单的日志功能：默认的 log 对象向标准错误输出中写入并打印每条日志信息的日期和时间。除了 `Println` 和 `Printf` 函数，其它的致命性函数都会在写完日志信息后调用 `os.Exit(1)`，那些退出函数也是如此。而 Panic 效果的函数会在写完日志信息后调用 `panic()`；可以在程序必须中止或发生了临界错误时使用它们，就像当 web 服务器不能启动时那样（参见 [15.4 节](15.4.md) 中的例子）。\n\nlog 包用那些方法 (methods) 定义了一个 `Logger` 接口类型，如果你想自定义日志系统的话可以参考 [http://golang.org/pkg/log/#Logger](http://golang.org/pkg/log/#Logger) 。\n\n这是一个展示 `panic()`，`defer` 和 `recover()` 怎么结合使用的完整例子：\n\n示例 13.3 [panic_recover.go](examples/chapter_13/panic_recover.go)：\n\n```go\n// panic_recover.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc badCall() {\n\tpanic(\"bad end\")\n}\n\nfunc test() {\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tfmt.Printf(\"Panicing %s\\r\\n\", e)\n\t\t}\n\t}()\n\tbadCall()\n\tfmt.Printf(\"After bad call\\r\\n\") // <-- would not reach\n}\n\nfunc main() {\n\tfmt.Printf(\"Calling test\\r\\n\")\n\ttest()\n\tfmt.Printf(\"Test completed\\r\\n\")\n}\n```","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/unknwon/the-way-to-go_ZH_CN/blob/7a54d34d3657084b6a59e5618bd069b912d571aa/eBook/13.3.md#L26-L62","documentation":"Example 13.3 (panic_recover.go): badCall() panics with 'bad end'; test() installs a deferred closure that recovers and prints 'Panicing bad end'. It demonstrates that recover stops the unwinding, deferred functions still execute, and the statement after badCall() ('After bad call') is never reached.","triggerScenarios":"Calling badCall() (or any equivalent panicking function) from a location that lacks the deferred recover; or calling it from a different goroutine — recover only works within the panicking goroutine, so a parent's recover cannot catch a child goroutine's panic.","commonSituations":"Students restructuring the demo and dropping the defer; spawning badCall with 'go' and expecting the caller's recover to fire (it cannot, and the program crashes); assuming execution resumes after the panic point within the same function (it does not — only the deferred wrapper continues).","solutions":["Keep the deferred recover in the same function that transitively calls badCall","If badCall runs in a goroutine, put its own deferred recover inside that goroutine's function","Replace the panic with a returned error when the failure is an expected condition","After recover, explicitly return any default values — code below the panic point never resumes"],"exampleFix":"// before\ngo badCall() // parent recover cannot catch this; process crashes\n\n// after\ngo func() {\n    defer func() {\n        if e := recover(); e != nil {\n            fmt.Printf(\"Panicing %s\\r\\n\", e)\n        }\n    }()\n    badCall()\n}()","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// the canonical pattern from the source — recover in the SAME goroutine\nfunc test() {\n    defer func() {\n        if e := recover(); e != nil {\n            fmt.Printf(\"Panicing %s\\r\\n\", e)\n        }\n    }()\n    badCall()\n    fmt.Printf(\"After bad call\\r\\n\") // unreached if badCall panics\n}","preventionTips":["recover() only works in the goroutine that panicked — every worker goroutine needs its own deferred recover","recover is only meaningful directly deferred inside the panicking function's frame chain","After a recover, set default results explicitly; the function does not resume where it stopped"],"tags":["go","panic","recover","defer","goroutine","tutorial"],"backgroundTag":null,"analyzedSha":"7a54d34d3657084b6a59e5618bd069b912d571aa","analyzedAt":"2026-08-15T15:13:06.026Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}