Help us understand the problem. What is going on with this article?

【Go×ログ】logrusの使い方を簡単に分かりやすくまとめてみた

はじめに

Goの標準パッケージのlogはシンプルで使いやすいのですが、機能が貧弱としばしば言われます。例えば、他言語では当たり前なログレベルなどの機能はあえて組み込まれていません。
本記事では、乱立するlogの外部パッケージの中で最も有名なパッケージの1つであるlogrusの使い方をまとめます。

zozo.go

インストール

$ go get github.com/sirupsen/logrus

機能

メッセージと変数の分離

logrusでは、 WithFields を使って「メッセージ」と「変数」を分離して実装と表示させる方法が推奨されています。こちらの方が長い文字列を延々と書くよりも生産的だろ?という思想とのことです。

ログ例
↓msgがログメッセージ、それ以降が変数です

time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
従来
log.Fatalf("Failed to send event %s to topic %s with key %d")
WithFields
log.WithFields(log.Fields{
  "event": event,
  "topic": topic,
  "key": key,
}).Fatal("Failed to send event")

WithFieldsを使うかどうかは任意です。
従来にあるような文字列の中に変数を埋め込むこともできます。

デフォルト変数

全てのログに共通的に出力するような変数は Fields で一度だけの宣言にまとめることができます。

func main() {
    requestID := 1
    userIP := "192.168.10.1"
    requestLogger := log.WithFields(log.Fields{"request_id": requestID, "user_ip": userIP})
    requestLogger.Info("something happened on that request")
    requestLogger.Warn("something not great happened")
}
実行結果
INFO[0000] something happened on that request            request_id=1 user_ip=192.168.10.1
WARN[0000] something not great happened                  request_id=1 user_ip=192.168.10.1

ログレベル

Trace, Debug, Info, Warning, Error, Fatal, Panicの7つのレベルが用意されています。

log.Trace("Something very low level.")
log.Debug("Useful debugging information.")
log.Info("Something noteworthy happened!")
log.Warn("You should probably take a look at this.")
log.Error("Something failed but I'm not quitting.")
log.Fatal("Bye.") // Calls os.Exit(1) after logging
log.Panic("I'm bailing.") // Calls panic() after logging

SetLevelで、セットしたレベル以上のレベルを出力するようにします。

log.SetLevel(log.InfoLevel)

ちなみに、TTYが有効になっているとログレベルに合わせてこんな風にカラフルに表示されます。
logrus画像.png

Fatalハンドラー

RegisterExitHandlerで、Fatal以上のレベルのメッセージを出力した際に、logrusがos.Exit(1)してしまう前に自動で実行される1つ以上の関数を登録しておくことができます。

handler := func() {
  // gracefully shutdown something...
}
logrus.RegisterExitHandler(handler)

フォーマット

logrusの標準ではテキストフォーマットとJSONフォーマットが用意されています。

テキストフォーマット

デフォルトのフォーマットです。
何も指定しなかった場合はテキストフォーマットになります。

package main

import (
  log "github.com/sirupsen/logrus"
)

func main() {
  log.WithFields(log.Fields{
    "animal": "walrus",
  }).Info("A walrus appears")
}
実行結果
INFO[0000] A walrus appears                              animal=walrus number=1 size=10

JSONフォーマット

SetFormatter&log.JSONFormatter{}を指定すれば、JSONフォーマットになります。
例えばdatadogやLogstashなどでログを監視する場合は、JSONフォーマットを使うことが多いと思います。

func main() {
    log.SetFormatter(&log.JSONFormatter{})
    log.WithFields(log.Fields{
        "animal": "walrus",
    }).Info("A walrus appears")
}
実行結果
{"animal":"walrus","level":"info","msg":"A walrus appears","time":"2019-06-16T09:27:28+09:00"}

環境

環境毎にログの設定を変える機能はlogrusには用意されていません。
プロダクション環境とその他の環境でログ設定を変更したい場合は、パッケージ変数などを活用して自分でハンドリングする必要があります。

init() {
  // do something here to set environment depending on an environment variable
  // or command-line flag
  if Environment == "production" {
    log.SetFormatter(&log.JSONFormatter{})
  } else {
    // The TextFormatter is default, you don't actually have to do this.
    log.SetFormatter(&log.TextFormatter{})
  }
}

ログローテーション

ログローテーション機能はlogrusには用意されていません。

具体例

具体例1(init関数で共通設定)

package main

import (
  "os"
  log "github.com/sirupsen/logrus"
)

func init() {
  // JSONフォーマット
  log.SetFormatter(&log.JSONFormatter{})

  // 標準エラー出力でなく標準出力とする
  log.SetOutput(os.Stdout)

  // Warningレベル以上を出力
  log.SetLevel(log.WarnLevel)
}

func main() {
  // ログ例1(出力されない)
  log.WithFields(log.Fields{
    "animal": "walrus",
    "size":   10,
  }).Info("A group of walrus emerges from the ocean")

  // ログ例2(出力される)
  log.WithFields(log.Fields{
    "omg":    true,
    "number": 122,
  }).Warn("The group's number increased tremendously!")

  // ログ例3(出力される)
  log.WithFields(log.Fields{
    "omg":    true,
    "number": 100,
  }).Fatal("The ice breaks!")

  // 共通的に使用する変数の設定
  contextLogger := log.WithFields(log.Fields{
    "common": "this is a common field",
    "other": "I also should be logged always",
  })

  contextLogger.Info("I'll be logged with common and other field")
  contextLogger.Info("Me too")
}
実行結果
{"level":"warning","msg":"The group's number increased tremendously!","number":122,"omg":true,"time":"2019-06-17T07:18:09+09:00"}
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,"time":"2019-06-17T07:18:09+09:00"}
exit status 1

具体例2(ログインスタンスで共通設定)

package main

import (
  "os"
  "github.com/sirupsen/logrus"
)

// ログインスタンス生成
var log = logrus.New()

func main() {
  // 標準出力に設定
  log.Out = os.Stdout

  // ファイル出力の場合の例
  // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
  // if err == nil {
  //  log.Out = file
  // } else {
  //  log.Info("Failed to log to file, using default stderr")
  // }

  // ログ出力例
  log.WithFields(logrus.Fields{
    "animal": "walrus",
    "size":   10,
  }).Info("A group of walrus emerges from the ocean")
}
実行結果
INFO[0000] A group of walrus emerges from the ocean      animal=walrus size=10

最後に

logrusは簡単に導入できる反面、zapなどの他の外部パッケージに比べてると速度が遅いという短所もあります。
私が導入したプロジェクトではとりあえずJSONで出力できるようにしたかっただけで、それほどリクエスト数も多くなく、ログの出力数も多くはなかったのでlogrusを導入しました。ケースを見定めてパッケージを選定する必要がありそうです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした