LoginSignup
1
3

More than 5 years have passed since last update.

go langのSimpleなloggerを作った話

Last updated at Posted at 2017-05-07

はじめに

 GWは時間を持て余していたので、前々から作りたかった
go lang向けのloggerを作ってみました。

 Simpleな機能だけで終わらせたかったのですが、不思議と作り込んでしまい
GWの時間をほぼ捧げてしまいました(血涙

Slogger(SimpleLogger)

 今回作ったloggerの名前です。
Thread-safeで、以下の機能を有しています!

  • LogLevel
  • Logを記録した回数(LogLevel毎に取得可能)
  • Logを記録した日付に合わせたFile出力
  • 非同期記録
  • Formatの調整は無く、Processorの実装で独自の出力対応

Processorについては後述します。(LTSVで標準出力してみます)

使ってみる!

Install

go get github.com/flowtumn/slogger

Demo

main.go
package main

import (
    "github.com/flowtumn/slogger"
)

func main() {
    logger, err := slogger.CreateSlogger(
        slogger.SloggerSettings{
            LogLevel:     slogger.DEBUG,        // DEBUG以上は全て記録。
            LogName:      "EXAMPLE-SLOGGER",    // Loggerの名前。この名前でFile保存される。
            LogDirectory: "/tmp",               // Log保存先のDirectory。
            LogExtension: "log",                // Logの拡張子。
        },
        slogger.CreateSloggerProcessorFile(),   //Logを処理するProcessor。Fileに記録する。
    )

    if nil != err {
        panic(err)
    }

    defer func() {
        //Closeを呼びlogをflush
        logger.Close()
    }()

    logger.Debug("Debug Message. %+v", map[int]int{100: 200, 300: 400, 500: 600})
    logger.Info("Info Message.")
    logger.Warn("Warn Message.")
    logger.Error("Error Message.")
    logger.Critical("Critical Message.")
    //記録した回数を表示。
    fmt.Printf("%+v\n", logger.RecordCounter());
}

結果

$ go run main.go
&{Debug:1 Info:1 Warn:1 Error:1 Critical:1}

$ cat /tmp/EXAMPLE-SLOGGER-2017-05-06.log
2017-05-06 23:41:46 [DEBUG] main.go(22): Debug Message. map[100:200 300:400 500:600]
2017-05-06 23:41:46 [INFO] main.go(23): Info Message.
2017-05-06 23:41:46 [WARN] main.go(24): Warn Message.
2017-05-06 23:41:46 [ERROR] main.go(25): Error Message.
2017-05-06 23:41:46 [CRITICAL] main.go(26): Critical Message.

 イメージ通りの出力になっていますでしょうか。
普通に使う上では、slogger.SloggerSettingsを適宜設定すればお使い頂けると思います。
slogger.CreateSloggerProcessorFile()はFileに記録するProcessorになっており
一般的な使い方ならslogger.CreateSloggerProcessorFile()を指定して下さい。

Processorを作ってLTSV出力

 折角GW中に作ったので、Processorを独自に実装し、LTSV出力をしてみます。
loggerを普通に使いたい方にはあまり有用な情報ではありません(。。

Interface

まず以下を実装する必要があります。

interface.go
type SloggerProcessor interface {
    // LoggerがRecordに成功した時に呼ばれます。Logger本体もGetLogPathを持っており、ここで受け取った値を返しています。
    GetLogPath() *string

    // Loggerが記録する時 thread-safe に呼ばれます。(errorを返すと、失敗と見なし記録した回数を更新しません)
    Record(SloggerSettings, *SloggerData) error

    // Loggerが停止する時に呼ばれます。
    Shutdown()
}

LTSV Processorの実装

さて実装してみましょう。お題はLTSV出力。
Recordの中で、tabを挟んで標準出力しています。

ltsv_processor.go
type ExampleProcessorLTSV struct {
}

func (self *ExampleProcessorLTSV) GetLogPath() *string {
    //標準出力なので、Pathというものは無し。
    return nil
}

func (self *ExampleProcessorLTSV) Record(setting slogger.SloggerSettings, data *slogger.SloggerData) error {
    /////
    //settingは、初回のCreateSloggerで渡したSloggerSettingsがそのまま渡されます。
    /////
    //slogger.SloggerDataには以下の情報を持っています。
    //   LogLevel
    //   記録した時間(ms)
    //   呼んだソースコード名
    //   呼んだ行番号
    //   実際に記録したLogMessage
    /////
    fmt.Printf("%s\t%s\t%d\t%s\t%s\n",
        data.LogLevel.ToString(),
        data.SourceName,
        data.SourceLine,
        slogger.ConvertTimeStamp(data.CurrentTimeMillis, slogger.Full),
        data.LogMessage,
    )
    return nil
}
func (self *ExampleProcessorLTSV) Shutdown() {
    //標準出力なので後始末は不要
}

//LTSVProcessorのInstanceを返します。
func CreateProcessorLTSV() *slogger.SloggerProcessor {
    var r slogger.SloggerProcessor
    r = &ExampleProcessorLTSV{}
    return &r
}

以上で完成になります!

実装コードの中の CreateProcessorLTSV ですが、私自身この書き方に違和感があります。
もっと簡単且つ適当なやり方がありましたら、是非ご教授願いたいです。

LTSV Processorを実行。

動作させてみましょう! slogger.CreateSloggerに渡すProcessorを
今回作ったLTSVに置き換えるだけになります。

main.go
package main

import (
    "fmt"
    "github.com/flowtumn/slogger"
)

func main() {
    logger, err := slogger.CreateSlogger(
        slogger.SloggerSettings{
            LogLevel:     slogger.DEBUG,
            LogName:      "EXAMPLE-SLOGGER",
            LogDirectory: "/tmp",
            LogExtension: "log",
        },
        CreateProcessorLTSV(),      //Processorを LTSV に変更。
    )

    if nil != err {
        panic(err)
    }

    defer func() {
        //Closeを呼びlogをflush
        logger.Close()
    }()

    logger.Debug("Debug Message. %+v", map[int]int{100: 200, 300: 400, 500: 600})
    logger.Info("Info Message.")
    logger.Warn("Warn Message.")
    logger.Error("Error Message.")
    logger.Critical("Critical Message.")
    //記録した回数を表示。
    fmt.Printf("%+v\n", logger.RecordCounter());
}

結果

go run main.go
[DEBUG] main.go 56      2017-05-07 13:35:14     Debug Message. map[100:200 300:400 500:600]
[INFO]  main.go 57      2017-05-07 13:35:14     Info Message.
[WARN]  main.go 58      2017-05-07 13:35:14     Warn Message.
[ERROR] main.go 59      2017-05-07 13:35:14     Error Message.
[CRITICAL]      main.go 60      2017-05-07 13:35:14     Critical Message.
&{Debug:1 Info:1 Warn:1 Error:1 Critical:1}

きちんとLTSVで出力されていますね!!

今回は標準出力にしていますが、鋭い方は File に記録する時はどうするの?とお察しのはず。
ご明察の通り、現状は Format を変えての File 記録には、Fileに書き落とす処理も自分で書く必要があります(><

今後の課題とさせて下さい。

CacheProcessorとか、これから追加したいなーと考えています。

2017/9/17 ProcessorCacheFileを追加しました。

その他

2017/9/17 出力方式にCacheFileを追加し、BenchMarkもGraphにしてみました。

BentchMarkの対象は

NullSink(/dev/null相当)
CacheFile(Non-Blocking)
File(Blocking)

となり、Githubのページで見ることが出来ます。

Github

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3