介绍go中的日志, http客户端, http服务端和上下文
Logging Go 标准库提供了从 Go 程序输出日志的直接工具,其中 log 包用于自由格式输出,log/slog 包用于结构化输出。
logging.go 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package mainimport ( "bytes" "fmt" "log" "os" "log/slog" ) func main () { log.Println("standard logger" ) log.SetFlags(log.LstdFlags | log.Lmicroseconds) log.Println("with micro" ) log.SetFlags(log.LstdFlags | log.Lshortfile) log.Println("with file/line" ) mylog := log.New(os.Stdout, "my:" , log.LstdFlags) mylog.Println("from mylog" ) mylog.SetPrefix("ohmy:" ) mylog.Println("from mylog" ) var buf bytes.Buffer buflog := log.New(&buf, "buf:" , log.LstdFlags) buflog.Println("hello" ) fmt.Print("from buflog:" , buf.String()) jsonHandler := slog.NewJSONHandler(os.Stderr, nil ) myslog := slog.New(jsonHandler) myslog.Info("hi there" ) myslog.Info("hello again" , "key" , "val" , "age" , 25 ) }
输出示例;输出的日期和时间取决于示例运行的时间。
log 1 2 3 4 5 6 7 8 9 10 11 12 13 $ go run logging.go 2023/08/22 10:45:16 standard logger 2023/08/22 10:45:16.904141 with micro 2023/08/22 10:45:16 logging.go:40: with file/line my:2023/08/22 10:45:16 from mylog ohmy:2023/08/22 10:45:16 from mylog from buflog:buf:2023/08/22 10:45:16 hello {"time":"2023-08-22T10:45:16.904166391-07:00", "level":"INFO","msg":"hi there"} {"time":"2023-08-22T10:45:16.904178985-07:00", "level":"INFO","msg":"hello again", "key":"val","age":25}
为了在网站上更清晰地展示,这些内容被包裹起来;实际上,它们是在一行中发出的。
HTTP Client Go 标准库中的 net/http 包为 HTTP 客户端和服务器提供了出色的支持。在本例中,我们将使用它来发出简单的 HTTP 请求。
http-clients.go 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport ( "bufio" "fmt" "net/http" ) func main () { resp, err := http.Get("https://gobyexample.com" ) if err != nil { panic (err) } defer resp.Body.Close() fmt.Println("Response status:" , resp.Status) scanner := bufio.NewScanner(resp.Body) for i := 0 ; scanner.Scan() && i < 5 ; i++ { fmt.Println(scanner.Text()) } if err := scanner.Err(); err != nil { panic (err) } }
log 1 2 3 4 5 6 7 $ go run http-clients.go Response status: 200 OK <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Go by Example</title>
HTTP Server 使用 net/http 软件包,编写基本 HTTP 服务器非常简单。
http-servers.go 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package mainimport ( "fmt" "net/http" ) func hello (w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "hello\n" ) } func headers (w http.ResponseWriter, req *http.Request) { for name, headers := range req.Header { for _, h := range headers { fmt.Fprintf(w, "%v: %v\n" , name, h) } } } func main () { http.HandleFunc("/hello" , hello) http.HandleFunc("/headers" , headers) http.ListenAndServe(":8090" , nil ) }
在后台运行服务器, 访问 /hello 路由。
log 1 2 3 4 $ go run http-servers.go & $ curl localhost:8090/hello hello
Context 在上一个示例中,我们了解了如何设置一个简单的 HTTP 服务器。HTTP 服务器可用于演示如何使用 context.Context 控制取消。Context 可跨 API 边界和 goroutines 传递截止日期、取消信号和其他请求范围值。
context-in-http-servers.go 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package mainimport ( "fmt" "net/http" "time" ) func hello (w http.ResponseWriter, req *http.Request) { ctx := req.Context() fmt.Println("server: hello handler started" ) defer fmt.Println("server: hello handler ended" ) select { case <-time.After(10 * time.Second): fmt.Fprintf(w, "hello\n" ) case <-ctx.Done(): err := ctx.Err() fmt.Println("server:" , err) internalError := http.StatusInternalServerError http.Error(w, err.Error(), internalError) } } func main () { http.HandleFunc("/hello" , hello) http.ListenAndServe(":8090" , nil ) }
在后台运行服务, 模拟客户端向 /hello 提出请求,并在开始后不久按下 Ctrl+C 以示取消。
log 1 2 3 4 5 6 7 $ go run context-in-http-servers.go & $ curl localhost:8090/hello server: hello handler started ^C server: context canceled server: hello handler ended
参考链接