LoginSignup
18
17

More than 5 years have passed since last update.

独自のfmt.Formatterを実装する

Posted at

fmt.Formatter interfaceを実装すると独自で定義した型がfmt.Sprintffmt.Fprintfに渡されたときのverb(verbとは例えば%s%vなど)ごとの出力をカスタマイズすることができる.

これを実装しているのは以前 Golangでエラー時にスタックトレースを表示するで紹介したpkg/errors パッケージである.pkg/errorsで生成したエラーは%sverbを使うと通常の表示,%+vverbを使うとスタックトレース形式の表示という切り替えが可能になっている.これはfmt.Formatterを実装することで実現している.実装は以下のようになっている.

type StackTrace []Frame

func (st StackTrace) Format(s fmt.State, verb rune) {
    switch verb {
    case 'v':
        switch {
        case s.Flag('+'):
            for _, f := range st {
                fmt.Fprintf(s, "\n%+v", f)
            }
        case s.Flag('#'):
            fmt.Fprintf(s, "%#v", []Frame(st))
        default:
            fmt.Fprintf(s, "%v", []Frame(st))
        }
    case 's':
        fmt.Fprintf(s, "%s", []Frame(st))
    }
}

実装は単純でswitchを使ってverbごとの出力を定義しているだけである.

最近自分ではgo-httpstatというHTTPリクエストの各ステップのレイテンシ情報をトレースするパッケージを書いている(詳しくは Tracing HTTP request latency in golang).これはもともと以下のように各レイテンシ情報をそれぞれPrint関数に渡して出力する必要があった.

fmt.Printf("DNS lookup: %d ms", int(result.DNSLookup/time.Millisecond))
fmt.Printf("TCP connection: %d ms", int(result.TCPConnection/time.Millisecond))
fmt.Printf("TLS handshake: %d ms", int(result.TLSHandshake/time.Millisecond))
fmt.Printf("Server processing: %d ms", int(result.ServerProcessing/time.Millisecond))
fmt.Printf("Content transfer: %d ms", int(result.ContentTransfer/time.Millisecond))

これは使う側としてはめんどくさいのでfmt.Formatterを実装して%+vを指定すれば良いだけにした.

fmt.Printf("%+v\n", result)
DNS lookup:         461 ms
TCP connection:     163 ms
TLS handshake:      427 ms
Server processing:  165 ms
Content transfer:     0 ms
...

具体的な実装は https://github.com/tcnksm/go-httpstat/blob/master/httpstat.go#L84-L139 を参照.

fmt.Stringer

fmt.Fromatterを使う人はあまりいないと思う.それよりもよく使うのはfmt.Stringerだろう.これは単純に独自の型がfmtの関数に渡ったときの出力をカスタマイズできる.

例えばnet/httpパッケージには以下のような実装がある.

type contextKey struct {
    name string
}

func (k *contextKey) String() string { 
    return "net/http context value " + k.name 
}

これをfmt.Printlnに渡すと出力は以下のようになる.

fmt.Println(&contextKey{"http-server"}) // net/http context value http-server

(ちなみにcontextKeyServerContextKeyLocalAddrContextKeyの実装に使われている)

18
17
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
18
17