App Engine 2nd Generation で構造化ログを出力するための ae-plain-logger というライブラリを作成しました。
aelog とは?
ログエントリを json ペイロードとして標準出力に出力します。この時 traceID と spanID をログエントリに含めることで stackdriver logging のGUIで構造化ログとして出力することができます。
サポートしてること
- 構造化ログを出力すること
- プラットフォームリクエストログにアプリケーションログが紐づくこと
サポートしていないこと
- サポートしてること以外の Proprietary App Engine Log API で提供していた機能
使い方
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のドキュメントを真面目に読み込んだりしてました。
- GAE のログに憧れて
- AppEngineの旧Log APIを脱却したい話
- https://cloud.google.com/logging/docs/agent/configuration
- https://cloud.google.com/logging/docs/structured-logging
追記
追記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 に変更しました。