首页 > 代码库 > Go实战--实现一个自己的网络请求日志httplogger(The way to go)

Go实战--实现一个自己的网络请求日志httplogger(The way to go)

生命不止,继续go go go~~~

之前我们简要介绍了go语言中的log package 和 net/http package,那么我们今天就干点实事儿,将二者结合,实现我们自己的日志记录网络请求。

另外,我们还没有跟你介绍time package,但是也可以看懂的。

首先,我默认你了解go语言的组织结构。

导入需要的package
我们需要 log net/http time三个包

package httplogger

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

实现一个结构体

type loggedRoundTripper struct {
    rt  http.RoundTripper
    log HTTPLogger
}

其中http.RoudTripper:
RoundTripper is an interface representing the ability to execute a single HTTP transaction, obtaining the Response for a given Request.

实现一个接口HTTPLogger

type HTTPLogger interface {
    LogRequest(*http.Request)
    LogResponse(*http.Request, *http.Response, error, time.Duration)
}

实现函数:

type DefaultLogger struct {
}


func (dl DefaultLogger) LogRequest(*http.Request) {
}


func (dl DefaultLogger) LogResponse(req *http.Request, res *http.Response, err error, duration time.Duration) {
    duration /= time.Millisecond
    if err != nil {
        log.Printf("HTTP Request method=%s host=%s path=%s status=error durationMs=%d error=%q", req.Method, req.Host, req.URL.Path, duration, err.Error())
    } else {
        log.Printf("HTTP Request method=%s host=%s path=%s status=%d durationMs=%d", req.Method, req.Host, req.URL.Path, res.StatusCode, duration)
    }
}

完整代码

package httplogger

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

type loggedRoundTripper struct {
    rt  http.RoundTripper
    log HTTPLogger
}

func (c *loggedRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
    c.log.LogRequest(request)
    startTime := time.Now()
    response, err := c.rt.RoundTrip(request)
    duration := time.Since(startTime)
    c.log.LogResponse(request, response, err, duration)
    return response, err
}

// NewLoggedTransport takes an http.RoundTripper and returns a new one that logs requests and responses
func NewLoggedTransport(rt http.RoundTripper, log HTTPLogger) http.RoundTripper {
    return &loggedRoundTripper{rt: rt, log: log}
}

// HTTPLogger defines the interface to log http request and responses
type HTTPLogger interface {
    LogRequest(*http.Request)
    LogResponse(*http.Request, *http.Response, error, time.Duration)
}

// DefaultLogger is an http logger that will use the standard logger in the log package to provide basic information about http responses
type DefaultLogger struct {
}

// LogRequest doens‘t do anything since we‘ll be logging replies only
func (dl DefaultLogger) LogRequest(*http.Request) {
}

// LogResponse logs path, host, status code and duration in milliseconds
func (dl DefaultLogger) LogResponse(req *http.Request, res *http.Response, err error, duration time.Duration) {
    duration /= time.Millisecond
    if err != nil {
        log.Printf("HTTP Request method=%s host=%s path=%s status=error durationMs=%d error=%q", req.Method, req.Host, req.URL.Path, duration, err.Error())
    } else {
        log.Printf("HTTP Request method=%s host=%s path=%s status=%d durationMs=%d", req.Method, req.Host, req.URL.Path, res.StatusCode, duration)
    }
}

// DefaultLoggedTransport wraps http.DefaultTransport to log using DefaultLogger
var DefaultLoggedTransport = NewLoggedTransport(http.DefaultTransport, DefaultLogger{})

使用
实现接口:

type httpLogger struct {
    log *log.Logger
}

func newLogger() *httpLogger {
    return &httpLogger{
        log: log.New(os.Stderr, "log - ", log.LstdFlags),
    }
}

func (l *httpLogger) LogRequest(req *http.Request) {
    l.log.Printf(
        "Request %s %s",
        req.Method,
        req.URL.String(),
    )
}

func (l *httpLogger) LogResponse(req *http.Request, res *http.Response, err error, duration time.Duration) {
    duration /= time.Millisecond
    if err != nil {
        l.log.Println(err)
    } else {
        l.log.Printf(
            "Response method=%s status=%d durationMs=%d %s",
            req.Method,
            res.StatusCode,
            duration,
            req.URL.String(),
        )
    }
}

完整代码:

package main

import (
    "log"
    "net/http"
    "os"
    "time"

    "httplogger/httplogger"
)

func main() {
    client := http.Client{
        Transport: httplogger.NewLoggedTransport(http.DefaultTransport, newLogger()),
    }

    client.Get("https://www.baidu.com")
}

type httpLogger struct {
    log *log.Logger
}

func newLogger() *httpLogger {
    return &httpLogger{
        log: log.New(os.Stderr, "log - ", log.LstdFlags),
    }
}

func (l *httpLogger) LogRequest(req *http.Request) {
    l.log.Printf(
        "Request %s %s",
        req.Method,
        req.URL.String(),
    )
}

func (l *httpLogger) LogResponse(req *http.Request, res *http.Response, err error, duration time.Duration) {
    duration /= time.Millisecond
    if err != nil {
        l.log.Println(err)
    } else {
        l.log.Printf(
            "Response method=%s status=%d durationMs=%d %s",
            req.Method,
            res.StatusCode,
            duration,
            req.URL.String(),
        )
    }
}

结果:
log - 2017/04/18 23:39:41 Request GET https://www.baidu.com
log - 2017/04/18 23:39:42 Response method=GET status=200 durationMs=614 https://www.baidu.com

技术分享

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    Go实战--实现一个自己的网络请求日志httplogger(The way to go)