Amazon Athena パースができず空白行になった原因のデータファイルを特定する

eyecatch_athena

Amazon Athena を利用してファイルをクエリーした時、パースができず空白行になった原因のデータファイルを特定する方法をご紹介します。Apacheのアクセスログのように大量のファイルの中から問題のファイルを特定するのは容易ではありません。

問題のデータ(テキストファイル)

今回は複数ファイルに分かれるアプリケーションログでパースができず空白行になった場合を例に解説します。 アプリログは以下の2つのファイルです。

  • applogs1-20170715.txt
2017-7-15 23:31:42.363 ERROR  com.example.app.LogbackSample            - expected error: 
java.lang.ArrayIndexOutOfBoundsException: 2
    at com.example.app.LogbackSample.doSomething(LogbackSample.java:13)
    at com.example.app.LogbackSample.main(LogbackSample.java:23)

  • applogs1-20170716.txt
2017-7-16 18:30:25.178 INFO   com.example.app.LogbackSample            - --- アプリケーション開始 ---
2017-7-16 18:30:25.187 INFO   com.example.app.LogbackSample            - --- アプリケーション終了 ---

上記のファイルに対して、テーブル定義します。

CREATE EXTERNAL TABLE IF NOT EXISTS applogs1(
    log_ts string,
    level string,
    class string,
    message string)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
  'serialization.format' = '1',
  'input.regex' = '(.{21,23}) (TRACE|DEBUG|INFO |WARN |ERROR|FATAL)  ([\\s\\w-.]+) - ((?:[^\\n\\r]+)(?:[\\n\\r]{1,2}[^\\n\\r]+)*)'
)
LOCATION 's3://cm-datalake/blog/20170906/'
TBLPROPERTIES ('has_encrypted_data'='false');

RegexSerdeでパースができず空白行が発生

クエリを実行すると以下のように、パース出来なかった行は空白行として表示されます。

20170906-regexserde-parse-error-2

隠しカラム"$path"を利用して問題のファイル名を特定

カラムの最後に"$path"を追加してクエリを実行します。ダブルクオーテーションで括ることを忘れないでください。

20170906-regexserde-parse-error-with-filepath-2

AthenaのクエリエンジンPrestoでは 隠しカラム"$path"を指定することで、データソースファイルのパスを出力することができます。

他に隠しカラムはないのか?

早速ソースコードを確認しました。

public class HiveColumnHandle
        implements ColumnHandle
{
    public static final int PATH_COLUMN_INDEX = -11;
    public static final String PATH_COLUMN_NAME = "$path";
    public static final HiveType PATH_HIVE_TYPE = HIVE_STRING;
    public static final TypeSignature PATH_TYPE_SIGNATURE = PATH_HIVE_TYPE.getTypeSignature();

    public static final int BUCKET_COLUMN_INDEX = -12;
    public static final String BUCKET_COLUMN_NAME = "$bucket";
    public static final HiveType BUCKET_HIVE_TYPE = HIVE_INT;
    public static final TypeSignature BUCKET_TYPE_SIGNATURE = BUCKET_HIVE_TYPE.getTypeSignature();

    private static final String UPDATE_ROW_ID_COLUMN_NAME = "$shard_row_id";
    :
    :

"$bucket" と "$shard_row_id"がありましたが、残念ながらともに構文エラーになりました。

20170906-error-for-other-hidden-column

最後に

当時、ELBv2(ALB)のログに対するクエリーで表示できないレコードがあるという報告を受けたものの、大量のログファイルの中でどこでエラーが発生したのか途方にくれていました。そんなときに出会ったのが隠しカラム"$path"でした。

Analyzing Data in S3 using Amazon Athenaで解説されているDDLでは、登録済みインスタンスからアイドルタイムアウトまで応答がない場合に-が設定される事が考慮されていないため、正規表現の解析でエラーが発生します。

なお、これを踏まえて、ELBv1/CLB(Apache Web のアクセスログ)、ELBv2(ALB)を探索するブログを書いています。以下のブログで解説している方法では-が設定されることを考慮して実装しています。

Amazon Athena RegexSerDe を利用して CLB ログ / Apache Web のアクセスログを探索する

Amazon Athena RegexSerDe を利用して ALB ログを探索する