golang Go HTTP服务器的Logger中间件,它以Apache格式记录每个具有响应状态代码的请求。

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang Go HTTP服务器的Logger中间件,它以Apache格式记录每个具有响应状态代码的请求。相关的知识,希望对你有一定的参考价值。

package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"time"
)

// Example log output:
// 127.0.0.1 - - [28/Oct/2016:18:35:05 -0400] "GET / HTTP/1.1" 200 13 "" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36"
// 127.0.0.1 - - [28/Oct/2016:18:35:05 -0400] "GET /favicon.ico HTTP/1.1" 404 10 "http://localhost:8080/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36"
func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		fmt.Fprintln(w, "Hello World!")
	})
	log.Println("Serving...")
	// Logger takes an io.Writer and an http.Handler function to wrap:
	http.ListenAndServe(":8080", Logger(os.Stderr, http.DefaultServeMux))
}

// Logs incoming requests, including response status.
func Logger(out io.Writer, h http.Handler) http.Handler {
	logger := log.New(out, "", 0)
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		o := &responseObserver{ResponseWriter: w}
		h.ServeHTTP(o, r)
		addr := r.RemoteAddr
		if i := strings.LastIndex(addr, ":"); i != -1 {
			addr = addr[:i]
		}
		logger.Printf("%s - - [%s] %q %d %d %q %q",
			addr,
			time.Now().Format("02/Jan/2006:15:04:05 -0700"),
			fmt.Sprintf("%s %s %s", r.Method, r.URL, r.Proto),
			o.status,
			o.written,
			r.Referer(),
			r.UserAgent())
	})
}

type responseObserver struct {
	http.ResponseWriter
	status      int
	written     int64
	wroteHeader bool
}

func (o *responseObserver) Write(p []byte) (n int, err error) {
	if !o.wroteHeader {
		o.WriteHeader(http.StatusOK)
	}
	n, err = o.ResponseWriter.Write(p)
	o.written += int64(n)
	return
}

func (o *responseObserver) WriteHeader(code int) {
	o.ResponseWriter.WriteHeader(code)
	if o.wroteHeader {
		return
	}
	o.wroteHeader = true
	o.status = code
}

Golang系列文章:创建Web服务

使用Go语言,我们可以轻松创建出Web服务,这一点比Node.js还要简单,今天就来总结一下Go语言中Web服务的创建方式。

首先,我们需要引入net/http这个包来处理HTTP请求,然后在指定的端口开启服务,下面来写一个最简单的Web服务程序:

// server.go

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", handler)

    log.Println("go server listening at port 3000...")

    err := http.ListenAndServe(":3000", nil)

    if err != nil {
        log.Fatal("err: ", err)
    }
}

func handler(res http.ResponseWriter, req *http.Request) {
    log.Println(req.URL.Path)
    fmt.Fprintf(res, "URL.Path = %q
", req.URL.Path)
}

上面代码中,我们会处理所有3000端口的请求,然后在页面显示请求的路径。另外,我们在代码中引入了log包,使用log.Println(s)方法打印信息,运行代码后,在浏览器请求localhost:3000localhost:3000/test,控制台打印信息如下:

$ go run server.go
2018/08/30 13:26:58 go server listening at port 3000...
2018/08/30 13:27:01 /
2018/08/30 13:27:07 /test

接下来,我们希望这个服务能够解析出浏览器的请求信息,返回并显示到页面,下面是经过改进后的代码:

// server.go

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", handler)

    log.Println("go server listening at port 3000...")

    err := http.ListenAndServe(":3000", nil)

    if err != nil {
        log.Fatal("err: ", err)
    }
}

func handler(res http.ResponseWriter, req *http.Request) {
    // 请求方法 请求地址 协议类型
    fmt.Fprintf(res, "%s %s %s
", req.Method, req.URL, req.Proto)

    // 请求头信息
    for k, v := range req.Header {
        fmt.Fprintf(res, "Header[%q] = %q
", k, v)
    }

    // 请求的服务器URL & 请求的远程地址
    fmt.Fprintf(res, "Host = %q
", req.Host)
    fmt.Fprintf(res, "RemoteAddr = %q
", req.RemoteAddr)

    if err := req.ParseForm(); err != nil {
        log.Print(err)
    }

    // 表单信息
    for k, v := range req.Form {
        fmt.Fprintf(res, "Form[%q] = %q
", k, v)
    }
}

handler函数中,我们从http.Request中取出相应的HTTP请求信息,然后返回给浏览器,大家可以亲自运行以上代码,来观察实际效果。

接下来,我们希望做个小功能 - 统计用户的访问次数,这是一项很古老的技术了,在上个世纪的论坛网站中曾广泛应用。:)

我们改进后的代码如下:

// server.go

package main

import (
    "fmt"
    "log"
    "sync"
    "net/http"
)

var mutex sync.Mutex
var count int

func main() {
    http.HandleFunc("/", handler)
    // 添加/count路由及处理函数
    http.HandleFunc("/count", counter)

    log.Println("go server listening at port 3000...")

    err := http.ListenAndServe(":3000", nil)

    if err != nil {
        log.Fatal("err: ", err)
    }
}

// 每次访问时 count递增
func handler(res http.ResponseWriter, req *http.Request) {
    mutex.Lock()
    count++
    mutex.Unlock()

    fmt.Fprintf(res, "URL.Path = %q
", req.URL.Path)
}

// 显示当前访问次数
func counter(res http.ResponseWriter, req *http.Request) {
    mutex.Lock()
    fmt.Fprintf(res, "Visit Count: %d
", count)
    mutex.Unlock()
}

这次我们引入了sync包,来应对并发访问带来的数据更新问题,sync.Mutex类提供了Lock()Unlock()方法,分别对更新操作进行加锁和解锁,保证count的递增操作不会受到并发访问的影响。

运行上面的代码,然后在浏览器中访问除/count之外的路径,都可以累积访问次数,最后,在访问/count路径时,会返回当前服务被访问的次数。

以上是关于golang Go HTTP服务器的Logger中间件,它以Apache格式记录每个具有响应状态代码的请求。的主要内容,如果未能解决你的问题,请参考以下文章

golang Go中的简单HTTP服务器使用mongoDB和通道

golang 在go(golang)中使用http.Get的示例

golang 简单web服务

golang高性能日志库zap配置示例

Golang系列文章:创建Web服务

go语言怎样把json格式的数据发给前端jquery处理