読者です 読者をやめる 読者になる 読者になる

HDE BLOG

コードもサーバも、雲の上

Go言語のログ出力は標準logよりLogrus

Go

こんにちは。HDEクラウドプロダクト開発部 小本です。

昨年はGo言語が大盛り上がりでした。HDEでも新規サービスをGo言語で書いています。

しかし、先日リリースされたaws-sdk-goなどについては諸先輩方が書いてくださると思うので、 私は重箱の隅つつきのようなことを書こうと思います。

今日書くのは、私のチームではGo言語標準のlogを(多少のラッパー関数経由で)使っていたのですが、 実はLogrus(https://github.com/Sirupsen/logrus)が、かなり便利だったという話です。

標準のlogは貧弱

エラーレベル(Debug, Info, Warn, Error等)が無いのも問題ですが、 ログをPrintfなどで自分で整形しなければいけないのも問題です。

つまり・・・

// 悪い例1
log.Printf(`some error occured level=%s method=%s path=%s host=%s status=%s bytes=%s\n`,
           `error`, `GET`, `/`, `example.com`, `200 OK`, 10234)

// 悪い例2
msg := "some error occured "
fields = map[string]string {
  "level": "info",
  "method": "GET",
  "path": "/",
  "host": "example.com",
  "status": "200",
  "bytes": "10234",
}
for k, v := range fields {
  msg += fmt.Sprintf(" %s=%s", k, v)
}
log.Println(msg)

書捨てコードならともかく、コード量が多くなると一々書くのはおっくうです。

また、「後からログのフォーマットを変更したいときはどうするのか?」 「そもそもどんなフォーマットで出力するのか?」という問題もあります。

そしてバグ発覚時に「ここでログを出しておけば・・・」と後悔するでしょう。

これらの問題はlog以外のサードパーティのログライブラリにも同様に存在します。

Logrusはフォーマット指定が不要

一方、LogrusはパラメータをWithFieldsで渡すだけ。フォーマットは後から簡単に変更できます。

つまり 「とりあえず全部JSONで出力しとけばいっか」 → 後から必要に応じて変更 といった遅延評価が可能です。

logrus.SetFormatter(&logrus.JSONFormatter{})

logrus.WithFields(logrus.Fields{
  "level": "info",
  "method": "GET",
  "path": "/",
  "host": "example.com",
  "status": 200,
  "bytes": 10234,
}).Info("some error occured")

// => {"level": "info", "method": "GET", "path": "/", "host": "example.com", "status": 200, "bytes": 10234}

またLogrusには標準でJSON形式・テキスト形式(logfmt)が付属していますが、 メソッドを一つ実装するだけで簡単に新しい出力フォーマットを自作できます。

type Formatter interface {
   Format(*Entry) ([]byte, error)
}

というわけで、LTSV用フォーマッタを作ってみました。

doloopwhile/logrusltsv · GitHub

LTSV(Labeled Tab-Separated Values)フォーマットは、 シェルスクリプトでも扱えるほどシンプルな上、後からフィールドを追加するのも簡単。 fluentd等のツールも対応しており、JSONを上回る「とりあえずこれでいいか」力を持っています。

そのためHDEの一部製品でもLTSVを使っています。

あとがき

HDEでは勉強会スペースの貸し出しをしております。 ぜひご利用ください。