Golang中如何在代码异常或错误时发送邮件
Golang中如何在代码异常或错误时发送邮件 我想在应用程序出现任何错误时发送电子邮件。 包含文件名和行号的 .go 文件以及异常信息。
这将对我们有很大帮助。
3 回复
我不推荐这样做。为什么不使用常规的日志记录呢?如果你的问题在于行号,并且有时需要花费很长时间来查找,我也遇到过同样的问题。
以下是我用于HTTP错误响应的代码,但你可以使用类似的方法,并总能立即找到错误的原因。
type ErrorResponse struct {
Detail string `json:"detail"` // 这是我自己的注释,说明导致错误的原因。
Error string `json:"error"`
}
userpermissions, err := app.Queries.GetPurePermissionsByUserId(context.Background(), user.ID)
if err != nil {
utils.RespondWithJSON(w, utils.ErrorResponse{
Detail: "Error getting permissions for user",
Error: err.Error(),
})
return
}
在Go中实现错误时发送邮件,可以通过自定义错误处理中间件或全局错误捕获机制来实现。以下是一个完整的示例,展示了如何在发生panic或错误时发送包含文件名、行号和错误信息的邮件:
package main
import (
"fmt"
"log"
"net/smtp"
"os"
"runtime/debug"
"strings"
"time"
)
// 邮件配置结构体
type MailConfig struct {
SMTPHost string
SMTPPort string
FromEmail string
FromPassword string
ToEmails []string
}
// 错误信息结构体
type ErrorInfo struct {
Time string
File string
Line int
Function string
Error string
Stack string
}
var mailConfig = MailConfig{
SMTPHost: "smtp.gmail.com",
SMTPPort: "587",
FromEmail: "your-email@gmail.com",
FromPassword: "your-password",
ToEmails: []string{"admin@example.com"},
}
// 发送邮件的函数
func sendErrorEmail(errorInfo ErrorInfo) error {
auth := smtp.PlainAuth("", mailConfig.FromEmail, mailConfig.FromPassword, mailConfig.SMTPHost)
subject := fmt.Sprintf("Subject: Application Error Alert - %s\n", time.Now().Format("2006-01-02 15:04:05"))
mime := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n"
body := fmt.Sprintf(`
<html>
<body>
<h2>应用程序错误报告</h2>
<p><strong>发生时间:</strong> %s</p>
<p><strong>文件位置:</strong> %s:%d</p>
<p><strong>函数名:</strong> %s</p>
<p><strong>错误信息:</strong> %s</p>
<h3>堆栈跟踪:</h3>
<pre>%s</pre>
</body>
</html>
`, errorInfo.Time, errorInfo.File, errorInfo.Line, errorInfo.Function, errorInfo.Error, errorInfo.Stack)
msg := []byte(subject + mime + body)
err := smtp.SendMail(
mailConfig.SMTPHost+":"+mailConfig.SMTPPort,
auth,
mailConfig.FromEmail,
mailConfig.ToEmails,
msg,
)
return err
}
// 获取调用者信息的函数
func getCallerInfo(skip int) (file string, line int, function string) {
pc, file, line, ok := runtime.Caller(skip)
if !ok {
return "unknown", 0, "unknown"
}
// 获取函数名
fn := runtime.FuncForPC(pc)
if fn != nil {
function = fn.Name()
}
// 简化文件路径,只保留最后两级目录
parts := strings.Split(file, "/")
if len(parts) > 2 {
file = strings.Join(parts[len(parts)-2:], "/")
}
return file, line, function
}
// 全局错误处理函数
func HandlePanic() {
if r := recover(); r != nil {
// 获取堆栈信息
stack := debug.Stack()
// 获取调用者信息(跳过两层:当前函数和panic调用)
file, line, function := getCallerInfo(3)
errorInfo := ErrorInfo{
Time: time.Now().Format("2006-01-02 15:04:05"),
File: file,
Line: line,
Function: function,
Error: fmt.Sprintf("%v", r),
Stack: string(stack),
}
// 尝试发送邮件
if err := sendErrorEmail(errorInfo); err != nil {
log.Printf("发送错误邮件失败: %v", err)
}
// 记录到日志
log.Printf("PANIC: %v\nFile: %s:%d\nFunction: %s\nStack:\n%s",
r, file, line, function, stack)
// 重新panic,让程序终止或由上层处理
panic(r)
}
}
// 错误包装函数,用于普通错误
func HandleError(err error, skip int) error {
if err != nil {
file, line, function := getCallerInfo(skip)
errorInfo := ErrorInfo{
Time: time.Now().Format("2006-01-02 15:04:05"),
File: file,
Line: line,
Function: function,
Error: err.Error(),
Stack: string(debug.Stack()),
}
// 异步发送邮件,避免阻塞主流程
go func() {
if sendErr := sendErrorEmail(errorInfo); sendErr != nil {
log.Printf("发送错误邮件失败: %v", sendErr)
}
}()
log.Printf("ERROR: %v\nFile: %s:%d\nFunction: %s",
err, file, line, function)
}
return err
}
// 使用示例
func main() {
// 设置全局panic处理
defer HandlePanic()
// 示例1: 触发panic
func() {
defer HandlePanic()
panic("测试panic错误")
}()
// 示例2: 处理普通错误
err := someFunction()
if HandleError(err, 2) != nil {
// 处理错误逻辑
}
// 示例3: 在HTTP服务器中使用
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
defer HandlePanic()
// 业务逻辑
if err := processRequest(r); HandleError(err, 3) != nil {
http.Error(w, "内部服务器错误", http.StatusInternalServerError)
}
})
}
func someFunction() error {
return fmt.Errorf("模拟业务错误")
}
func processRequest(r *http.Request) error {
// 模拟处理错误
return fmt.Errorf("请求处理失败")
}
// 中间件版本(用于HTTP服务)
func ErrorMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if r := recover(); r != nil {
stack := debug.Stack()
file, line, function := getCallerInfo(4)
errorInfo := ErrorInfo{
Time: time.Now().Format("2006-01-02 15:04:05"),
File: file,
Line: line,
Function: function,
Error: fmt.Sprintf("%v", r),
Stack: string(stack),
}
go sendErrorEmail(errorInfo)
log.Printf("HTTP PANIC: %v\nFile: %s:%d\nFunction: %s",
r, file, line, function)
http.Error(w, "内部服务器错误", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
这个实现提供了以下功能:
- 邮件发送:使用标准库的
smtp包发送HTML格式的错误邮件 - 错误信息收集:自动捕获文件名、行号、函数名和完整堆栈跟踪
- 两种错误处理:
HandlePanic():用于处理panic,会终止程序执行HandleError():用于处理普通错误,异步发送邮件不阻塞主流程
- HTTP中间件:
ErrorMiddleware可以集成到HTTP服务器中 - 配置灵活:通过
MailConfig结构体配置邮件服务器参数
使用时需要注意:
- 需要替换邮件配置中的实际SMTP服务器信息
- 对于Gmail,可能需要启用"不够安全的应用"或使用应用专用密码
- 生产环境中建议将邮件发送改为异步队列处理,避免影响主程序性能
- 可以考虑添加邮件发送失败的重试机制和限流控制

