GoのWebアプリケーションのSQLロガーとパフォーマンス分析ツールを作ってみた

はじめに

この記事は Go2 Advent Calendar 2018の記事です。

担当の10日より1時間ほど投稿が遅れてしまったことをお詫びします。

今回は、拙作のGo製のWebアプリケーションにSQLのクエリロガーを仕込めるOSS『graqt』 を紹介したいと思います。

過去に書いた記事を足して2で割ったような記事なので、graqtを知ってる方は読まなくてもいいと思います。*1

graqtとは

github.com

go-request-and-query-loggerです。

発音はガラクタでお願いします。

Go用のアクセスログとクエリのログを記録するためのhttp handlerミドルウェアで、そのログを解析するためのツールもついてます。

使い方

シンプルなWebアプリケーションの実装例

package main

import (
    "database/sql"
    "fmt"
    "math/rand"
    "net/http"
    "os"
    "time"

    _ "github.com/go-sql-driver/mysql"

    // 2つパッケージを追加してください
    "github.com/justinas/alice"
    "github.com/serinuntius/graqt"
)

var (
    // 2つ変数を追加してください
    traceEnabled = os.Getenv("GRAQT_TRACE")
    driverName   = "mysql"

    db *sql.DB
)

func init() {
    rand.Seed(time.Now().UnixNano())
}
func main() {

    // ifを追加して 環境変数 GRAQT_TRACEが1のときだけログを吐きます。
    // ISUCONで本番ベンチのときは オフにしておいたほうがいいと思います。
    // しかし、パフォーマンスには結構気を使ったので、そんなに低下はしないと思います。
    if traceEnabled == "1" {
        // driverNameは絶対にこれでお願いします。
        driverName = "mysql-tracer"

        // もし、ログを吐く場所を変えたかったら以下のメソッドを呼んで下さい。
        // デフォルトは、 query.logとrequest.logが このGoファイル(main.go)と同じディレクトリに出ると思います。
        // graqt.SetQueryLogger("log/query.log")
        // graqt.SetRequestLogger("log/request.log")
    }

    var err error

    // ここを先程の変数に変更してください
    db, err = sql.Open(driverName, "root:@/graqt")
    if err != nil {
        panic(err)
    }
    defer db.Close()

    mux := http.NewServeMux()
    mux.HandleFunc("/user", createUser)

    // ミドルウェアを追加します。
    var chain alice.Chain
    if traceEnabled == "1" {
        chain = alice.New(graqt.RequestId)
    } else {
        chain = alice.New()
    }

    // ミドルウェアを渡します
    http.ListenAndServe(":8080", chain.Then(mux))
}

func createUser(w http.ResponseWriter, r *http.Request) {
    // contextを使用する方のメソッドに変えてください。
    ctx := r.Context()
    stmt, _ := db.PrepareContext(ctx, "INSERT INTO `user` (email,age) VALUES (?, ?)")
    t1 := time.Now().UnixNano()
    age := rand.Intn(80)

    stmt.ExecContext(ctx, fmt.Sprintf("hoge%d@hoge.com", t1), age)

    w.Write([]byte(`ok`))
}

CLI部分

gocuiというパッケージを使って実装しています。

alpをリスペクトして作ったCLIの動作例*2

今はこれより進化してて、普通に全部の情報が正しく表示されるはずです。

まとめ

ある程度便利なの作ったはずなので、煮るなり焼くなりしてあげてください。

*1:あまり居ないと思いますが

*2:すみません、再度録画するのが面倒で使いまわしです・・・