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


119

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

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

回答:


59
  • 単一のlog.Loggerを作成して渡しますか?

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

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

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

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

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

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

それはあなたのパッケージに依存します。前のメールサービスの例では、サービスのインスタンスごとに1つのロガーを用意することをお勧めします。これにより、ユーザーは、ローカルのMTAを使用しているときに発生した障害(たとえばsendmail )。


37

単純なケースでは、ログパッケージで定義されたグローバルロガーがありますlog.Logger。このグローバルロガーは、を介して設定できますlog.SetFlags

その後、そのグローバルインスタンスを使用するlog.Printfやなどのログパッケージの最上位関数を呼び出すことができlog.Fatalfます。


カスタムロガーを使用できないフラグを設定できると思いました。
0xcaff 14

@caffinatedmonkeyは、実際には、io.Writerインターフェースを実装し、を介してデフォルトのロガーの出力を変更する場合、カスタムロガーを使用できますSetOutput()
congusbongus 14年

16

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

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

私はこの質問が少し古いことを知っていますが、私のように、プロジェクトが複数の小さなファイルで構成されてlogger.goいる場合、4番目のオプションに投票します-私はパッケージメインの一部であるを作成しました。この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)
}

8
正常に終了するには、おそらくdefer errorlog.Close()実行の最後に、またはその終了を確実にするために、Goのシグナルパッケージgolang.org/pkg/os/signal
Anfernee

4

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

rlogにはいくつかの機能があります。

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

これは非常に小さく、標準のGolangライブラリを除いて外部の依存関係はなく、活発に開発されています。例はリポジトリで提供されています。


3
推奨する製品との関係を開示していただきありがとうございます。ありがたいです。
ロバートコロンビア

2

デフォルトのログパッケージ(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ポートに変換されます)。


0

考慮できるロギングモジュールの1つはklogです。です。特定のレベルでログを記録する柔軟性を提供する「V」ログをサポートします

klogはglogのフォークであり、以下の欠点を克服しています

  • glogは多くの「落とし穴」を提示し、コンテナ化された環境に課題をもたらしますが、そのすべてが十分に文書化されていません。
  • glogはログをテストする簡単な方法を提供しないため、ログを使用するソフトウェアの安定性が損なわれます
  • glogはC ++ベースで、klogは純粋なgolang実装です

実装例

package main

import (
    "flag"

    "k8s.io/klog"


)

type myError struct {
    str string
}

func (e myError) Error() string {
    return e.str
}

func main() {
    klog.InitFlags(nil)
    flag.Set("v", "1")
    flag.Parse()

    klog.Info("hello", "val1", 1, "val2", map[string]int{"k": 1})
    klog.V(3).Info("nice to meet you")
    klog.Error(nil, "uh oh", "trouble", true, "reasons", []float64{0.1, 0.11, 3.14})
    klog.Error(myError{"an error occurred"}, "goodbye", "code", -1)
    klog.Flush()
}

-1

ブログを確認してください。GOlangでのLOGの使用について説明します。 ください

例:// Se declara la variable Log。Estaseráusada para reregstrar los eventos。var(ログ* log.Logger = Loggerx())

func Loggerx() *log.Logger {
    LOG_FILE_LOCATION := os.Getenv("LOG_FILE_LOCATION")
        //En el caso que la variable de entorno exista, el sistema usa la configuración del docker.
    if LOG_FILE_LOCATION == "" {
        LOG_FILE_LOCATION = "../logs/" + APP_NAME + ".log"
    } else {
        LOG_FILE_LOCATION = LOG_FILE_LOCATION + APP_NAME + ".log"
    }
    flag.Parse()
        //Si el archivo existe se rehusa, es decir, no elimina el archivo log y crea uno nuevo.
    if _, err := os.Stat(LOG_FILE_LOCATION); os.IsNotExist(err) {
        file, err1 := os.Create(LOG_FILE_LOCATION)
        if err1 != nil {
            panic(err1)
        }
                //si no existe,se crea uno nuevo.
        return log.New(file, "", log.Ldate|log.Ltime|log.Lshortfile)
    } else {
                //si existe se rehusa.
        file, err := os.OpenFile(LOG_FILE_LOCATION, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
        if err != nil {
            panic(err)
        }
        return log.New(file, "", log.Ldate|log.Ltime|log.Lshortfile)
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.