مدیریت خطای متمرکز
بیایید برنامه خود را با انتقال برخی از کد مدیریت خطا به متدهای کمکی مرتب کنیم. این به جدا کردن نگرانیهای ما کمک میکند و از تکرار کد در حین پیشرفت ساخت جلوگیری میکند.
ادامه دهید و یک فایل helpers.go جدید در زیر دایرکتوری cmd/web اضافه کنید:
$ touch cmd/web/helpers.go
و کد زیر را اضافه کنید:
package main import ( "net/http" ) // The serverError helper writes a log entry at Error level (including the request // method and URI as attributes), then sends a generic 500 Internal Server Error // response to the user. func (app *application) serverError(w http.ResponseWriter, r *http.Request, err error) { var ( method = r.Method uri = r.URL.RequestURI() ) app.logger.Error(err.Error(), "method", method, "uri", uri) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } // The clientError helper sends a specific status code and corresponding description // to the user. We'll use this later in the book to send responses like 400 "Bad // Request" when there's a problem with the request that the user sent. func (app *application) clientError(w http.ResponseWriter, status int) { http.Error(w, http.StatusText(status), status) }
در این کد، ما همچنین چیز جدید دیگری معرفی کردهایم: تابع http.StatusText(). این یک نمایش متنی دوستدار انسان از یک کد وضعیت HTTP داده شده برمیگرداند — به عنوان مثال http.StatusText(400) رشته "Bad Request" را برمیگرداند و http.StatusText(500) رشته "Internal Server Error" را برمیگرداند.
حالا که این انجام شد، به فایل handlers.go خود برگردید و آن را برای استفاده از متد کمکی جدید serverError() بهروزرسانی کنید:
package main import ( "fmt" "html/template" "net/http" "strconv" ) func (app *application) home(w http.ResponseWriter, r *http.Request) { w.Header().Add("Server", "Go") files := []string{ "./ui/html/base.tmpl", "./ui/html/partials/nav.tmpl", "./ui/html/pages/home.tmpl", } ts, err := template.ParseFiles(files...) if err != nil { app.serverError(w, r, err) // Use the serverError() helper. return } err = ts.ExecuteTemplate(w, "base", nil) if err != nil { app.serverError(w, r, err) // Use the serverError() helper. } } ...
وقتی که بهروزرسانی شد، برنامه خود را مجدداً راهاندازی کنید و درخواستی به http://localhost:4000 در مرورگر خود بدهید.
دوباره، این باید منجر به ایجاد خطای (عمدی) ما شود و باید ورودی لاگ مربوطه را در ترمینال خود ببینید، شامل متد درخواست و URI به عنوان ویژگیها.
$ go run ./cmd/web time=2024-03-18T11:29:23.000+00:00 level=INFO msg="starting server" addr=:4000 time=2024-03-18T11:29:23.000+00:00 level=ERROR msg="open ./ui/html/pages/home.tmpl: no such file or directory" method=GET uri=/
بازگرداندن خطای عمدی
در این مرحله دیگر به خطای عمدی نیاز نداریم، بنابراین ادامه دهید و آن را مانند این اصلاح کنید:
$ mv ui/html/pages/home.bak ui/html/pages/home.tmpl
اطلاعات اضافی
ردیابی پشته
میتوانید از تابع debug.Stack() برای دریافت یک ردیابی پشته که مسیر اجرای برنامه را برای گوروتین فعلی ترسیم میکند استفاده کنید. شامل کردن این به عنوان یک ویژگی در ورودیهای لاگ شما میتواند برای اشکالزدایی خطاها مفید باشد.
اگر میخواهید، میتوانید متد serverError() را بهروزرسانی کنید تا یک ردیابی پشته در ورودیهای لاگ شامل کند، مانند این:
package main import ( "net/http" "runtime/debug" ) func (app *application) serverError(w http.ResponseWriter, r *http.Request, err error) { var ( method = r.Method uri = r.URL.RequestURI() // Use debug.Stack() to get the stack trace. This returns a byte slice, which // we need to convert to a string so that it's readable in the log entry. trace = string(debug.Stack()) ) // Include the trace in the log entry. app.logger.Error(err.Error(), "method", method, "uri", uri, "trace", trace) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) }
خروجی ورودی لاگ سپس شبیه این خواهد بود (شکستن خطوط برای خوانایی اضافه شده است):
time=2024-03-18T11:29:23.000+00:00 level=ERROR msg="open ./ui/html/pages/home.tmpl:
no such file or directory" method=GET uri=/ trace="goroutine 6 [running]:\nruntime/
debug.Stack()\n\t/usr/local/go/src/runtime/debug/stack.go:24 +0x5e\nmain.(*applicat
ion).serverError(0xc00006c048, {0x8221b0, 0xc0000f40e0}, 0x3?, {0x820600, 0xc0000ab
5c0})\n\t/home/alex/code/snippetbox/cmd/web/helpers.go:14 +0x74\nmain.(*application
).home(0x10?, {0x8221b0?, 0xc0000f40e0}, 0xc0000fe000)\n\t/home/alex/code/snippetbo
x/cmd/web/handlers.go:24 +0x16a\nnet/http.HandlerFunc.ServeHTTP(0x4459e0?, {0x8221b
0?, 0xc0000f40e0?}, 0x6cc57a?)\n\t/usr/local/go/src/net/http/server.go:2136 +0x29\n
net/http.(*ServeMux).ServeHTTP(0xa7fde0?, {0x8221b0, 0xc0000f40e0}, 0xc0000fe000)\n
\t/usr/local/go/src/net/http/server.go:2514 +0x142\nnet/http.serverHandler.ServeHTT
P({0xc0000aaf00?}, {0x8221b0?, 0xc0000f40e0?}, 0x6?)\n\t/usr/local/go/src/net/http/
server.go:2938 +0x8e\nnet/http.(*conn).serve(0xc0000c0120, {0x8229e0, 0xc0000aae10})
\n\t/usr/local/go/src/net/http/server.go:2009 +0x5f4\ncreated by net/http.(*Server).
Serve in goroutine 1\n\t/usr/local/go/src/net/http/server.go:3086 +0x5cb\n"