The http.Server struct
Although http.ListenAndServe() is very useful in short examples and tutorials, in real-world applications it’s more common to manually create and use a http.Server struct instead. Doing this opens up the opportunity to customize the behavior of your server.
So in preparation for that, let’s quickly update our main.go file to stop using the http.ListenAndServe() shortcut, and manually create and use a http.Server struct instead.
func main() { addr := flag.String("addr", ":4000", "HTTP network address") dbDriver := flag.String("dbdriver", "mysql", "Database driver name") dsn := flag.String("dsn", "zeb:zebpwd@tcp(localhost:3306)/snippetbox?parseTime=true", "MySQL data source name") flag.Parse() logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) db, err := openDB(*dbDriver, *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 app := &application{ logger: logger, snippet: &models.SnippetModel{DB: db}, templateCache: templateCache, formDecoder: formDecoder, sessionManager: sessionManager, } // Initialize a new http.Server struct. We set the Addr and Handler fields so that the server // uses the same network address and routes as before. srv := &http.Server{ Addr: *addr, Handler: app.routes(), } logger.Info("starting server", "addr", *addr) // Call the ListenAndServe() method on our new http.Server struct to start the server. err = srv.ListenAndServe() logger.Error(err.Error()) os.Exit(1) }
The server error log
It’s important to be aware that Go’s http.Server may write its own log entries relating to things like unrecovered panics, or problems accepting or writing to HTTP connections.
By default, it writes these entries using the standard logger — which means they will be written to the standard error stream (instead of standard out like our other log entries), and they won’t be in the same format as the rest of our nice structured log entries.
To demonstrate this, let’s add a deliberate error to our application and set a Content-Length header with an invalid value on our responses. Go ahead and update the render() helper like so:
func (app *application) render(w http.ResponseWriter, r *http.Request, status int, page string, data templateData) { ts, ok := app.templateCache[page] if !ok { err := fmt.Errorf("the template %s does not exist", page) app.serverError(w, r, err) return } buf := new(bytes.Buffer) err := ts.ExecuteTemplate(buf, "base", data) if err != nil { app.serverError(w, r, err) return } // Deliberate error: set a Content-Length header with an invalid (non-integer) value. w.Header().Set("Content-Length", "this isn't an integer!") w.WriteHeader(status) buf.WriteTo(w) }
Then run the application, make a request to http://localhost:4000 , and check the application log. You should see that it looks similar to this:
zzh@ZZHPC:/zdata/Github/snippetbox$ go run ./cmd/web time=2024-09-05T19:13:17.197+08:00 level=INFO msg="starting server" addr=:4000 time=2024-09-05T19:13:28.452+08:00 level=INFO msg="received request" ip=[::1]:57134 proto=HTTP/1.1 method=GET uri=/ 2024/09/05 19:13:28 http: invalid Content-Length of "this isn't an integer!"
Unfortunately, it’s not possible to configure http.Server to use our new structured logger directly. Instead, we have to convert our structured logger handler into a *log.Logger which writes log entries at a specific fixed level, and then register that with the http.Server . We can do this conversion using the slog.NewLogLogger() function, like so:
func main() { addr := flag.String("addr", ":4000", "HTTP network address") dbDriver := flag.String("dbdriver", "mysql", "Database driver name") dsn := flag.String("dsn", "zeb:zebpwd@tcp(localhost:3306)/snippetbox?parseTime=true", "MySQL data source name") flag.Parse() logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) db, err := openDB(*dbDriver, *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 app := &application{ logger: logger, snippet: &models.SnippetModel{DB: db}, templateCache: templateCache, formDecoder: formDecoder, sessionManager: sessionManager, } srv := &http.Server{ Addr: *addr, Handler: app.routes(), // Create a *log.Logger from our structured logger handler, which writes log entries at // Error level, and assign it to the ErrorLog field. If you would prefer to log the // server errors at Warn level instead, you could pass slog.LevelWarn as the final // parameter. ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError), } logger.Info("starting server", "addr", *addr) err = srv.ListenAndServe() logger.Error(err.Error()) os.Exit(1) }
zzh@ZZHPC:/zdata/Github/snippetbox$ go run ./cmd/web time=2024-09-05T19:24:08.317+08:00 level=INFO msg="starting server" addr=:4000 time=2024-09-05T19:24:16.686+08:00 level=INFO msg="received request" ip=[::1]:47308 proto=HTTP/1.1 method=GET uri=/ time=2024-09-05T19:24:16.689+08:00 level=ERROR msg="http: invalid Content-Length of \"this isn't an integer!\""
标签:Web,http,addr,err,Server,Application,Error,Go,logger From: https://www.cnblogs.com/zhangzhihui/p/18399111