[Java 応用] ログファイルを集計するバッチファイル(.bat)を作る

0
1062
views

ログファイルを集計するバッチファイルを作ってみます。実際に処理を行うのは Java プログラムです。

LogAggregator

LogAggregator は、ログを集計/整形するツールです。バッチファイルに複数のログファイルをドラッグ&ドロップすることで、処理したログデータをCSVファイルとして吐き出します。

利用するログは、夜間バッチ処理の開始終了を記録したログです。

求められる仕様

・バッチ終了時のログ行だけを集計する
・バッチ名、日付、開始時、終了時、データ処理件数などを取得する
・開始、終了時を元に処理時間を計算し、処理効率を算出する
・複数のログファイルをドラッグ/ドロップで処理する
・集計結果は CSV ファイルで出力する
・メモリの独占やメモリ圧迫による実行時エラーを起こさない

主要プログラム

バッチログの集計に必要なプログラムは次の3つです。

・LogAggregator.java
・LogAggregator.class
・run-aggregator.bat

LogAggregator.class は LogAggregator.java をコンパイルして生成します。以下はコンパイルの実行の様子です。

LogAggregator.java

LogAggregator.java は、実際にログの集計/整形を行い CSV ファイルの出力を行うプログラムです。以下がプログラムの内容です。

import java.io.File;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import java.math.BigDecimal;

public class LogAggregator {
    public static void main(String... args) {
        try {
            File file = new File(args[0]);
            BufferedReader br = new BufferedReader(new FileReader(file));
            FileWriter fw = new FileWriter("./output.csv", true);
            PrintWriter pw = new PrintWriter(new BufferedWriter(fw));
            String contents = br.readLine();
            String utf8Contents;
            while (contents != null) {
                //読み込んだ文字列の文字コードを sjis から utf-8 に変換
                utf8Contents = new String(contents.getBytes("UTF-8"), "UTF-8");
                if (utf8Contents.indexOf("終了しました") != -1) {
                    //2文字目から12文字目を切り抜いて「日付」とする
                    String jobDate = utf8Contents.substring(1,11);
                    pw.print(jobDate + ",");
                    //「ジョブ名」の取得・表示
                    int jobNameStartIndex = utf8Contents.indexOf("[BATCH");
                    pw.print(utf8Contents.substring(jobNameStartIndex + 1, jobNameStartIndex + 10) + ",");
                    //「開始時間」の取得・表示
                    int jobStartTimeStartIndex = utf8Contents.indexOf("開始時間[");
                    String jobStartTime = utf8Contents.substring(jobStartTimeStartIndex + 5, jobStartTimeStartIndex + 13);
                    pw.print(jobStartTime + ",");
                    //「終了時間」の取得・表示
                    int jobEndTimeStartIndex = utf8Contents.indexOf("終了時間[");
                    String jobEndTime = utf8Contents.substring(jobEndTimeStartIndex + 5, jobEndTimeStartIndex + 13);
                    pw.print(jobEndTime + ",");
                    Date startDate = parseStrToDate(jobDate + " " + jobStartTime);
                    Date endDate = parseStrToDate(jobDate + " " + jobEndTime);
                    //「処理時間」の計算
                    Long longStartDate = startDate.getTime();
                    Long longEndDate = endDate.getTime();
                    Long longDiff = (longEndDate - longStartDate);
                    Date dateDiff = new Date(longDiff);
                    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
                    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
                    String strDiff = sdf.format(dateDiff);
                    pw.print(strDiff + ",");
                    //「入力件数」の取得・表示
                    int inputCountsStartIndex = utf8Contents.indexOf("入力件数=(");
                    int safeCountsStartIndex = utf8Contents.indexOf("),正常件数=(");
                    String strInputCounts = utf8Contents.substring(inputCountsStartIndex + 6, safeCountsStartIndex);
                    pw.print(strInputCounts + ",");
                    //「正常件数」の取得・表示
                    int warnCountsStartIndex = utf8Contents.indexOf("),異常件数=(");
                    String strSafeCounts = utf8Contents.substring(safeCountsStartIndex + 8, warnCountsStartIndex);
                    pw.print(strSafeCounts + ",");
                    //「読み飛ばし件数」の取得・表示
                    int skipCountsStartIndex = utf8Contents.indexOf("読み飛ばし件数=(");
                    int skipCountsEndIndex = utf8Contents.indexOf("),[StrImpl");
                    String strSkipCounts = utf8Contents.substring(skipCountsStartIndex + 9, skipCountsEndIndex);
                    pw.print(strSkipCounts + ",");

                    //処理時間の「時間」を取得
                    int hours = Integer.parseInt(strDiff.substring(0,2));
                    //処理時間の「分」を取得
                    int minutes = Integer.parseInt(strDiff.substring(3,5));
                    //処理時間の「秒」を取得
                    int secs = Integer.parseInt(strDiff.substring(6,8));
                    //処理時間の合計を計算(単位:秒)
                    int totalProcessTime = secs + (minutes + (hours * 60)) * 60;
                    // 割り算で1件あたりの処理時間を算出(合計処理時間 / 入力件数)少数第3位で四捨五入
                    BigDecimal processEfficiency = BigDecimal.valueOf(totalProcessTime).divide(BigDecimal.valueOf(Integer.parseInt(strInputCounts)), 3, BigDecimal.ROUND_HALF_UP);
                    pw.print(processEfficiency);
                    //csvを改行
                    pw.println();
                }
                //次の読み込み行へ
                contents = br.readLine();
            }
            br.close();
            pw.close();
        }catch(FileNotFoundException e){
          System.out.println(e);
        }catch(IOException e){
          System.out.println(e);
        }
    }
    public static Date parseStrToDate(String strDate){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        Date formattedDate = new Date();
        if(strDate == null) {
            strDate = null;
        } else {
            try {
                formattedDate = sdf.parse(strDate);
            } catch (ParseException e) {
                strDate = null;
            }
        }
        return formattedDate;
    }
}

run-aggregator.bat

LogAggregator を呼び出すバッチファイルです。ドラッグした複数のログファイルをこのバッチファイルにドロップすることで処理が始まります。

@echo off

echo ログの処理を開始しました
echo 日付,ジョブ名,開始時間,終了時間,処理時間,入力件数,正常件数,読み飛ばし件数,処理効率(時間/1件(秒)) > ./output.csv

for %%f in (%*) do (
  java LogAggregator %1
  echo %%~nxf の処理が終了しました
)

pause;

ツールの使い方

集計したいログファイルをドラッグし、run-aggregator.bat ファイルにドロップします。ドロップするとコマンドプロンプトが開き、処理経過が表示されます。ログの集計結果は、カレントディレクトリに output.csv として吐き出されます。

注意点として、ログファイルは実行ファイルと同じディレクトリに置く必要があります。

入力ファイル

処理仕様に合わせたログのテキストファイルであれば何でも読み込めます。以下のテキストはログのサンプルです。

[2016/01/23 [BATCH0157] [開始しました] 開始時間[00:00:22] ,[StrImpl Type]]
[2016/01/23 [BATCH0153] [開始しました] 開始時間[00:25:16] ,[StrImpl Type]]
[2016/01/23 [BATCH0157] [終了しました] 開始時間[00:00:22] 終了時間[01:01:22] 入力件数=(3120),正常件数=(3120),異常件数=(0),読み飛ばし件数=(0),[StrImpl Type]]
[2016/01/23 [BATCH0153] [終了しました] 開始時間[00:25:16] 終了時間[03:28:51] 入力件数=(72834),正常件数=(24),異常件数=(678),読み飛ばし件数=(1294),[StrImpl Type]]
[2016/01/23 [BATCH0019] [開始しました] 開始時間[08:25:16] ,[StrImpl Type]]
[2016/01/23 [BATCH0019] [終了しました] 開始時間[08:25:16] 終了時間[01:28:51] 入力件数=(1272834),正常件数=(72834),異常件数=(53),読み飛ばし件数=(622),[StrImpl Type]]

このような内容のログファイルを run-aggregator.bat に投入することで自動的にログが集計されます。以下が実際の処理の様子です。

それぞれ1万行あるログファイルを10個処理し、5万行のデータを1つの CSV ファイルとして新たに出力しています。

出力ファイル

ドロップしたログの集計が、カレントディレクトリに output.csv として出力されます。出力文字コードは Shift-JIS です。