emahiro/b.log

Drastically Repeat Yourself !!!!

GAE/Go1.12 において構造化ログを出力する

App Engine 2nd Generation で構造化ログを出力するための ae-plain-logger というライブラリを作成しました。

github.com

aelog とは?

ログエントリを json ペイロードとして標準出力に出力します。この時 traceID と spanID をログエントリに含めることで stackdriver logging のGUIで構造化ログとして出力することができます。

サポートしてること

  • 構造化ログを出力すること
    • プラットフォームリクエストログにアプリケーションログが紐づくこと

サポートしていないこと

  • サポートしてること以外の Proprietary App Engine Log API で提供していた機能
    • プラットフォームリクエストログに紐づいたアプリケーションログの中で一番高い severity をプラットフォームリクエストログの severity として伝播させる機能etc...

使い方

README.md にも記載してる通りピュアな net/http を使っているケースのみで How to use で記載してますが、他にもメジャーで使われてる(と思う)WAFでの使い方を記載してみました。
基本的には http.Handler を引数に指定するだけで実装が可能ですので、以下に示している WAF 以外でも使うことは可能だと思います。

Gin

mux := gin.New()
mux.GET("/hello", func(gc *gin.Context) {
    gc.Status(http.StatusOK)
    gc.Writer.Write([]byte("hello"))
    return
})

h := middleware.AELogger("ginRouter")(mux)
server := http.Server{
    Addr:    fmt.Sprintf(":%d", addr),
    Handler: h,
}

go func() {
    if err := server.ListenAndServe(); err != nil {
        log.Fatalf("server closed with error: %v", err)
    }
}()

go-chi

mux := chi.NewMux()
mux.HandleFunc("/hello", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("hello"))
}))

h := middleware.AELogger("chiServeMux")(mux)
server := http.Server{
    Addr:    fmt.Sprintf(":%d", addr),
    Handler: h,
}

go func() {
    if err := server.ListenAndServe(); err != nil {
        log.Fatalf("server closed with error: %v", err)
    }
}()

gemux

最近気になってる薄い router の gemux も追加しておきます。

mux := &gemux.ServeMux{}
mux.Handle("/hello", http.MethodGet, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("hello"))
}))

h := middleware.AELogger("chiServeMux")(mux)
server := http.Server{
    Addr:    fmt.Sprintf(":%d", addr),
    Handler: h,
}

go func() {
    if err := server.ListenAndServe(); err != nil {
        log.Fatalf("server closed with error: %v", err)
    }
}()

参考

先駆者の方々のお知恵を色々拝借したり、Googleのドキュメントを真面目に読み込んだりしてました。

追記

追記1.

ドキュメントを読んでて気づいたいんですけど https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry の Payload type の箇所の protoPayload の項目に "type.googleapis.com/google.appengine.logging.v1.RequestLog" が明記されていました。
これはしばらくはこのフォーマットがサポートされる、的な認識を持って良いでしょうかね?

google.appengine.logging.v1.RequestLog をうまいこと自前で組み立てれば、1st Gen に近しいログ出力になりそう、かつ 1st Gen の ログフォーマットに依存した監視設定をしているケースにおいてはそこを移行せずともよくなるので protoPayload を使えると嬉しみがあります。

追記2

第一弾としては構造化ログの出力のみをサポートしましたが、long-running operation 用の filed も用意されているので、こちらにも対応したいなと思ってます。
ref: https://cloud.google.com/logging/docs/agent/configuration#special-fields

Update

2019/11/11 update package name

パッケージ名を ae-plain-logger から aelog に変更しました。

github.com