fluentd
golang
msgpack

fluent-logger-golangでtime.Timeを送ったら"error":"#<MessagePack::UnknownExtTypeError: unexpected extension type>"が出た

はじめに

以下のようなスクリプト、configでfluentdに送ってみる
Logについてはgo generateでmsgp.Marshalerを自動生成しています
msgpのgeneratorについてはこちら

main.go
package main

//go:generate msgp

import (
    "log"
    "time"

    "github.com/fluent/fluent-logger-golang/fluent"
)

func main() {
    logger, err := fluent.New(fluent.Config{FluentPort: 24224, FluentHost: "127.0.0.1"})
    if err != nil {
        log.Println(err.Error())
        return
    }
    defer logger.Close()

    logData := Log{
        ID:        12345,
        Name:      "hogehoge",
        CreatedAt: time.Now(),
    }
    if err := logger.Post("debug.test", logData); err != nil {
        log.Println(err.Error())
        return
    }
    log.Println("done")
}

type Log struct {
    ID        uint32    `msg:"id"`
    Name      string    `msg:"name"`
    CreatedAt time.Time `msg:"created_at"`
}
fluent.conf
<source>
    @type forward
</source>
<match **>
    @type file
    path /path/to/log
</match>

実行

$ go run main.go main_gen.go
2017/12/02 19:30:16 done

ログを見てみる
何かエラーが出ている。

cat log/app.log.20171202.b55f58f8a5413f5de
2017-12-02T19:30:16+09:00   fluent.error    {"error":"#<MessagePack::UnknownExtTypeError: unexpected extension type>","error_class":"MessagePack::UnknownExtTypeError","message":"forward error error=#<MessagePack::UnknownExtTypeError: unexpected extension type> error_class=MessagePack::UnknownExtTypeError"}

解決策

msgpでtime.Timeを扱うときにstringとして変換するように以下の1行を追加してgo generate

//msgp:shim time.Time as:string using:timeToStr/strToTime

これでtime.TimeはtimeToStrstrToTimeメソッドでstringに変換されて用いられます。
なので、これらのメソッドも実装しておきます

func timeToStr(t time.Time) string {
    return t.Format("2006-01-02 15:04:05")
}

func strToTime(v string) time.Time {
    t, _ := time.Parse("2006-01-02 15:04:05", v)
    return t
}

最終的なスクリプトは以下

main.go
package main

//go:generate msgp
//msgp:shim time.Time as:string using:timeToStr/strToTime

import (
    "log"
    "time"

    "github.com/fluent/fluent-logger-golang/fluent"
)

func main() {
    logger, err := fluent.New(fluent.Config{FluentPort: 24224, FluentHost: "127.0.0.1"})
    if err != nil {
        log.Println(err.Error())
        return
    }
    defer logger.Close()

    logData := Log{
        ID:        12345,
        Name:      "hogehoge",
        CreatedAt: time.Now(),
    }
    if err := logger.Post("debug.test", logData); err != nil {
        log.Println(err.Error())
        return
    }
    log.Println("done")
}

type Log struct {
    ID        uint32    `msg:"id"`
    Name      string    `msg:"name"`
    CreatedAt time.Time `msg:"created_at"`
}

func timeToStr(t time.Time) string {
    return t.Format("2006-01-02 15:04:05")
}

func strToTime(v string) time.Time {
    t, _ := time.Parse("2006-01-02 15:04:05", v)
    return t
}

これで送信すると

$ go run main.go main_gen.go
2017/12/02 19:39:56 done

ちゃんとした形でログが出ています

$ cat log/app.log.20171202.b55f58f8a5413f5de
2017-12-02T19:39:56+09:00   debug.test  {"id":12345,"name":"hogehoge","created_at":"2017-12-02 19:39:56"}

こうやったら直った、っていうだけで実はあまり理由を理解してない。。。