こんにちは。DX推進開発部というチームでアプリケーションエンジニアをしている鈴木です。レガシーシステムのログひっ迫問題を、生成AIを利用して解決した話を書いてみようと思います。
はじめに
私は社内向けの内製システムの開発・保守運用チームでアプリケーションエンジニアをしています。担当するシステムは多岐にわたるのですが、中にはレガシーと呼ばれる古いシステムも存在します。小規模なシステムなのですが、古い分潜在的な課題が残っていることがあります。
ある日、担当するシステムの一つ(Windowsバッチアプリケーション)で、サーバーの容量がひっ迫しているという問題が発生しました。
この問題に生成AIを利用して課題解決をしていったことを書いていきます。
ログ管理の問題
サーバーの容量ひっ迫について調査した結果、原因は膨大なログファイルデータによる容量不足であることが判明しました。
さらに調査を続けたところ、ログファイルに対して、いわゆるログローテーションと呼ばれる有効な設定(アーカイブや削除等)が入っておりませんでした。
生成AIに最適解を聞いてみる
ログローテーションに対して正しい知見を持っていなかったため、生成AIに相談してみます。
以下はプロンプトです。今回はGemini 1.5 Flashを利用しました。
命令書:あなたは、優秀なシステムエンジニアです。以下の相談事項をもとに適切なアドバイスをしてください。相談事項:Windows環境でバッチのログ管理を検討しています。ログローテーションを導入するにあたって最適な方法を複数教えてください。
以下は回答です。
ログローテーションの最適解についてのアドバイスWindowsバッチアプリケーションでのログローテーションは、アプリケーションの安定性、メンテナンス性、そして可用性を考慮することが重要です。以下にいくつかの方法とツールについて解説します。1. ローカルでのログローテーションローカルスクリプトでのログローテーションWindowsバッチファイルを使ってログローテーションを実現するシンプルな方法です。(省略)2. クラウドベースの解決策ログデータをクラウドベースで管理することで、柔軟性やスケーラビリティが向上します。例えば、AWS、Azure、Google Cloud などのクラウドプロバイダーを利用できます。(省略)3. 専用ツールの利用- Splunk:Splunk はログデータの収集、分析、監視を行う強力なツールです。以下に簡単な設定手順を紹介します。- Logstash と ElasticSearch を含む ELK Stack :これにより、ログの収集、解析、および可視化が可能です。- Graylog :集中ログ管理を行うオープンソースツールで、ログデータの収集、インデックス作成、リアルタイム検索が可能です。
スクリプトを組むか、クラウドサービスを利用するか、サードパーティのツールを利用する手段を提示してくれました。念のためGoogleで検索をしましたが概ね同じ内容が得られました。
しかしクラウドサービスやサードパーティツールは利用料がかかります。移行費用も含め、レガシーシステムにそこまでのコストを払うのは現実的ではありません。そのためスクリプトを組むという方針で対応することにします。
コーディング
コーディングも例によってAIにお願いします。今回はD:\APPLOGs\Logsというフォルダに「GMO01.YYYYMMDD.log」という名称で溜まっていくシンプルなログを想定してスクリプトを組んでもらいます。
あなたは、PowerShellに精通した優秀なエンジニアです。以下の条件をもとに、PowerShellのスクリプトをコーディングしてください。# 条件・ログファイルのパス:D:\APPLOGs\Logs・圧縮ファイルの格納パス:D:\APPLOGs\Logs\Archive・ログファイルのファイル名:GMO01.YYYYMMDD.log(例:GMO01.20231231.log)・1週間前までのログファイルを圧縮する・2年前までの圧縮ファイルは削除する
スクリプトの生成は1回で期待する成果は返ってこないので、複数回のラリーとトライ&エラーをしています。最終的に以下のようなコードとなりました。
# パス設定
$LogFilePath = "D:\APPLOGs\Logs"
$ArchivePath = "D:\APPLOGs\Logs\Archive"
$LogFileNamePrefix = "GMO01"
$CompressionFormat = "Zip"
$DaysToCompress = 7
$YearsToDelete = 2
$Now = Get-Date
$CompressDate = $Now.AddDays(-$DaysToCompress)
$DeleteDate = $Now.AddYears(-$YearsToDelete)
$logOutputPath = "D:\APPLOGs\script_output.log"
# ログ出力用関数
function WriteLog {
param(
[string]$message
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "$timestamp - $message"
$logEntry | Out-File -FilePath $logOutputPath -Append -Encoding UTF8
Write-Host $logEntry
}
WriteLog "Starting the script..."
# ログファイルを取得
Try {
WriteLog "Starting to retrieve log files..."
$LogFiles = Get-ChildItem -Path $LogFilePath -Filter "$LogFileNamePrefix*.log"
WriteLog ("Retrieved log file list: {0} files" -f $LogFiles.Count)
if ($LogFiles.Count -eq 0) {
WriteLog "No target log files found."
}
} Catch {
WriteLog "Error occurred while retrieving log files: $_"
Exit
}
# ログファイルを圧縮し、元のファイルを削除
foreach ($LogFile in $LogFiles) {
Try {
if ($LogFile.LastWriteTime -le $CompressDate) {
$DestinationFile = Join-Path -Path $ArchivePath -ChildPath ($LogFile.BaseName + ".zip")
Compress-Archive -Path $LogFile.FullName -DestinationPath $DestinationFile -CompressionLevel Optimal -Force
WriteLog ("Compressed log file and moved to Archive folder: {0} -> {1}" -f $LogFile.FullName, $DestinationFile)
# 元のログファイルを削除
Remove-Item -Path $LogFile.FullName -Force
WriteLog ("Deleted original log file: {0}" -f $LogFile.FullName)
}
} Catch {
WriteLog "Error occurred during log file compression or deletion: $_"
}
}
# 指定日付より古い圧縮ファイルを削除
Try {
WriteLog "Starting to retrieve compressed files..."
$CompressedFiles = Get-ChildItem -Path $ArchivePath -Filter "$LogFileNamePrefix*.zip"
WriteLog ("Retrieved compressed file list: {0} files" -f $CompressedFiles.Count)
if ($CompressedFiles.Count -eq 0) {
WriteLog "No target compressed files found for deletion."
} Else {
foreach ($CompressedFile in $CompressedFiles) {
Try {
# ファイル名から日付を抽出(例:GMO01_yyyyMMdd.zip)
$fileName = $CompressedFile.BaseName
if ($fileName -match "$LogFileNamePrefix`.(\d{8})") {
$dateString = $matches[1] # yyyyMMdd 形式の文字列
$fileDate = [datetime]::ParseExact($dateString, "yyyyMMdd", $null)
WriteLog ("Date of compressed file: {0}, check date: {1}" -f $fileDate, $DeleteDate)
if ($fileDate -le $DeleteDate) {
Remove-Item -Path $CompressedFile.FullName -Force
WriteLog ("Deleted compressed file: {0}" -f $CompressedFile.FullName)
} Else {
WriteLog ("Compressed file is not target for deletion: {0}" -f $CompressedFile.FullName)
}
} Else {
WriteLog ("Compressed file name format is incorrect: {0}" -f $CompressedFile.FullName)
}
} Catch {
WriteLog "Error occurred during compressed file deletion: $_"
}
}
}
} Catch {
WriteLog "Error occurred while retrieving compressed files: $_"
}
WriteLog "Script execution completed."
こちらのスクリプトをタスクスケジューラにタスク設定します。週1の起動設定をすれば、毎週圧縮&削除を実施してくれるようになります。
スクリプトの解説
# ログ出力用関数
function WriteLog {
param(
[string]$message
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "$timestamp - $message"
$logEntry | Out-File -FilePath $logOutputPath -Append -Encoding UTF8
Write-Host $logEntry
}
このスクリプトを実施した際のログ出力用の関数$logOutputPath = "D:\APPLOGs\script_output.log"に出力します。
# ログファイルを圧縮し、元のファイルを削除
foreach ($LogFile in $LogFiles) {
Try {
if ($LogFile.LastWriteTime -le $CompressDate) {
$DestinationFile = Join-Path -Path $ArchivePath -ChildPath ($LogFile.BaseName + ".zip")
Compress-Archive -Path $LogFile.FullName -DestinationPath $DestinationFile -CompressionLevel Optimal -Force
WriteLog ("Compressed log file and moved to Archive folder: {0} -> {1}" -f $LogFile.FullName, $DestinationFile)
# 元のログファイルを削除
Remove-Item -Path $LogFile.FullName -Force
WriteLog ("Deleted original log file: {0}" -f $LogFile.FullName)
}
} Catch {
WriteLog "Error occurred during log file compression or deletion: $_"
}
}
ログファイルの最終更新日時が$CompressDate以前かどうかを判定。このサンプルでは$CompressDateは7日前です。$CompressDate以前であれば、ログファイルを ZIP 形式で圧縮し、指定されたフォルダに移動します。圧縮が成功したら、元のログファイルを削除します。
# 指定日付より古い圧縮ファイルを削除
Try {
WriteLog "Starting to retrieve compressed files..."
$CompressedFiles = Get-ChildItem -Path $ArchivePath -Filter "$LogFileNamePrefix*.zip"
WriteLog ("Retrieved compressed file list: {0} files" -f $CompressedFiles.Count)
if ($CompressedFiles.Count -eq 0) {
WriteLog "No target compressed files found for deletion."
} Else {
foreach ($CompressedFile in $CompressedFiles) {
Try {
# ファイル名から日付を抽出(例:GMO01_yyyyMMdd.zip)
$fileName = $CompressedFile.BaseName
if ($fileName -match "$LogFileNamePrefix`.(\d{8})") {
$dateString = $matches[1] # yyyyMMdd 形式の文字列
$fileDate = [datetime]::ParseExact($dateString, "yyyyMMdd", $null)
WriteLog ("Date of compressed file: {0}, check date: {1}" -f $fileDate, $DeleteDate)
if ($fileDate -le $DeleteDate) {
Remove-Item -Path $CompressedFile.FullName -Force
WriteLog ("Deleted compressed file: {0}" -f $CompressedFile.FullName)
} Else {
WriteLog ("Compressed file is not target for deletion: {0}" -f $CompressedFile.FullName)
}
} Else {
WriteLog ("Compressed file name format is incorrect: {0}" -f $CompressedFile.FullName)
}
} Catch {
WriteLog "Error occurred during compressed file deletion: $_"
}
}
}
} Catch {
WriteLog "Error occurred while retrieving compressed files: $_"
}
アーカイブファイルの削除を実施します。削除条件はファイル名に含まれる日付が$DeleteDateよりも前かどうかで判定します。このサンプルでは$DeleteDateは2年前です。
おわりに
今回、生成AIの力を借りて課題解決をした経験を書いてみました。対応方針のヒントをもらい、開発コストを抑えることができました。
このように最近では生成AIを利用して効率的に課題解決を目指す機会が増えています。開発の時間を極力減らし、より生産的な作業に集中するということが重要になってきていると思います。
最後に、散々言われていることですが、生成AIも万能ではないです。出力内容が正確ではないこともあるため注意が必要です。コーディングに関しての所感ですが、エラー処理、セキュリティ対策などはきちんと条件に含めないと正しく入れてくれないことが多いと感じています。アイデア出しや基本的なコード作成は非常に強力ですが、最終的には自身でのチェックが必須になってきます。