Let's Go تمرین‌های راهنما › اضافه کردن حالت دیباگ
قبلی · فهرست · بعدی
فصل ۱۶.۲.

اضافه کردن حالت دیباگ (Adding Debug Mode)

اگر از فریم‌ورک‌های وب (Web Frameworks) برای زبان‌های دیگر مانند Django یا Laravel استفاده کرده‌اید، احتمالاً با ایده‌ی حالت دیباگ (Debug Mode) آشنا هستید که در آن خطاهای دقیق (Detailed Errors) به جای پیام عمومی (Generic Message) "خطای داخلی سرور" به کاربر نمایش داده می‌شود.

هدف شما در این تمرین این است که یک حالت «دیباگ» مشابه برای برنامه خود تنظیم کنید که با استفاده از فلگ خط فرمان (Command Line Flag) -debug به صورت زیر فعال شود:

$ go run ./cmd/web -debug

هنگام اجرای برنامه در حالت دیباگ، هرگونه خطاهای دقیق (Detailed Errors) و ردیابی استک (Stack Trace) باید در مرورگر نمایش داده شود، مشابه این:

16.02-01.png

مرحله ۱

یک فلگ خط فرمان (Command Line Flag) جدید با نام debug و مقدار پیش‌فرض (Default Value) false ایجاد کنید. سپس مقدار این فلگ خط فرمان را از طریق ساختار برنامه (Application Struct) در دسترس هندلرهای خود قرار دهید.

نکته: تابع flag.Bool() برای این کار مناسب‌ترین است.

نمایش کد پیشنهادی

مرحله ۲

به فایل cmd/web/helpers.go بروید و تابع کمکی (Helper Function) serverError() را به‌روزرسانی کنید تا در صورت تنظیم فلگ debug، یک پیام خطا (Error Message) و ردیابی استک (Stack Trace) دقیق در پاسخ HTTP (HTTP Response) رندر کند. در غیر این صورت، پیام خطای عمومی را به صورت عادی ارسال کنید. می‌توانید ردیابی استک را با استفاده از تابع debug.Stack() دریافت کنید.

نمایش کد پیشنهادی

مرحله ۳

تغییرات را امتحان کنید. برنامه را اجرا کنید و با استفاده از DSN بدون پارامتر parseTime=true یک خطای زمان اجرا ایجاد کنید:

$ go run ./cmd/web/ -debug -dsn=web:pass@/snippetbox

بازدید از https://localhost:4000/ باید منجر به پاسخی مانند این شود:

16.02-01.png

اجرای مجدد برنامه بدون فلگ -debug باید منجر به پیام عمومی "خطای داخلی سرور" شود.

کد پیشنهادی

کد پیشنهادی برای مرحله ۱

File: cmd/web/main.go
package main

...

type application struct {
    debug          bool // Add a new debug field.
    logger        *slog.Logger
    snippets       models.SnippetModelInterface
    users          models.UserModelInterface
    templateCache  map[string]*template.Template
    formDecoder    *form.Decoder
    sessionManager *scs.SessionManager
}

func main() {
    addr := flag.String("addr", ":4000", "HTTP network address")
    dsn := flag.String("dsn", "web:pass@/snippetbox?parseTime=true", "MySQL data source name")
    // Create a new debug flag with the default value of false.
    debug := flag.Bool("debug", false, "Enable debug mode")
    flag.Parse()

    logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

    db, err := openDB(*dsn)
    if err != nil {
        logger.Error(err.Error())
        os.Exit(1)
    }
    defer db.Close()

    templateCache, err := newTemplateCache()
    if err != nil {
        logger.Error(err.Error())
        os.Exit(1)
    }

    formDecoder := form.NewDecoder()

    sessionManager := scs.New()
    sessionManager.Store = mysqlstore.New(db)
    sessionManager.Lifetime = 12 * time.Hour
    sessionManager.Cookie.Secure = true

    app := &application{
        debug:          *debug, // Add the debug flag value to the application struct.
        logger:         logger,
        snippets:       &models.SnippetModel{DB: db},
        users:          &models.UserModel{DB: db},
        templateCache:  templateCache,
        formDecoder:    formDecoder,
        sessionManager: sessionManager,
    }

    tlsConfig := &tls.Config{
        CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256},
    }

    srv := &http.Server{
        Addr:         *addr,
        Handler:      app.routes(),
        ErrorLog:     slog.NewLogLogger(logger.Handler(), slog.LevelError),
        TLSConfig:    tlsConfig,
        IdleTimeout:  time.Minute,
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 10 * time.Second,
    }

    logger.Info("starting server", "addr", srv.Addr)

    err = srv.ListenAndServeTLS("./tls/cert.pem", "./tls/key.pem")
    logger.Error(err.Error())
    os.Exit(1)
}

...

کد پیشنهادی برای مرحله ۲

File: cmd/web/helpers.go
...

func (app *application) serverError(w http.ResponseWriter, r *http.Request, err error) {
    var (
        method = r.Method
        uri    = r.URL.RequestURI()
        trace  = string(debug.Stack())
    )

    app.logger.Error(err.Error(), "method", method, "uri", uri)

    if app.debug {
        body := fmt.Sprintf("%s\n%s", err, trace)
        http.Error(w, body, http.StatusInternalServerError)
        return
    }

    http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}

...

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

اصطلاح فارسی معادل انگلیسی توضیح
حالت دیباگ Debug Mode حالت اشکال‌زدایی برنامه
فریم‌ورک‌های وب Web Frameworks چارچوب‌های توسعه وب
خطاهای دقیق Detailed Errors پیام‌های خطای جزئی
پیام عمومی Generic Message پیام خطای کلی
فلگ خط فرمان Command Line Flag پارامتر ورودی برنامه
ردیابی استک Stack Trace مسیر اجرای برنامه
مقدار پیش‌فرض Default Value مقدار اولیه پارامتر
ساختار برنامه Application Struct ساختار داده اصلی برنامه
تابع کمکی Helper Function تابع کمکی برای عملیات رایج
پیام خطا Error Message توضیح خطای رخ داده
پاسخ HTTP HTTP Response پاسخ ارسالی به مرورگر