printf デバッグは便利だ。技術の後退と言われようと printf でないと解決できない事はまだまだたくさんあります。
今日は net/http
でクライアントが得たレスポンスの JSON を確認したいといった場合に、どうデバッグしたらいいかを書いてみたいと思う。
Go のインタフェースは大よそ io.Reader
もしくは io.Writer
を使う様に設計されている。こうする事でプログラムがメモリを一度に沢山確保してしまわない様にしています。
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
type Foo struct {
ID string `json:"id"`
Content string `json:"content"`
}
func main() {
resp, err := http.Get("http://example.com")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
var foo Foo
err = json.NewDecoder(resp.Body).Decode(&foo)
if err != nil {
log.Fatal(err)
}
fmt.Println(foo.Content)
}
例えばこういうコードの、resp.Body
に何が流れているのか確認したい場合、デバッグ出力する為に一旦 ioutil.ReadAll
で全て読み取ったりしていないでしょうか。
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
type Foo struct {
ID string `json:"id"`
Content string `json:"content"`
}
func main() {
resp, err := http.Get("http://example.com")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
var foo Foo
err = json.NewDecoder(bytes.NewReader(b)).Decode(&foo)
if err != nil {
log.Fatal(err)
}
fmt.Println(foo.Content)
}
デバッグ表示したいだけなのに、ちょっとコードが増えてしまった感じがしますよね。デバッグを無効にしたいときに消すコードも多い。しかも json.NewDecoder
の部分にも手を入れてしまわないといけなくてなんだか嫌な感じもします。元のコードは json.NewDecoder
の箇所に手を入れられるから良いですが、時には io.Reader を引数に持つ関数に渡す必要があったり、ioutil.ReadAll
で全て読み取る事が出来ないストリームデータの場合には使えません。こういった場合は io.TeeReader
を使います。
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
)
type Foo struct {
ID string `json:"id"`
Content string `json:"content"`
}
func main() {
resp, err := http.Get("http://example.com")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
var r io.Reader = resp.Body
r = io.TeeReader(r, os.Stderr)
var foo Foo
err = json.NewDecoder(r).Decode(&foo)
if err != nil {
log.Fatal(err)
}
fmt.Println(foo.Content)
}
こうしておき、必要に応じて r = io.TeeReader(r, os.Stderr)
の行をコメントアウトすれば良いのです。コメントアウトを外せばデバッグ表示になります。メモリも節約出来てお得感ありますね。