Let's Go پیکربندی و مدیریت خطا › مدیریت خطای متمرکز
قبلی · فهرست · بعدی
فصل 3.4.

مدیریت خطای متمرکز

بیایید برنامه خود را با انتقال برخی از کد مدیریت خطا به متدهای کمکی مرتب کنیم. این به جدا کردن نگرانی‌های ما کمک می‌کند و از تکرار کد در حین پیشرفت ساخت جلوگیری می‌کند.

ادامه دهید و یک فایل helpers.go جدید در زیر دایرکتوری cmd/web اضافه کنید:

$ touch cmd/web/helpers.go

و کد زیر را اضافه کنید:

File: 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() به‌روزرسانی کنید:

File: cmd/web/handlers.go
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"