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

مدیریت خطای متمرکز (Centralized Error Handling)

بیایید برنامه خود را با انتقال بخشی از کد مدیریت خطا به متدهای کمکی (Helper Methods) مرتب کنیم. این کار به جداسازی نگرانی‌ها (Separation of Concerns) کمک می‌کند و از تکرار کد در طول ساخت جلوگیری می‌کند.

یک فایل جدید به نام 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=/

بازگرداندن خطای عمدی (Reverting the Deliberate Error)

در این مرحله دیگر به خطای عمدی نیاز نداریم، بنابراین آن را به این صورت برطرف کنید:

$ mv ui/html/pages/home.bak ui/html/pages/home.tmpl

اطلاعات اضافی (Additional Information)

ردیابی پشته (Stack Traces)

می‌توانید از تابع 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/letsgofa/code/snippetbox/cmd/web/helpers.go:14 +0x74\nmain.(*application
   ).home(0x10?, {0x8221b0?, 0xc0000f40e0}, 0xc0000fe000)\n\t/home/letsgofa/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"

واژه‌نامه اصطلاحات فنی

اصطلاح فارسی معادل انگلیسی توضیح
مدیریت خطای متمرکز Centralized Error Handling روشی برای مدیریت خطاها در یک مکان مشخص و یکپارچه
متدهای کمکی Helper Methods توابع کمکی که برای انجام وظایف تکراری استفاده می‌شوند
جداسازی نگرانی‌ها Separation of Concerns اصل طراحی که هر بخش از کد باید یک مسئولیت مشخص داشته باشد
کد وضعیت HTTP HTTP Status Code کد عددی که نتیجه یک درخواست HTTP را نشان می‌دهد
ردیابی پشته Stack Trace نمایش مسیر اجرای برنامه تا نقطه بروز خطا
گوروتین Goroutine واحد اجرای همزمان در Go
اشکال‌زدایی Debugging فرآیند شناسایی و رفع خطاها در برنامه
خطای عمدی Deliberate Error خطایی که به صورت عمدی برای آزمایش سیستم ایجاد می‌شود
کد وضعیت داخلی سرور Internal Server Error خطای 500 که نشان‌دهنده مشکلی در سمت سرور است
کد وضعیت درخواست بد Bad Request خطای 400 که نشان‌دهنده مشکلی در درخواست کاربر است