Logstash + Elasticsearch連携時のLogstash confメモ(jsonからconvert, filter, dateなど)

LogstashからElasticsearchへデータを送信する仕組みを試しました。Logstashのconfの参考になれば幸い。
json側のフィールドをElasticsearchのdate型にconvertしたり、文字列を数値にconvertしたりしました。

ログはjsonフォーマット

{"reportDate":"2016-11-08 00:00:00","reportType":"report","gender":"female","ageBracket":"18-24","value":"269"}

Logstashのconfファイル

input {
  file {
    path => "/path_to_log_file"
    codec => "json"
  }
}

filter {
  date {
    match => ["reportDate", "YYYY-MM-dd HH:mm:ss" ]
  }
  date {
	  match => ["reportDate", "yyyy-MM-dd HH:mm:ss"]
	  target => "reportDate"
  }
  mutate {
    convert => {
      "value" => "integer"
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "test"
  }
}

Logstashはinput/codec/filter/outputのそれぞれの処理を理解する必要がある。
今回の例でいうと以下のように整理できる。

1: input

  • inputはログファイル(file)から行う

2:codec

  • inputから受け取ったログファイルをjson形式に整形する

3:filter

  • reportDateをElasticsearchの@timestampへ適応させている。(以下のコードが参考箇所)
  date {
    match => ["reportDate", "YYYY-MM-dd HH:mm:ss" ]
  }
  • また、reportDateの値はElasticsearchの1つのvalueとしてインデックスされるが型がdate型に変換している。(以下のコードが参考箇所)
  date {
	  match => ["reportDate", "yyyy-MM-dd HH:mm:ss"]
	  target => "reportDate"
  }
  • 最後にvalueの値をinteger型に変換している(以下のコードが参考箇所)
  mutate {
    convert => {
      "value" => "integer"
    }
  }

4:output

  • ログの出力先にElasticsearchを指定している。インデックスはtest。

関連エントリ

naruto-io.hatenablog.com

go langからGoogle Analytics APIを使う

こちらの記事↓ではSpring Boot + kotlinからの利用方法でしたがgoからの利用も調査しました。

Spring Boot + kotlinでGoogle Analytics APIを使ってみた - 平日インプット週末アウトプットぶろぐ

簡易的なデータの取り方を確認できたので記載。

main.go

package main

import (
	"fmt"
	"log"
	"os"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	analytics "google.golang.org/api/analytics/v3"
)

func main() {

	client, err := google.DefaultClient(
		oauth2.NoContext,
		"https://www.googleapis.com/auth/analytics.readonly")
	if err != nil {
		log.Fatalf("Unable to read client : %v", err)
	}

	service, err := analytics.New(client)
	if err != nil {
		log.Fatalf("Unable to Access Google Analytics: %v", err)
	}

	result, err := service.Data.Ga.Get("ga:"+os.Getenv("PROFILE_ID"), "yesterday", "today", "ga:sessions").Dimensions("ga:visitorGender,ga:visitorAgeBracket").Do()

	if err != nil {
		log.Fatalf("Unable to get data: %v", err)
	}

	for _, row := range result.Rows {
		fmt.Printf("%s,%s,%s\n", row[0], row[1], row[2])
	}
}

実行する

GOOGLE_APPLICATION_CREDENTIALS=secret.json PROFILE_ID=xxxxx go run main.go
  • GOOGLE_APPLICATION_CREDENTIALSにConsoleから取得したサービスアカウントの鍵ファイル(json)のパスを指定
  • PROFILE_IDにGoogleアナリティクスのprofileIDを指定

実行結果

$ GOOGLE_APPLICATION_CREDENTIALS=secret.json PROFILE_ID=xxxxx go run main.go
female,18-24,260
female,25-34,1495
female,35-44,1503
female,45-54,424
female,55-64,55
male,18-24,28
male,25-34,106
male,35-44,132
male,45-54,32

※ 2016/11現在、Google Analytics APIのgoのサポートはalphaバージョンですのでご利用はご注意ください

developers.google.com

Spring Boot + KotlinでGoogle Analytics APIを使ってみた

Googleアナリテクスの設定は本家情報を参照

https://developers.google.com/analytics/devguides/reporting/core/v3/quickstart/service-java?hl=ja

手順まとめ
  1. Google API Consoleでサービスアカウントを作成して認証する鍵を作成(今回はp12ファイルを使う)
  2. Googleアナリティクスのユーザ追加でAPI Consoleで作成したアクアンとを追加する

※ 2のConsoleで作ったアカウントをGoogleアナリティクスにユーザ追加が抜けていたため、ハマってしまった。

ビルドツールはgraldeを使う

compile "com.google.apis:google-api-services-analytics:$google_api_version"

versionは4.7を使う。

Configurationのインスタンス

@Configuration
open class GoogleAnalyticsConfig {

    val applicationName = "googleAnalytics"
    val jsonFactory = JacksonFactory.getDefaultInstance()!!
    val serviceAccountEmail = "xxxxx@xxxxx.iam.gserviceaccount.com"
    val keyFileLocation = "/credential.p12"

    @Bean
    open fun analytics(): Analytics {
        val httpTransport = GoogleNetHttpTransport.newTrustedTransport()
        val credential = GoogleCredential.Builder()
                .setTransport(httpTransport)
                .setJsonFactory(jsonFactory)
                .setServiceAccountId(serviceAccountEmail)
                .setServiceAccountPrivateKeyFromP12File(
                        File(javaClass.getResource(keyFileLocation).path))
                .setServiceAccountScopes(AnalyticsScopes.all())
                .build();
        return Analytics.Builder(httpTransport, jsonFactory, credential)
                .setApplicationName(applicationName).build();
    }
}
  • serviceAccountEmailはAPI Consoleで作成したアカウントのメールアドレス
  • keyFileLocationはAPI Consoleで作成したアカウントの認証鍵

データの取得

  • 昨日から今日までのセッション数を性別と年齢別に取得する
  • "ga:" + "xxxxx"のxxxxxはアナリティクスのprofileIDを指定する
  • 簡易的なコントローラーを用意してlog出力
@RestController
open class AnalyticsController @Autowired constructor(val googleAnalytics: Analytics) {

    val log: Logger = LoggerFactory.getLogger("ANALYTICS");

    @RequestMapping(value = "/analytics", method = arrayOf(RequestMethod.GET))
    fun index(): String {
        val gaData = googleAnalytics.data().ga()
                .get("ga:" + "xxxxx", "yesterday", "today", "ga:sessions")
                .setDimensions("ga:visitorGender,ga:visitorAgeBracket")
                .execute()

        for (row in gaData.rows) {
            log.info(Moshi.Builder().build()
                    .adapter(List::class.java)
                    .toJson(row))
        }

        return "ok";
    }
}
出力例
{"@timestamp":"2016-11-07T12:10:56.021+09:00","@version":1,"message":"[\"female\",\"18-24\",\"xxx\"]","logger_name":"ANALYTICS","thread_name":"http-nio-8080-exec-7","level":"INFO","level_value":20000,"HOSTNAME":"xxxxx"}
{"@timestamp":"2016-11-07T12:10:56.022+09:00","@version":1,"message":"[\"female\",\"25-34\",\"xxx\"]","logger_name":"ANALYTICS","thread_name":"http-nio-8080-exec-7","level":"INFO","level_value":20000,"HOSTNAME":"xxxxx"}
{"@timestamp":"2016-11-07T12:10:56.023+09:00","@version":1,"message":"[\"female\",\"35-44\",\"xxx\"]","logger_name":"ANALYTICS","thread_name":"http-nio-8080-exec-7","level":"INFO","level_value":20000,"HOSTNAME":"xxxxx"}
{"@timestamp":"2016-11-07T12:10:56.024+09:00","@version":1,"message":"[\"female\",\"45-54\",\"xxx\"]","logger_name":"ANALYTICS","thread_name":"http-nio-8080-exec-7","level":"INFO","level_value":20000,"HOSTNAME":"xxxxx"}
{"@timestamp":"2016-11-07T12:10:56.025+09:00","@version":1,"message":"[\"female\",\"55-64\",\"xxx\"]","logger_name":"ANALYTICS","thread_name":"http-nio-8080-exec-7","level":"INFO","level_value":20000,"HOSTNAME":"xxxxx"}
{"@timestamp":"2016-11-07T12:10:56.025+09:00","@version":1,"message":"[\"male\",\"18-24\",\"xxx\"]","logger_name":"ANALYTICS","thread_name":"http-nio-8080-exec-7","level":"INFO","level_value":20000,"HOSTNAME":"xxxxx"}
{"@timestamp":"2016-11-07T12:10:56.026+09:00","@version":1,"message":"[\"male\",\"25-34\",\"xxx\"]","logger_name":"ANALYTICS","thread_name":"http-nio-8080-exec-7","level":"INFO","level_value":20000,"HOSTNAME":"xxxxx"}
{"@timestamp":"2016-11-07T12:10:56.027+09:00","@version":1,"message":"[\"male\",\"35-44\",\"xxx\"]","logger_name":"ANALYTICS","thread_name":"http-nio-8080-exec-7","level":"INFO","level_value":20000,"HOSTNAME":"xxxxx"}
{"@timestamp":"2016-11-07T12:10:56.028+09:00","@version":1,"message":"[\"male\",\"45-54\",\"xxx\"]","logger_name":"ANALYTICS","thread_name":"http-nio-8080-exec-7","level":"INFO","level_value":20000,"HOSTNAME":"xxxxx"}