Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

embulk-parser-grokの紹介

More than 5 years have passed since last update.

先日の Embulk Meetup Tokyo #2 で発表したembulk-parser-grokというプラグインが好評だったので、使い方を紹介したいと思います。

embulk-parser-grok

embulk-parser-grokは、Grokというライブラリを利用した汎用的なパーサライブラリです。

Grokはほぼ正規表現のようなものなのですが、パターンに名前をつけて再利用できるという大きなメリットを持っています。

社内ではこのプラグインを使って、各種アプリケーションログ、Apacheのアクセスログ、Javaのスレッドダンプ、GC Log、JMeterの出力などをパースしています。
汎用的に使えるのでとても重宝しています。

簡単なログをパースしてみる

こんな感じの簡単なログをパースしてみましょう。

access.log
/index.html GET 1234
/sample.html POST 1
/api/status GET 9999
/api/save POST 100

まずはgrokのパターンファイルをつくります。

sample.patterns
URIPATH     (?:/[A-Za-z0-9$.+!*'(){},~:;=@#%_\-]*)+
WORD        \b\w+\b
NUMBER      (?<![0-9.+-])(?>[+-]?(?:(?:[0-9]+(?:\.[0-9]+)?)|(?:\.[0-9]+)))
ACCESS_LOG  %{URIPATH:request} %{WORD:method} %{NUMBER:duration}

パターン名のあとにスペースをあけてパターンを記述します。パターンは正規表現で記述できます。
パターンの中で%{パターン名:id}と記述することで、パターンを再利用することができます。
idはパターンにマッチした文字列を取り出すための識別子になります。パターンにマッチした結果を利用しない場合はidに"UNWANTED"を指定します。

なお、パターンファイルは自分で書かなくても、汎用的なものをlogstashのリポジトリなどで見つけられるので、必要に応じてもらってくるとよいでしょう。

また、Grok Constructorというサービスを使うとブラウザ上でパターンの確認ができるので便利です。

次に、embulkの設定ファイルをつくります。

config.yml
in:
  type: file
  path_prefix: access.log
  parser:
    type: grok
    grok_pattern_files:
      - sample.patterns
    grok_pattern: '%{ACCESS_LOG}'
    charset: UTF-8
    newline: CRLF
    columns:
    - {name: request, type: string}
    - {name: method, type: string}
    - {name: duration, type: long}

parsertypeに"grok"を指定します。
grok_pattern_filesには、さきほど作成したパターンファイルのパスを指定します。
grok_patternには、ログにマッチするパターンの名前を指定します。
charset,newline,columnsは他のparserプラグインと同じ使い方になります。なお、columnnameは、パターンファイルに記述したidと同じ名前を指定します。

実行してみましょう。

$ embulk preview config.yml

以下のように出力されたら成功です。

+----------------+---------------+---------------+
| request:string | method:string | duration:long |
+----------------+---------------+---------------+
|    /index.html |           GET |         1,234 |
|   /sample.html |          POST |             1 |
|    /api/status |           GET |         9,999 |
|      /api/save |          POST |           100 |
+----------------+---------------+---------------+

複数行のログをパースする

embulk-parser-grokは、1つのレコードが複数行に渡るログにも対応しています。

例えば、以下のようなスタックトレースが含まれるログファイルをパースしてみましょう。

multiline.log
2015-12-15 11:45:44.342+0900 [INFO] (transaction): Loaded plugin embulk-parser-grok (0.1.5)
2015-12-15 11:45:44.355+0900 [INFO] (transaction): Listing local files at directory 'src/test/resources' filtering filename by prefix 'apache.log'
2015-12-15 11:45:44.358+0900 [INFO] (transaction): Loading files [src/test/resources/apache.log]
org.embulk.exec.PartialExecutionException: org.embulk.config.ConfigException: com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'ruby': was expecting ('true', 'false' or 'null')
 at [Source: ruby; line: 1, column: 9]
    at org.embulk.exec.BulkLoader$LoaderState.buildPartialExecuteException(org/embulk/exec/BulkLoader.java:363)
    at org.embulk.exec.BulkLoader.doRun(org/embulk/exec/BulkLoader.java:572)
    at org.embulk.exec.BulkLoader.access$000(org/embulk/exec/BulkLoader.java:33)
    at org.embulk.exec.BulkLoader$1.run(org/embulk/exec/BulkLoader.java:374)
    at org.embulk.exec.BulkLoader$1.run(org/embulk/exec/BulkLoader.java:370)
    at org.embulk.spi.Exec.doWith(org/embulk/spi/Exec.java:25)
    at org.embulk.exec.BulkLoader.run(org/embulk/exec/BulkLoader.java:370)
    at org.embulk.EmbulkEmbed.run(org/embulk/EmbulkEmbed.java:180)
    at java.lang.reflect.Method.invoke(java/lang/reflect/Method.java:497)
    at RUBY.run(/home/ikezoe/.embulk/bin/embulk!/embulk/runner.rb:77)
    at RUBY.run(/home/ikezoe/.embulk/bin/embulk!/embulk/command/embulk_run.rb:296)
    at RUBY.<top>(/home/ikezoe/.embulk/bin/embulk!/embulk/command/embulk_main.rb:2)
    at org.jruby.RubyKernel.require(org/jruby/RubyKernel.java:940)
    at RUBY.(root)(uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/rubygems/core_ext/kernel_require.rb:1)
    at home.ikezoe.$_dot_embulk.bin.embulk.embulk.command.embulk_bundle.<top>(file:/home/ikezoe/.embulk/bin/embulk!/embulk/command/embulk_bundle.rb:51)
    at java.lang.invoke.MethodHandle.invokeWithArguments(java/lang/invoke/MethodHandle.java:627)
    at org.embulk.cli.Main.main(org/embulk/cli/Main.java:23)

このログにマッチするパターンを作成します。

multiline.patterns
MULTILINES (.*+\n)*+

MULTILINELOG_FIRSTLINE %{TIMESTAMP_ISO8601:timestamp} \[%{LOGLEVEL:log_level}\] %{DATA:message}$
MULTILINELOG %{TIMESTAMP_ISO8601:timestamp} \[%{LOGLEVEL:log_level}\] %{DATA:message}(?:\n%{MULTILINES:stack_trace})?$

複数行のログをパースする場合は、ログの1行目だけにマッチするパターン(MULTILINELOG_FIRSTLINE)と、ログ全体にマッチするパターン(MULTILINELOG)の2種類を用意します。

なお、上記のパターンファイルに含まれる TIMESTAMP_ISO8601, LOGLEVEL, DATAなどのパターンは logstashのリポジトリにあるgrok-patterns に定義されているものを使います。

embulkの設定ファイルをつくります。

config.yml
in:
  type: file
  path_prefix: multiline.log
  parser:
    type: grok
    grok_pattern_files: 
      - grok-patterns
      - multiline.patterns
    first_line_pattern: '%{MULTILINELOG_FIRSTLINE}'
    grok_pattern: '%{MULTILINELOG}'
    charset: UTF-8
    newline: CRLF
    columns:
      - {name: timestamp, format: '%Y-%m-%d %H:%M:%S.%N%z', type: timestamp}
      - {name: log_level, type: string}
      - {name: message, type: string}
      - {name: stack_trace, type: string}

今回はパターンファイルを複数個使うので、grok_pattern_filesにファイルのパスを2つ指定しています。
first_line_patternにログの1行目にマッチするパターン、grok_patternにログ全体にマッチするパターンを記述します。
その他は、1行のログの場合と同じです。

その他のオプション

stop_on_invalid_record

パターンにマッチしないレコードがあったときに、処理を停止する(true)か、無視して続行する(false)かを指定することができます。
デフォルトでは、処理を続行する(false)設定になっています。

timestamp_parser

EmbulkではタイムスタンプをパースするためにJRubyのAPIを使っているのですが、このパーサが非常に遅いという問題があります。

https://github.com/embulk/embulk/issues/145

そこで、embulk-parser-grokプラグインでは利用するタイムスタンプのパーサをオプションで選択できるようにしています。
timestamp_parserを指定しないか"ruby"を指定した場合は標準のタイムスタンプパーサ、"sdf"や"SimpleDateFormat"を指定するとJavaのSimpleDateFormatを利用します。
どちらのパーサを使う場合でも、columnsに指定するフォーマットはJRuby形式のものにしてください。(例: %Y-%m-%d %H:%M:%S.%N%z)

なお、Embulk Meetupで標準のタイムスタンプのパーサが遅いとお伝えしたところ、v0.8で対応してくれることになりました。
そうなればこのオプションは不要ですね。

guess機能

embulk-parser-grokプラグインはguess機能も備えています。

ここでは、とあるApacheのアクセスログを推測してみましょう。Apacheのアクセスログはカスタマイズされている可能性があるので、いくつかのパターンの中からマッチするものを探す必要があります。

まず、以下のような設定ファイルをつくります。

config.yml
in:
  type: file
  path_prefix: apache.log
  parser:
    type: grok
    grok_pattern_files:
      - grok-patterns
    guess_patterns:
      - "%{COMBINEDAPACHELOG}"
      - "%{COMMONAPACHELOG}"
    charset: UTF-8
    newline: CRLF

guess_patternsに、パターンファイルに記述されているパターン名を羅列します。
ここに指定したパターンの中からマッチするものを推測します。なお、複数のパターンにマッチした場合は、一番最初にマッチしたものが採用されます。
ここでは grok-patterns に定義されている%{COMBINEDAPACHELOG}%{COMMONAPACHELOG}を指定します。

guessコマンドを実行してみましょう。

$ embulk guess -g grok config.yml -o apache.yml

guessに成功すると、以下のようにgrok_patternにマッチするパターン名が指定され、columnsの定義が出力されます。

apache.yml
in:
  type: file
  path_prefix: apache.log
  parser:
    type: grok
    grok_pattern_files: [grok-patterns]
    guess_patterns: ['%{COMBINEDAPACHELOG}', '%{COMMONAPACHELOG}']
    charset: UTF-8
    newline: CRLF
    grok_pattern: '%{COMBINEDAPACHELOG}'
    columns:
    - {name: request, type: string}
    - {name: agent, type: string}
    - {name: COMMONAPACHELOG, type: string}
    - {name: auth, type: string}
    - {name: ident, type: string}
    - {name: verb, type: string}
    - {name: referrer, type: string}
    - {name: bytes, type: long}
    - {name: response, type: long}
    - {name: clientip, type: string}
    - {name: COMBINEDAPACHELOG, type: string}
    - {name: httpversion, type: string}
    - {name: rawrequest, type: string}
    - {name: timestamp, format: '%d/%b/%Y:%T %z', type: timestamp}

ただし、現在のところ複数行ログのguessには未対応です。また、タイムスタンプのフォーマットと、データ型の推測がちょっと弱いです。

なお、Embulk 0.7.10ではJavaで書かれたGuess Pluginが動かない不具合があるのでご注意ください。( https://github.com/embulk/embulk/pull/348 )

おわりに

embulk-parser-grokプラグインを使うと、様々なフォーマットのログをパースすることができるようになります。
とても便利なのでぜひ使ってみてください。フィードバックもお待ちしております。

ちなみに

zoetro
YAMLエンジニア
http://zoetrope.hatenablog.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away