くまくまの業務日誌

Markdown記法で徒然に書いてみましょう。

ログからキーワードを抽出して、ソートして、同じものを切り捨てる 参

圧倒的な高速処理でPythonが決着を付けてしまいました。

早速PythonPowerShellとほぼ同じロジックを組んでみました。出力結果はどちらも同じになります。それはWinMergeを使って確認しました。スクリプトもほぼ同じにしました。

しかし、Pythonの方が圧倒的に早い!時間を計るロジックを入れる気も起きません。
と、同時に感じたのは、どうもPowerShellは、画面出力が異常に遅い。

なので、途中経過を出力させるのをやめて、プログレスバーの出力で途中経過を出力するようにしました。そうすると、もうPowerShellダメだ…と思うほどの処理速度差になってしまいました。 しかも、Pythonで使っているプログレスバーの出力に使ったtqdmは、本当にここに仕込んでいいのかと悩んでしまうところに実装しています。実装に3分程度でした。対して、PowerShellのWrite-Progressは、使い方分かっていたので、偉く実装に手間取りました。こんなところもPythonおそるべし(己の不手際は置いといて…)。

ただ、Python正規表現を扱う際、事前のコンパイルが書きやすかったのでさくっと使っていますが、PowerShellはまだ使い方が分からなかったので使っていません。しかし、そこだけの差ではないと思えています。それと置換後の文字列出力の際、各項目を変数に入れてから使っているところが冗長でした。しかし、これはグループ変数の取り出しが、'\1'とかで取り出せない仕組みの方が悪いような。

さっき調べたら、以下の処理でループ外には出せそうです。でもきっと焼石に水ではなかろうか。

$expression = [regex]"(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}),(\d{3}) (\d{3,5}) (\w+)(\s+-\s+)"
$expression.Replace($row, { 出力用のLambda式 })

もし、読まれている方でロジックやコードに「ここはどうなの?」という方の御意見お待ちしております。

すっかり、読み込むログの形を提示しておりませんでしたが、以下のような感じです。
たしか、カテゴリ部分以降が、%6p - %m%nではなかったかな。

2020-07-10 01:57:28,577 31128 INFO   - ここになんらかのlog4cppで出力したメッセージ

Pythonスクリプト

import re
from tqdm import tqdm

targetLog = r"..\LogParser\hogehoge.log"
outputLog = r".\output.txt"

contents = list()

fout = open(outputLog, "w", encoding="shift_jis")

with open(targetLog, "r", encoding="shift_jis") as f:
    contents = f.readlines()

outputString = "{0}\t{1}\t{2}\t{3}\t{4}\t{5}\n".format(
    "Date", "Time", "ms", "PID", "Category", "Message")
fout.writelines(outputString)

expression = re.compile(r"(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}),(\d{3}) (\d{3,5}) (\w+)(\s+-\s+)")

for row in tqdm(contents):
    if (len(row) == 0):
        continue
    elif (row[0:4] != "2020"):
        continue

    row = row.replace("\t", " ")

    row = expression.sub(r"\1\t\2\t\3\t\4\t\5\t", row)

    #print(row)
    fout.writelines(row)

fout.close()

PowerShellスクリプト

$targetLog = ".\hogehoge.log"
$outputLog = ".\hogehoge.txt"

$contents = Get-Content -Path $targetLog -Encoding OEM

$outputString = "{0}`t{1}`t{2}`t{3}`t{4}`t{5}" -F "Date", "Time", "ms", "PID", "Category", "Message"
$outputString | Out-File $outputLog -Encoding OEM

$count = 0
foreach ($row in $contents) {
    $count += 1

    if ($row.Length -eq 0) {
        continue
    }
    elseif ($row.Substring(0, 4) -ne "2020") {
        continue
    }

    $row = $row.Replace("`t", " ")

    $row = [regex]::Replace($row, "(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}),(\d{3}) (\d{3,5}) (\w+)(\s+-\s+)",
        {
            $date = $args.groups[1].value
            $time = $args.groups[2].value
            $millisec = $args.groups[3].value
            $tid = $args.groups[4].value
            $category = $args.groups[5].value
            $statement = $args.groups[7].value

            "{0}`t{1}`t{2}`t{3}`t{4}`t{5}" -F $date, $time, $millisec, $tid.PadLeft(5, " "), $category, $statement
        })
    
    $progress = "{0}" -F [Math]::round(($count / $contents.Length * 100), [MidpointRounding]::AwayFromZero)
    $running = "processed:{0:#,#}/{1:#,#}" -F $count, $contents.Length
    Write-Progress -Activity "処理中..." -CurrentOperation ($progress + "%") -Status $running -PercentComplete $progress
    
    #Write-Output $row
    $row | Out-File -FilePath $outputLog -Append -Encoding OEM
}