YAMAGUCHI::weblog

噛み付き地蔵に憧れて、この神の世界にやってきました。マドンナみたいな男の子、コッペです。

GoでStackdriver Logging向けのログをお手軽に出力する設定

はじめに

こんにちは、Stackdriverで遊んでいる人です。Stackdriver Loggingは標準出力に出されたJSON形式のログをFluentdベースのエージェント経由でいい感じに表示してくれます。

一方でGoに限らず通常のロギングライブラリは標準エラーにログを吐くという感じになるのですが、Stackdriver Loggingの場合デフォルト設定だと標準エラーに吐かれたログはすべてエラー扱いになりますので(まあ当たり前だよな)、そのあたりのすり合わせ調整が必要。

標準の log パッケージの場合

標準パッケージだけ使うのであれば、とりあえずこれだけやっておけばOK

  • 標準出力に出す(GKEの場合)
  • JSON形式でいくつかの決められたフィールド名(message, time, severity)を満たす

ということなのでこういう log.Loggerインスタンスを作ってあげる感じになりそう。

func sdLog(l *log.Logger, severity, msg string) {
    now := time.Now().Format(time.RFC3339Nano)
    entry := map[string]string{
        "time":     now,
        "severity": severity,
        "message":  msg,
    }
    b, err := json.Marshal(entry)
    if err != nil {
        log.Fatal(err)
    }
    l.Print(string(b))
}

logger := log.New(os.Stdout, "", 0)
sdLog(logger, "info", "hello")

logrus を使う場合

上の条件を満たすだけなんだけども、 logrus の場合はJSONFormatterがあるし、logrus.Loggerに各種ログレベルにあわせたメソッド(InfofWarnf など)があるので楽に設定できる。

log := logrus.New()
log.Level = logrus.DebugLevel
log.Formatter = &logrus.JSONFormatter{
    FieldMap: logrus.FieldMap{
        logrus.FieldKeyTime:  "timestamp",
        logrus.FieldKeyLevel: "severity",
        logrus.FieldKeyMsg:   "message",
    },
    TImestampFormat: time.RFC3339Nano,
}
log.Out = os.Stdout

Stackdriver LoggingのGo用のクライアントライブラリを使う

これを使っていいならはじめから使ったほうが良い。ただ個人的には logrus のほうがseverityに応じたヘルパーメソッドがあるので使い勝手が良いように感じる。

godoc.org

client, _ := logging.NewClient(ctx, projectID)
logger := client.Logger("my-log")
// Text Payload
logger.Log(logging.Entry{Payload: "Hello, world!")

// JSON Payload
type MyEntry struct {
    Name  string
    Count int
}
logger.Log(logging.Entry{
        Payload: MyEntry{Name: "Bob", Count: 3},
        Severity: logging.Critical,
})