アプリケーションやバッチ処理のログを開き直しながら確認していると、異常の発見が遅れます。PowerShellではGet-Contentに-Waitを付けることで、ファイルへ追記された行を継続的に表示できます。
ただし、単純なGet-Content -Waitだけでは、監視開始前の行を表示するか、文字コードをどう扱うか、ログファイルが削除・再作成されたときにどう復帰するかが曖昧です。この記事では、ログの追従表示を安全に続けるためのPowerShellスクリプトを、最小例から再接続対応版まで段階的に作ります。
- 新しく追記される行だけを見るなら
-Tail 0 -Waitを使います。 - 直前の内容も確認するなら
-Tail 20 -Waitのように行数を指定します。 - 日本語の文字化けを防ぐため、ログに合った
-Encodingを明示します。 -Waitは約1秒間隔で更新を確認するため、厳密なイベント通知ではありません。- 削除・再作成やローテーションがあるログは、ファイルの世代変更を検知して監視を接続し直します。
- キーワード検知や通知は別記事の役割とし、この記事ではログを欠落させにくい追従処理に集中します。
特定の文字列を検知して通知したい場合はログの特定キーワードを監視してアラートを出す方法、プロセス自体を監視したい場合はプロセスを監視して自動再起動する方法を参照してください。
Get-Contentでログをリアルタイム監視する
新しく追記される行だけを表示する最小構成です。-LiteralPathを使うと、ファイル名に角括弧などのワイルドカード文字が含まれていても、そのままのパスとして扱えます。
$LogPath = "C:\Logs\application.log" Get-Content -LiteralPath $LogPath -Tail 0 -Wait -Encoding utf8
実行後に別のPowerShellからログへ行を追加すると、監視画面へ表示されます。監視を終了するにはCtrl+Cを押します。
$LogPath = "C:\Logs\application.log"
$message = "{0} INFO test message" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
Add-Content -LiteralPath $LogPath -Value $message -Encoding utf8
Tail 0とTail 20の違い
-Tailは、監視開始時にファイル末尾から何行読むかを指定します。目的に応じて値を使い分けます。
-Tail 0 -Wait:監視開始後に追記された行だけを表示する-Tail 20 -Wait:既存の末尾20行を表示してから、新しい行を追従する-Tail 100:末尾100行を表示して終了し、継続監視はしない
$LogPath = "C:\Logs\application.log" Get-Content -LiteralPath $LogPath -Tail 20 -Wait -Encoding utf8
障害調査では、エラー行の直前に原因となる警告や処理開始ログが出ていることがあります。そのため、手動調査では-Tail 20や-Tail 100、常駐監視では重複を避けるため-Tail 0が使いやすい構成です。
文字コードを指定して文字化けを防ぐ
PowerShellのバージョンとログ出力元によって既定の文字コードが異なります。日本語が文字化けする場合は、ログを作成しているアプリの設定を確認し、-Encodingを合わせます。
- UTF-8:
-Encoding utf8 - UTF-16 LE:
-Encoding unicode - Windows PowerShellで作られた既定形式:
-Encoding unicodeの場合がある - Shift_JIS系:Windows PowerShell 5.1では
-Encoding defaultを検討する
PowerShell 7では文字コードの扱いが変わっているため、「日本語ログだからこの値」と決めつけず、実ファイルをエディタや出力元の仕様で確認してください。
ファイルがまだ存在しない場合は待機する
アプリケーション起動後に初めてログが作成される場合、ファイルがない状態でGet-Contentを実行するとエラーになります。ファイルができるまで待ってから監視を開始します。
param(
[string]$LogPath = "C:\Logs\application.log",
[int]$RetrySeconds = 3,
[string]$Encoding = "utf8"
)
while (-not (Test-Path -LiteralPath $LogPath -PathType Leaf)) {
Write-Host "ログファイルの作成を待っています: $LogPath"
Start-Sleep -Seconds $RetrySeconds
}
Write-Host "ログ監視を開始します: $LogPath"
Get-Content -LiteralPath $LogPath -Tail 0 -Wait -Encoding $Encoding
削除・再作成されたログへ再接続する
Get-Content -Waitはファイルを待機し続けますが、監視中のファイルが削除されると処理が中断します。日次ログの切り替えやローテーションでファイルが削除・再作成される運用では、外側に再接続ループを設けます。
次のスクリプトは、監視処理をバックグラウンドジョブで動かし、元ファイルの削除、再作成、作成時刻の変化を確認します。世代が変わったら古いジョブを停止し、新しいファイルへ接続し直します。
param(
[string]$LogPath = "C:\Logs\application.log",
[string]$Encoding = "utf8",
[int]$CheckIntervalSeconds = 1,
[int]$ReconnectDelaySeconds = 2
)
$ErrorActionPreference = "Stop"
function Wait-LogFile {
while (-not (Test-Path -LiteralPath $LogPath -PathType Leaf)) {
Write-Host "$(Get-Date -Format s) WAIT file=$LogPath"
Start-Sleep -Seconds $ReconnectDelaySeconds
}
}
while ($true) {
$job = $null
try {
Wait-LogFile
$file = Get-Item -LiteralPath $LogPath
$generation = $file.CreationTimeUtc.Ticks
Write-Host "$(Get-Date -Format s) CONNECT file=$LogPath"
$job = Start-Job -ArgumentList $LogPath, $Encoding -ScriptBlock {
param($Path, $TextEncoding)
Get-Content -LiteralPath $Path `
-Tail 0 `
-Wait `
-Encoding $TextEncoding `
-ErrorAction Stop
}
while ($job.State -eq "Running") {
Receive-Job -Job $job
if (-not (Test-Path -LiteralPath $LogPath -PathType Leaf)) {
Write-Warning "ログファイルが削除されました。再作成を待ちます。"
break
}
$current = Get-Item -LiteralPath $LogPath
if ($current.CreationTimeUtc.Ticks -ne $generation) {
Write-Warning "ログファイルの世代変更を検知しました。再接続します。"
break
}
Start-Sleep -Seconds $CheckIntervalSeconds
}
Receive-Job -Job $job
if ($job.State -eq "Failed") {
$reason = $job.ChildJobs[0].JobStateInfo.Reason
Write-Warning "ログ監視が停止しました: $reason"
}
}
catch {
Write-Warning "監視処理でエラーが発生しました: $($_.Exception.Message)"
}
finally {
if ($job) {
Stop-Job -Job $job -ErrorAction SilentlyContinue
Remove-Job -Job $job -Force -ErrorAction SilentlyContinue
}
}
Start-Sleep -Seconds $ReconnectDelaySeconds
}
この例では再接続後に-Tail 0から監視するため、接続前から存在する古い行を重複表示しません。一方、ファイル作成から再接続までの短い間に書かれた行も確認したい場合は、再接続時だけ-Tail 20などへ変更します。その場合は一部の行が重複する可能性があります。
ローテーション方式はアプリごとに異なります。元ファイルをリネームして同じパスへ新規作成する方式、内容を空にする方式、日付入りの別ファイルへ切り替える方式があります。重要な監査ログで欠落や重複を許容できない場合は、PowerShellの簡易監視ではなく、ログ収集エージェントや監視製品を使用してください。
日付ごとにファイル名が変わるログを監視する
application-20260609.logのように日付でファイル名が変わる場合は、日付が変わるたびに対象パスを作り直します。
$LogDirectory = "C:\Logs"
$currentPath = $null
while ($true) {
$todayPath = Join-Path $LogDirectory (
"application-{0}.log" -f (Get-Date -Format "yyyyMMdd")
)
if ($todayPath -ne $currentPath) {
Write-Host "監視対象を切り替えます: $todayPath"
$currentPath = $todayPath
}
if (Test-Path -LiteralPath $currentPath -PathType Leaf) {
Get-Content -LiteralPath $currentPath -Tail 20 -Encoding utf8
}
else {
Write-Host "ログファイルの作成待ち: $currentPath"
}
Start-Sleep -Seconds 60
}
この例は1分ごとに末尾20行を表示する確認用です。そのままでは同じ行を繰り返し表示するため、厳密な常駐監視には使いません。日付切り替えの考え方を確認し、実運用では前述の再接続版へ対象パスの更新処理を組み込んでください。
複数のログを同時に表示する
複数ファイルを1本のパイプラインへ渡すと、どのファイルから出た行か分かりにくくなります。ログごとにジョブを分け、ファイル名を付けて出力します。
$LogPaths = @(
"C:\Logs\web.log",
"C:\Logs\batch.log"
)
$jobs = foreach ($path in $LogPaths) {
Start-Job -ArgumentList $path -ScriptBlock {
param($LogPath)
Get-Content -LiteralPath $LogPath -Tail 0 -Wait -Encoding utf8 |
ForEach-Object {
"[{0}] {1}" -f (Split-Path $LogPath -Leaf), $_
}
}
}
try {
while ($true) {
$jobs | Receive-Job
Start-Sleep -Seconds 1
}
}
finally {
$jobs | Stop-Job
$jobs | Remove-Job -Force
}
対象数が増えるほどジョブ管理、再接続、ログ欠落の扱いが複雑になります。数十ファイルや複数サーバーを監視する用途では、専用のログ基盤へ送る方が保守しやすくなります。
タスクスケジューラで常駐させる
監視を継続するには、PowerShell画面を手動で開いたままにせず、タスクスケジューラからps1を起動します。実行ユーザーには監視対象ログの読み取り権限が必要です。
プログラム: powershell.exe 引数: -NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\watch-log-with-reconnect.ps1" -LogPath "C:\Logs\application.log" -Encoding utf8 開始: C:\Scripts
- トリガーは「コンピューターの起動時」または「ログオン時」にする
- タスクが既に実行中の場合は、新しいインスタンスを開始しない
- ログを読むために必要なユーザー権限を設定する
- 長時間実行タスクを自動停止する設定が有効になっていないか確認する
- 画面へ出すだけでなく、監視スクリプト自身の状態ログも別ファイルへ残す
ps1を実行できない場合はExecutionPolicyやUnblock-Fileを確認する方法、タスクスケジューラ連携の考え方はSSHログ取得を定期実行する方法も参考になります。
監視処理の動作をテストする
本番ログを使う前に、一時ファイルで追記、削除、再作成を試します。監視用とテスト用のPowerShellを別々に開いて確認してください。
$LogPath = "C:\Logs\application.log"
New-Item -ItemType Directory -Path (Split-Path $LogPath) -Force | Out-Null
Set-Content -LiteralPath $LogPath -Value "initial line" -Encoding utf8
1..3 | ForEach-Object {
Start-Sleep -Seconds 2
Add-Content -LiteralPath $LogPath -Value "before rotation $_" -Encoding utf8
}
Start-Sleep -Seconds 2
Move-Item -LiteralPath $LogPath -Destination "$LogPath.1" -Force
Set-Content -LiteralPath $LogPath -Value "new generation" -Encoding utf8
1..3 | ForEach-Object {
Start-Sleep -Seconds 2
Add-Content -LiteralPath $LogPath -Value "after rotation $_" -Encoding utf8
}
確認するポイントは、追記した行が表示されること、ローテーションを検知すること、新しいapplication.logへ再接続すること、監視スクリプトが終了しないことです。
Get-Content -Waitが向かないケース
- 1行未満の断片が長時間書き込まれ、改行まで即時処理したい
- 大量のログを高速に集約し、検索や可視化を行いたい
- 欠落・重複を許容できない監査ログを収集したい
- 数十台以上のサーバーや多数のファイルを一元監視したい
- ログの世代管理、圧縮、転送、保持期間を自動化したい
このような用途では、Windows Event Log、OpenTelemetry Collector、Fluent Bit、Filebeatなど、要件に合った収集基盤を検討します。PowerShellは、端末上での確認や小規模な運用補助に向いています。
よくある問題
追記してもすぐ表示されない
-Waitはファイルを約1秒間隔で確認します。また、出力元アプリがバッファリングしている場合、アプリがファイルへ書き出すまでPowerShell側では検知できません。
日本語が文字化けする
-Encodingをログの実際の文字コードへ合わせます。PowerShellのバージョンによる既定値の違いにも注意してください。
ログローテーション後に止まる
単純なGet-Content -Waitではなく、削除やファイル世代の変更を確認して接続し直す構成にします。ローテーション方式に合わせた実機テストが必要です。
ERROR行だけ通知したい
この記事はログの追従と再接続を扱っています。フィルタリングや通知は特定キーワードを監視してアラートを出す方法へ分けることで、役割を明確にしています。
まとめ
PowerShellでログファイルをリアルタイム監視する基本は、Get-Content -Tail 0 -Waitです。調査時は-Tailで既存行を含め、文字化けする場合は-Encodingを明示します。
常駐運用では、ファイル未作成時の待機、削除・再作成やローテーション後の再接続、タスクスケジューラの実行条件まで設計してください。重要なログで欠落や重複を許容できない場合は、PowerShellだけで抱え込まず専用のログ収集基盤を利用することが大切です。

