go langのSimpleなloggerを作った話

# はじめに

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

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

# Slogger(SimpleLogger)

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

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

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

# 使ってみる！

## Install 
```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());
}
```

## 結果
```result
$ 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());
}
```

### 結果

```result_ltsv
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の対象は

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

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

# Github

https://github.com/flowtumn/slogger