15
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Logstashで二重に登録しないために

Last updated at Posted at 2017-06-06

概要

elastic社のブログに「Little Logstash Lessons: Handling Duplicates」という内容があがっています。
Google翻訳のお力を頂戴して、日本語で内容を確認したその結果です。

結論

elasticsearchにoutputするとき、IDが明示的に指定されていないと、elasticsearch側でIDが採番される。
これが重複して登録する元になっている。

なので、fingerprint filterを使って以下のようにアプローチすると良い。

ともにfingerprintを使うのは同じ。

  • 同一の中身のものは、同一のものとして扱いたいとき
    • 同じ内容のものは同じIDになるようにし、その値をDocumentのIDとする
  • 1回のイベントで送信したものは再送されたとしても、同一のものとして扱いたいとき
    • イベントごとにUUIDを生成し、その値をDocumentのIDとする
input {
  stdin {}
}
filter {
  fingerprint {
    source => "message"
    target => "[@metadata][fingerprint]"
    method => "MURMUR3"
  }
}
output {
  elasticsearch {
    hosts => "example.com"
    document_id => "%{[@metadata][fingerprint]}"
  }
}

試した環境

product version
Logstash 5.4.1

なお、logstashの出力結果にホスト名がありますが、記事用に書き換えたものです。

同一内容のものの重複を避ける

stdoutによる確認

まずは、記事にあった内容でそのまま確認します。
[@metadata][fingerpring]に出力していると、metadata => trueにしていないとstdoutで確認できないため、便宜的にfingerprintという項目にfilter結果を入れるように変更します。

input {
  stdin {}
}
filter {
  fingerprint {
    source => "message"
    target => "fingerprint"
    method => "MURMUR3"
  }
}
output {
  stdout {
     codec => rubydebug
  }
}

もし、targetを[@metadata][fingerprint]にしている場合なら、rubydebugにmetadata=>trueを足しましょう。

//略
filter {
  fingerprint {
    source => "message"
    target => "[@metadata][fingerprint]"
    method => "MURMUR3"
  }
}
output {
  stdout {
     codec => rubydebug {
        metadata => true
     }
  }
}

aiueoと入力した場合の結果がこちら。fingerprintのところを見てください。
もし、fingerprintの項目をIDに指定していたとしたら、aiueoというmessageを持つものは、IDがすべて4063197173となり、
2回、3回、n回送っても、IDが重複している限り1件のデータにしかならない、ということでしょう。

{
     "@timestamp" => 2017-06-06T06:44:48.590Z,
       "@version" => "1",
           "host" => "UCC-COFFEE-110YEN",
    "fingerprint" => 4063197173,
        "message" => "aiueo\r"
}

関数を変える

MURMUR3でなく、MD5がいいんや、という場合は、methodを変更します。

input {
  stdin {}
}
filter {
  fingerprint {
    source => "message"
    target => "fingerprint"
    method => "MD5"
    key => "hogehoge"
  }
}
output {
  stdout {
     codec => rubydebug
  }
}

同じようにaiueoと入れた結果がこちら。

{
     "@timestamp" => 2017-06-06T06:50:35.501Z,
       "@version" => "1",
           "host" => "UCC-COFFEE-110YEN",
    "fingerprint" => "03944d6aa32124dc928257becfc40299",
        "message" => "aiueo\r"
}

methodで、SHA1やMD5を使う場合は、keyオプションが必要です。

If you are using any of the cryptographic hash functions algorithms (like SHA1, MD5), it is required to provide a key option. The key can be any arbitrary string which is used to compute the HMAC.

もし、keyを指定せずにlogstashを実行した場合は、次のエラーメッセージを含んだ文字列が出力されて終了します。

"Cannot register filter fingerprint plugin. The error reported is:
              Key value is empty. Please fill in an encryption key"

keyは設定するようにしましょう。

fingerprint生成元の指定

今回はsource => "message"としていたため、messageに入っている文字列から生成していました。

sourceはarrayで指定できるため、複数の項目から値を生成したい場合も可能です。
複数の列が集まってPrimary Keyを作っているDBテーブルのようなものをイメージすればよいと思います。

たとえば、company_cd, code, parameterのようなもので1意にしたい、ということであれば

source => ["company_cd", "code", "parameter"]

のような書き方になるでしょう。

もし、任意の項目ではなく、すべての項目の文字列を1つの文字列のように連結したとして、そこからfingerprintを生成したい、ということであれば、concatenate_sources => "true" を設定してやればよいでしょう。

input {
  stdin {}
}
filter {
  fingerprint {
    #source => "message"
    concatenate_sources => "true"
    target => "fingerprint"
    method => "MD5"
    key => "hogehoge"
  }
}
output {
  stdout {
     codec => rubydebug
  }
}

先と同じようにaiueoと入れた場合でも、結果が変わっていることがわかります。

{
     "@timestamp" => 2017-06-06T06:55:36.683Z,
       "@version" => "1",
           "host" => "UCC-COFFEE-110YEN",
    "fingerprint" => "3029457907e46196f8510cccfa1ff3ba",
        "message" => "aiueo\r"
}

不慮の重複を避ける

Logstashが何らかの影響で処理中に停止し、キューに入ったものが2回送られてしまうようなときを想定する。
少なくとも1回は送信されるけど、まれに2回送信されるかもね、というのはAWSのLambdaでも聞いたような気がします。

input {
  stdin {}
}
filter {
  fingerprint {
    source => "message"
    target => "generated_id"
    method => "UUID"
  }
}
output {
  stdout {
     codec => rubydebug
  }
}

このようにして、aiueoと2回送信してみます。1回目と2回目で同じ文字列を送っているのに、異なるgenerated_idとなっていることが確認できました。

generated_idをdocument_idとしておけば、キューが2回送信されたとしても重複しなくて済む、ということですね。

aiueo
{
      "@timestamp" => 2017-06-06T07:11:40.978Z,
        "@version" => "1",
            "host" => "UCC-COFFEE-110YEN",
         "message" => "aiueo\r",
    "generated_id" => "647943f7-1cb2-40f1-9e46-471a641f1651"
}
aiueo
{
      "@timestamp" => 2017-06-06T07:11:42.332Z,
        "@version" => "1",
            "host" => "UCC-COFFEE-110YEN",
         "message" => "aiueo\r",
    "generated_id" => "7d5e0691-b37c-4df1-82b5-3968b4bdbc29"
}

終わりに

As you've seen in this post, the fingerprint filter can serve multiple purposes and is a plugin you should familiarize yourself in the Logstash ecosystem. Take a stab at using this filter and let us know what you think!

fingerprint filterはいろいろ使えますよ、と。
もっと起動が早くなればいうことなしなんだけどな。

参考文献

15
20
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?