Stackdriver Logging からログを取得する

Posted: , Modified:   Go GoogleCloudPlatform Stackdriver Logging Qiita

本稿は Qiita 投稿記事 のバックアップです.

概要

English

Google Cloud Platform からログデータを取得する. logadmin パッケージが提供する,Stackdriver クライアントは使いやすいのだが, 手に入る各ログエントリーは logging.Entry型で,ペイロードの取得が手間である. 本稿では,ログエントリー及びそのペイロード取得方法をまとめる.

logadmin パッケージ

logadmin は,比較的新しいパッケージで, ログの取得に関わる操作を提供している. 変数filterに入っているクエリにマッチするログデータのみを取得するならば, 下記の通りでよい.

import (
    "cloud.google.com/go/logging/logadmin"
    "golang.org/x/net/context"
    "google.golang.org/api/iterator"
)

func GetLogEntries(filter string) error{
    ctx := context.Background()
    client, err := logadmin.NewClient(ctx, "your-project-id")
    if err != nil {
        return err
    }
    defer client.Close()

    iter := client.Entries(ctx, logadmin.Filter(filter))
    for {
        e, err := iter.Next()
        if err == iterator.Done {
            return nil
        } else if err != nil {
            return err
        }
        // logging.Entry型のログデータ e に対する操作
    }
}

logging.Entry

上記の操作で手に入るログデータは,logging.Entry構造体になっている. この構造体では,ペイロードは interface{}型と定義されており,利用するためにはキャストが必要になる. なお,Google Cloud Platform ではこのペイロードはほとんどの場合 JSON オブジェクトである. また,実際に利用する場合は,フィルタの値からスキーマがある程度分かっていることが多いと思う.

さて,手元で調べたところ,このペイロードは structpb.Structへのポインタになっている. このstructpb.Struct型から目的のログデータを表す構造体へ変換するのだが, 簡単な方法が分からなかったので,自作の関数 structpbconv.Convert で変換することにした. (そのコードはここで公開している)

型変換

例えば次のコードは,このstructpbconvを使って,structpb.StructからCompute Engine VM インスタンスの activity_log を取得している.

import (
    "github.com/golang/protobuf/ptypes/struct"
    "github.com/jkawamoto/structpbconv"
)


type ActivityPayload struct {
    EventTimestampUs string `structpb:"event_timestamp_us"`
    EventType        string `structpb:"event_type"`
    TraceID          string `structpb:"trace_id"`
    Actor            struct {
        User string
    }
    Resource struct {
        Zone string
        Type string
        ID   string
        Name string
    }
    Version      string
    EventSubtype string `structpb:"event_subtype"`
    Operation    struct {
        Zone string
        Type string
        ID   string
        Name string
    }
}

func NewActivityPayload(payload *structpb.Struct) *ActivityPayload {
    var res ActivityPayload
    structpbconv.Convert(payload, &res)
    return &res
}

ActivityPayload構造体は, activity_log のペイロードに合わせて作成する. 実際は JSON オブジェクトなので,小文字+アンダーバーなど命名規則が異なっている部分の対応付けには, structpb タグを用いる.

先ほどのログエントリー取得と合わせると,

// イベントタイプが GCE_OPERATION_DONE のログだけを取得する
iter := client.Entries(
  ctx, logadmin.Filter("jsonPayload.event_type = \"GCE_OPERATION_DONE\""))
for {
    e, err := iter.Next()
    if err == iterator.Done {
        return nil
    } else if err != nil {
        return err
    }

    // logging.Entry型のログデータ e に対する操作
    if s, ok := e.Payload.(*structpb.Struct); ok {
        payload := NewActivityPayload(s)

        fmt.Println(payload.TraceID) // trace_id を出力
    }

}

などのようにログデータを使用できる.