Stackdriver Logging からログを取得する(その2)

Posted: , Modified:   GoogleCloudPlatform Go Stackdriver Qiita

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

概要

Google Cloud Platform からログデータを取得する場合, Stackdriver Logging からログを取得するに記した logadminパッケージを利用する方法に加えて, APIバージョン2のlogging パッケージを使うこともできる. (バージョン1の方はログを書き出せるが読み出せなかったはず)

ざっと見た限り,logadminパッケージに比べて取得できるログの種類が多いように見えたので, その使用方法についてまとめる.

loggingパッケージ(Ver.2)

logadmin パッケージのドキュメントにはベータ版との記載されているが, バージョン2のloggingパッケージのドキュメントには実験的(EXPERIMENTAL)と書かれている. 将来大幅な変更があるかもしれないので,その点は注意.

loggingパッケージのクライアントを使って,変数 filter にマッチするログデータを取得する場合, 次のようなコードになる.

import (
	"context"

	"cloud.google.com/go/logging/apiv2"
	"google.golang.org/api/iterator"
	loggingpb "google.golang.org/genproto/googleapis/logging/v2"
)

func GetLogEntries(ctx context.Context, filter string) error{

	client, err := logging.NewClient(ctx)
	if err != nil {
		return
	}
	defer client.Close()

	iter := client.ListLogEntries(ctx, &loggingpb.ListLogEntriesRequest{
		ResourceNames: []string{
			fmt.Sprintf("projects/%v", "your-project-id"),
		},
		Filter: filter,
	})

	for {
		e, err := iter.Next()
		if err == iterator.Done {
			break
		} else if err != nil {
			return err
		}

		// ログデータ e に対する処理

	}

	return nil
}

logadmin パッケージとの違い

まず一つ目の違いは,logadminパッケージではプロジェクトIDをクライアントの作成時に渡していたのに対し, loggingパッケージではログデータのリクエスト時にリソース名として渡すことになっている点である. このリソース名には,プロジェクト以外にも色々なリソースを指定できるようだ.

loggingpb.ListLogEntriesRequest型のドキュメントによれば, このリソース名に指定できるフォーマットは,

という4種類になっている.

二つ目の違いは,イテレータが返すログデータが LogEntry型に変更され,アクセッサが追加された点である. logadminパッケージの場合だとログエントリのペイロードは interface{}型で 何をするにもキャストが必要であったが,LogEntry型の場合,ペイロードがテキストであれば GetTextPayload関数を,JSONオブジェクトの場合は GetJsonPayload関数を使用すれば良い.

ただし,GetJsonPayload関数はstructpb.Struct型であり, ログエントリ本来の構造を表す構造体に落とし込むためには変換が必要になる. 変換方法は前エントリで紹介した structpbconv が利用できる.
 例えば,イベントタイプが GCE_OPERATION_DONE のログだけを取得する場合,次のようになる.

import (
	"context"

	"cloud.google.com/go/logging/apiv2"
	"google.golang.org/api/iterator"
	loggingpb "google.golang.org/genproto/googleapis/logging/v2"
   	"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) (res *ActivityPayload) {
    res = new(ActivityPayload)
    structpbconv.Convert(payload, res)
    return
}

func GetLogEntries(ctx context.Context) error{

	client, err := logging.NewClient(ctx)
	if err != nil {
		return
	}
	defer client.Close()

	iter := client.ListLogEntries(ctx, &loggingpb.ListLogEntriesRequest{
		ResourceNames: []string{
			fmt.Sprintf("projects/%v", "your-project-id"),
		},
		Filter: `jsonPayload.event_type = "GCE_OPERATION_DONE"`,
	})

	for {

		e, err := iter.Next()
		if err == iterator.Done {
			return nil
		} else if err != nil {
			return err
		}

		payload := NewActivityPayload(e.GetJsonPayload())
		fmt.Println(payload.TraceID) // trace_id を出力

	}

}