My Note

自己理解のためのブログ

GoogleAnalyticsのPV数をGoとMackerelで可視化する

はじめに

今回はGoとMackerelを使ってGoogleAnalyticsのデータ(PV数)をMackerelにカスタムメトリクスとして投稿・可視化について書いていきます。

アーキテクチャ

f:id:yhidetoshi:20190919101124p:plain

  1. CloudWatch EventでLambdaを定期実行
  2. Lmabda ( Go ) でGoogleAnalyticsにAPIでデータを取得
  3. Lambda ( Go ) でMackerelにメトリクスを投げる

Google Cloud Platformについて

GoogleAnalyticsのAPIを有効にして、認証キーを作成します。

f:id:yhidetoshi:20190919084346p:plain

f:id:yhidetoshi:20190919084517p:plain

f:id:yhidetoshi:20190919084559p:plain

f:id:yhidetoshi:20190919084610p:plain

f:id:yhidetoshi:20190919084625p:plain

f:id:yhidetoshi:20190919084639p:plain

f:id:yhidetoshi:20190919084705p:plain

この鍵ファイルを使ってAPIを実行します。

f:id:yhidetoshi:20190919084701p:plain

GoogleAnalyticsについて

今回はGoogleAnalyticsの初期設定は完了している状態で進めます。

GCPで作成したサービスアカウントをGoogleAnalyticsに登録します。

f:id:yhidetoshi:20190919092459p:plain

f:id:yhidetoshi:20190919092519p:plain

f:id:yhidetoshi:20190919092517p:plain

f:id:yhidetoshi:20190919092514p:plain

ビューIDを確認する。

f:id:yhidetoshi:20190919092731p:plain

Lambda ( Go )について

LambdaにはGoogleAnalyticsにAPIでデータを取得してMackerelにメトリクスをPostする処理を定義して CloudWatch Eventを連携させます。Lambda関数やCloudwatchEventなどはServerlessFrameworkを利用してデプロイしました。

GoでGoogleAnalyticsのデータ取得にはこちらのパッケージを利用しました。

godoc.org

そして、Mackerelに取得したデータをメトリクスとして投稿にするところはこちらを利用!

github.com

*) Go: 1.12.4

  • main.go
package main

import (
    "context"
    "fmt"
    "os"
    "strconv"
    "time"

    "github.com/aws/aws-lambda-go/lambda"
    "github.com/mackerelio/mackerel-client-go"
    "golang.org/x/oauth2/google"
    "google.golang.org/api/analytics/v3"
    "google.golang.org/api/option"
)

var (
    mkrKey = os.Getenv("MKRKEY")
    client = mackerel.NewClient(mkrKey)

    json   = os.Getenv("GOOGLE_APPLICATION_CREDENTIALS_JSON")
    viewID = os.Getenv("VIEW_ID")
)

const (
    // GA
    startDate = "today"
    endDate   = "today"

    metricsUsers     = "users"
    metricsPVs       = "pagePath!=/preview"
    metricsPageViews = "pageviews"

    // Mackerel
    serviceName = "GoogleAnalytics"

    // Lambda
    timezone = "Asia/Tokyo"
    offset   = 9 * 60 * 60
)

func main() {
    lambda.Start(Handler)
}

// Handler Lambda
func Handler() {
    jst := time.FixedZone(timezone, offset)
    nowTime := time.Now().In(jst)

    ctx := context.Background()
    jwtConfig, err := google.JWTConfigFromJSON([]byte(json), analytics.AnalyticsReadonlyScope)
    if err != nil {
        fmt.Println(err)
    }

    ts := jwtConfig.TokenSource(ctx)
    client, err := analytics.NewService(ctx, option.WithTokenSource(ts))

    // Get Users
    resUsers, err := client.Data.Ga.Get("ga:"+viewID, startDate, endDate, "ga:"+metricsUsers).Do()
    if err != nil {
        fmt.Println(err)
    }

    // Get PVs
    resPVs, err := client.Data.Ga.Get(
        "ga:"+viewID, startDate, endDate, "ga:"+metricsPageViews).Filters("ga:" + metricsPVs).Do()
    if err != nil {
        fmt.Println(err)
    }

    intResultUsers, _ := strconv.Atoi(resUsers.TotalsForAllResults["ga:"+metricsUsers])
    intResultPVs, _ := strconv.Atoi(resPVs.TotalsForAllResults["ga:"+metricsPageViews])

    mkrErr := PostValuesToMackerel(intResultUsers, intResultPVs, nowTime)
    if err != nil {
        fmt.Println(mkrErr)
    }
}

// PostValuesToMackerel Post Metrics to Mackerel
func PostValuesToMackerel(intResultUsers int, intResultPVs int, nowTime time.Time) error {
    // Post users
    errUser := client.PostServiceMetricValues(serviceName, []*mackerel.MetricValue{
        &mackerel.MetricValue{
            Name:  "Users.users",
            Time:  nowTime.Unix(),
            Value: intResultUsers,
        },
    })
    if errUser != nil {
        fmt.Println(errUser)
    }

    // Post PV
    errPV := client.PostServiceMetricValues(serviceName, []*mackerel.MetricValue{
        &mackerel.MetricValue{
            Name:  "PV.PVs",
            Time:  nowTime.Unix(),
            Value: intResultPVs,
        },
    })
    if errPV != nil {
        fmt.Println(errPV)
    }

    return nil
}

認証ファイル(json)のセット方法

序盤でGoogleAnalyticsの認証に使うjsonファイルを生成しました。環境変数でFilePathを指定して実行してもいいみたいです。( 下記リンクに記載があります )

今回は認証ファイル(json)を環境変数に中身をセットして読み込む方法にしました。 ( Lambdaの環境変数にkey/valueでセットしたかったので )

以下、コードの一部抜粋です。

  • main.go
jwtConfig, err := google.JWTConfigFromJSON([]byte(json), analytics.AnalyticsReadonlyScope)
    if err != nil {
        fmt.Println(err)
    }

    ts := jwtConfig.TokenSource(ctx)
    client, err := analytics.NewService(ctx, option.WithTokenSource(ts))
func JWTConfigFromJSON(jsonKey []byte, scope ...string) (*jwt.Config, error) {
    var f credentialsFile
    if err := json.Unmarshal(jsonKey, &f); err != nil {
        return nil, err
    }
    if f.Type != serviceAccountKey {
        return nil, fmt.Errorf("google: read JWT from JSON credentials: 'type' field is %q (expected %q)", f.Type, serviceAccountKey)
    }
    scope = append([]string(nil), scope...) // copy
    return f.jwtConfig(scope), nil
}
// credentialsFile is the unmarshalled representation of a credentials file.
type credentialsFile struct {
    Type string `json:"type"` // serviceAccountKey or userCredentialsKey

    // Service Account fields
    ClientEmail  string `json:"client_email"`
    PrivateKeyID string `json:"private_key_id"`
    PrivateKey   string `json:"private_key"`
    TokenURL     string `json:"token_uri"`
    ProjectID    string `json:"project_id"`

    // User Credential fields
    // (These typically come from gcloud auth.)
    ClientSecret string `json:"client_secret"`
    ClientID     string `json:"client_id"`
    RefreshToken string `json:"refresh_token"`
}

→ 認証ファイル(json)をパースして利用しています。

認証ファイルを環境変数にセットできるように加工

そのままだとjsonの中身をbashで exportで環境変数にセットできないので以下のように修正しました。

  • 変更パターン

    • " --> \"
    • \n --> "'\n'"
  • 変更前 ( 一部抜粋 )

  "type": "service_account",
  "private_key": "-----BEGIN PRIVATE KEY-----\n"
  • 変更後 ( 一部抜粋 )
  \"type\": \"service_account\",
  \"private_key\": \"-----BEGIN PRIVATE KEY-----"'\n'"\"

GoogleAnalyticsから欲しい情報を取得する

今回取得するのは、今日のPV数です。クエリパラメータは以下のようにセットしました。 取得したデータに合わせて、クエリパラメータのページを参考に書けばいいかと。

( metricsPVs = "pagePath!=/preview"はてブロで執筆中に確認するプレビューはカウントしないようにしています )

  • main.go ( 一部抜粋 )
const (
    // GA
    startDate = "today"
    endDate   = "today"

    metricsUsers     = "users"
    metricsPVs       = "pagePath!=/preview"
    metricsPageViews = "pageviews"

    dimensionsTitle = "pageTitle"
    dimensionsPath  = "pagePath"

    // Mackerel
    serviceName = "GoogleAnalytics"

    // Lambda
    timezone = "Asia/Tokyo"
    offset   = 9 * 60 * 60
)

        // Get PVs
    resPVs, err := client.Data.Ga.Get(
        "ga:"+viewID, startDate, endDate, "ga:"+metricsPageViews).Filters("ga:" + metricsPVs).Do()

以下のページにクエリパラメータについて記載されています。

developers.google.com

Lambdaの定期実行

CloudWatch Eventを利用して定期実行させます。今回は15分ごとに実行するようにしました。 f:id:yhidetoshi:20190919150430p:plain

Mackerelにメトリクスを投稿する

GoでMackerelにメトリクスを投稿する方法は以前のブログに書いているのでよければ参照ください!

yhidetoshi.hatenablog.com

今回書いたMackerelにメトリクスをPostするコードは以下の通りです。

  • main.go ( 一部抜粋 )
// PostValuesToMackerel Post Metrics to Mackerel
func PostValuesToMackerel(intResultUsers int, intResultPVs int, nowTime time.Time) error {
    // Post users
    errUser := client.PostServiceMetricValues(serviceName, []*mackerel.MetricValue{
        &mackerel.MetricValue{
            Name:  "Users.users",
            Time:  nowTime.Unix(),
            Value: intResultUsers,
        },
    })
    if errUser != nil {
        fmt.Println(errUser)
    }

    // Post PV
    errPV := client.PostServiceMetricValues(serviceName, []*mackerel.MetricValue{
        &mackerel.MetricValue{
            Name:  "PV.PVs",
            Time:  nowTime.Unix(),
            Value: intResultPVs,
        },
    })
    if errPV != nil {
        fmt.Println(errPV)
    }

    return nil
}
  • Mackerelの画面 ( 実行結果 )

f:id:yhidetoshi:20190919143643p:plain

デプロイ

export MKRKEY=XXX
export GOOGLE_APPLICATION_CREDENTIALS_JSON="{...}"
  • Mackerelにサービスを作成する
    • 名前は GoogleAnalytics で作成する
curl -X POST https://api.mackerelio.com/api/v0/services \
    -H "X-Api-Key: ${MKRKEY}" \
    -H "Content-Type: application/json" \
    -d '{"name": "GoogleAnalytics", "memo": "google analytics"}'
make build
  • ServerlessFrameworkでデプロイする
sls deploy --aws-profile <PROFILE> --viewid <VIEW-ID> --mkrkey ${MKRKEY} --google-apikey ${GOOGLE_APPLICATION_CREDENTIALS_JSON}

さいごに

Goで利用してGoogleAnalyticsのデータをMackerelのメトリクスとして投稿・可視化をしました。 今回はじめて Google Cloud Servicesに接続するためのライブラリを利用したので、これから活用していくいいきっかけになりました。 他にも個人的に可視化したいメトリクスがあり、最近ありがたいことにアンバサダプランになったとこもあるので、どんどん活用していこうと思います!