emahiro/b.log

勉強記録と書評とたまに長めの呟きを書きます

【Go】WebサーバーでLogを表示する

やりたいこと

Goで立てた簡易webサーバーにアクセスした時に標準出力でLogを表示させるようにします。

webサーバーを立てる

/src/todo
  main.gp
  handler/main.gp

main.go

package main

import (
    "fmt"
    "net/http"

    "todos/handler"
)

var addr =  ":3000"

func main() {
    router := http.NewServeMux()
    router.HandleFunc("/", handler.Index)
    fmt.Printf("[START] server. port: %s\n", addr)
    if err := http.ListenAndServe(addr, router); err != nil {
        panic(fmt.Errorf("[FAILED] start sever. err: %v", err))
    }
}

handler/main.go

package handler

import "net/http"

func Index(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hello"))
}

ただし、このWeb Serverを起動(go run main.go)して、 http://localhost:3000 にアクセスしてもターミナル上で標準出力でありがちなリクエストログはされません。

go run main.go
[START] server. port: :3000
# ここに標準出力でリクエストをログを表示させたい。

ログを表示させる

ログを表示させるには、Log用のhandlerを用意して、Listenする時にそのログを出力するhandlerを通るように登録します。。 cf. Google グループ - Http Server and logging?

handler/log.go

package handler

import (
    "fmt"
    "net/http"
)

func Log(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        rAddr := r.RemoteAddr
        method := r.Method
        path := r.URL.Path
        fmt.Printf("Remote: %s [%s] %s", rAddr, method, path)
        h.ServeHTTP(w, r)
    })
}

main.goを以下の用に修正します。

if err := http.ListenAndServe(addr, handler.Log(router)); err != nil {
  panic(fmt.Errorf("[FAILED] start sever. err: %v", err))
}

ポイントはLogのhandlerの登録の仕方です。 Log出力用のhandlerを登録する場合は http.ListenAndServe(addr, hanlder) のhandlerの引数にrouterを単にrouterを追加すればいいのですが、以下のように middleware的な handlerの登録の記法が使えます。

func SomeMethod(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
}

return で返す http.HandlerFunc method内部でリクエスト毎に行いことをつらつら書いていけばなんでもできます。

まとめ

FWとか使っていると、この辺いつもFWが吸収してくれててブラックボックス化されてたんだなと痛感しました。
そういったところ、FW側でどう動いているのかわからずに使っていることが多かったので、どんな言語でも簡易的でいいので一旦web server作ってみるというのは学習としてはいい「車輪の再発明」だと思いました。