web-dev-qa-db-ja.com

Golangのグローバルログへの正しいアプローチ

Goでのアプリケーションロギングのパターンは何ですか?たとえば、ログインする必要がある5つのゴルーチンを持っている場合、...

  • 単一のlog.Loggerを作成して渡しますか?
  • そのlog.Loggerへのポインターを渡しますか?
  • 各ゴルーチンまたは関数はロガーを作成する必要がありますか?
  • ロガーをグローバル変数として作成する必要がありますか?
99
Carson
  • 単一のlog.Loggerを作成して渡しますか?

それは可能です。 log.Logger は、複数のゴルーチンから同時に使用できます。

  • そのlog.Loggerへのポインタを渡しますか?

log.New*Loggerを返します。これは通常、オブジェクトをポインターとして渡す必要があることを示しています。値として渡すと、構造体のコピー(つまり、ロガーのコピー)が作成され、複数のゴルーチンが同じ io.Writer に同時に書き込む可能性があります。ライターの実装によっては、これは深刻な問題になる可能性があります。

  • 各ゴルーチンまたは関数はロガーを作成する必要がありますか?

関数やゴルーチンごとに個別のロガーを作成しません。ゴルーチン(および関数)は、独立したロガーの保守を正当化しない非常に軽量なタスクに使用されます。プロジェクトの大きなコンポーネントごとにロガーを作成することをお勧めします。たとえば、プロジェクトがメールの送信にSMTPサービスを使用している場合、メールサービス用に別のロガーを作成することは、出力を個別にフィルタリングおよびオフにできるようにするための良い考えのようです。

  • ロガーをグローバル変数として作成する必要がありますか?

それはパッケージによって異なります。前のメールサービスの例では、サービスのインスタンスごとに1つのロガーを用意することをお勧めします。これにより、ユーザーは、ローカルMTAの使用中に発生した障害とは異なる方法でgmailメールサービスの使用中に障害を記録できます)。

49
tux21b

単純な場合、ログパッケージで定義されているグローバルロガー log.Logger があります。このグローバルロガーは log.SetFlags で設定できます。

その後、 log.Printflog.Fatalf のようなログパッケージのトップレベル関数を呼び出すだけで、そのグローバルインスタンスを使用できます。

32
zzzz

これはシンプルなロガーです

package customlogger

import (
    "log"
    "os"
    "sync"
)

type logger struct {
    filename string
    *log.Logger
}

var logger *logger
var once sync.Once

// start loggeando
func GetInstance() *logger {
    once.Do(func() {
        logger = createLogger("mylogger.log")
    })
    return logger
}

func createLogger(fname string) *logger {
    file, _ := os.OpenFile(fname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)

    return &logger{
        filename: fname,
        Logger:   log.New(file, "My app Name ", log.Lshortfile),
    }
}

このように使用できます

package main

import (
    "customlogger"
    "fmt"
    "net/http"
)

func main() {
    logger := customlogger.GetInstance()
    logger.Println("Starting")

    http.HandleFunc("/", sroot)
    http.ListenAndServe(":8080", nil)
}

func sroot(w http.ResponseWriter, r *http.Request) {
    logger := customlogger.GetInstance()

    fmt.Fprintf(w, "welcome")
    logger.Println("Starting")
}
10
Israel Barba

私はこの質問が少し古いことを知っていますが、私のように、あなたのプロジェクトが複数の小さなファイルで構成されている場合、私はあなたの4番目のオプションに投票します-私はパッケージメインの一部であるlogger.goを作成しました。このgoファイルはロガーを作成し、それをファイルに割り当て、メインの他の部分に提供します。エラーログを閉じるための優雅な方法を思いついていないことに注意してください...

package main

import (
    "fmt"
    "log"
    "os"
)

var errorlog *os.File
var logger *log.Logger

func init() {
    errorlog, err := os.OpenFile(logfile,  os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        fmt.Printf("error opening file: %v", err)
        os.Exit(1)
    }

    logger = log.New(errorlog, "applog: ", log.Lshortfile|log.LstdFlags)
}
9
Omortis

これは古い質問ですが、 http://github.com/romana/rlog (私たちが開発した)の使用を提案したいと思います。環境変数を使用して構成され、ロガーオブジェクトはrlogのインポート時に作成および初期化されます。したがって、ロガーを回す必要はありません。

rlogには多くの機能があります。

  • 完全に構成可能な日付/時刻スタンプ
  • ファイルだけでなく、stderrまたはstdoutへの同時出力。
  • 標準ログレベル(デバッグ、情報など)および自由に設定可能なマルチレベルロギング。
  • 発信者情報(ファイル、行番号、関数)のオンデマンドロギング。
  • さまざまなソースファイルにさまざまなログレベルを設定する機能。

これは非常に小さく、標準のGolangライブラリを除き、外部の依存関係はなく、積極的に開発されています。リポジトリに例を示します。

3
Juergen Brendel

デフォルトのログパッケージ( https://golang.org/pkg/log/ )が多少制限されていることがわかりました。たとえば、情報ログとデバッグログのサポートはありません。
いくつか調べてみたところ、 https://github.com/golang/glog を使用することに決めました。これは https://github.com/google/glog の移植であると思われ、ロギングにかなりの柔軟性を与えます。たとえば、アプリケーションをローカルで実行する場合、DEBUGレベルのログが必要な場合がありますが、実稼働環境ではINFO/ERRORレベルでのみ実行したい場合があります。完全な機能/ガイドのリストは、ここにあります https://google-glog.googlecode.com/svn/trunk/doc/glog.html (c ++モジュール用ですが、大部分はgolangポートに変換されます)

2
factotum