1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事は以下のシリーズの12番目の記事です。

この記事でやること

PowerShellをタスクスケジューラで実行すると、処理は画面の見えないところで動きます。

そのため、手動実行のように「画面を見て成功・失敗を判断する」ことができません。

そこで重要になるのがログ設計です。

この記事では、タスクスケジューラでPowerShellを実行するときに、最低限どのようなログを残すべきかを整理します。

扱う内容は次の通りです。

項目 内容
開始ログ タスクが起動したことを記録する
終了ログ 最後まで処理されたことを記録する
成功ログ 正常終了したことを記録する
エラーログ 失敗理由を記録する
実行ユーザーログ どのユーザーで動いたか記録する
処理件数ログ 何件処理したか記録する
終了コード 外部から成功・失敗を判断しやすくする

なぜタスクスケジューラではログが重要なのか

手動実行なら、PowerShellの画面にエラーが表示されます。

しかし、タスクスケジューラで実行すると、画面を見られないことが多いです。
特に夜間実行や無人実行では、失敗していても気づかないことがあります。

たとえば、次のような問題が起きます。

問題 ログがない場合
タスクが起動していない そもそも実行されたか分からない
ファイルが見つからない どのパスで失敗したか分からない
権限がない どのユーザーで動いたか分からない
CSV処理が途中で止まった 何行目まで処理したか分からない
共有フォルダにアクセスできない ネットワークか権限か判断しにくい

タスクスケジューラで動かすPowerShellでは、ログがないと運用できません。

検証用フォルダを作る

まず、検証用フォルダを作成します。

New-Item -ItemType Directory -Path "C:\work\ps-task-log-test" -Force
Set-Location "C:\work\ps-task-log-test"

ログ用フォルダも作ります。

New-Item -ItemType Directory -Path ".\logs" -Force

確認します。

Get-ChildItem

最低限残したいログ項目

タスクスケジューラ実行では、最低限次のログを残すと切り分けがしやすくなります。

ログ項目 目的
処理開始 タスク開始 タスクが起動したか確認する
処理終了 タスク終了 最後まで到達したか確認する
実行ユーザー DOMAIN\user01 権限問題を確認する
スクリプト場所 C:\work\script パス問題を確認する
カレントディレクトリ C:\Windows\System32 など 相対パス問題を確認する
対象ファイル数 10件 処理対象を確認する
成功件数 9件 処理結果を確認する
失敗件数 1件 異常有無を確認する
エラー内容 Access denied 原因調査に使う

特に重要なのは、開始ログと終了ログの両方を残すことです。

開始ログだけあって終了ログがない場合、途中で止まった可能性があります。

ログ出力用の関数を作る

まず、ログ出力用の関数を作ります。

$logDir = Join-Path $PSScriptRoot "logs"
New-Item -ItemType Directory -Path $logDir -Force | Out-Null

$today = Get-Date -Format "yyyyMMdd"
$logPath = Join-Path $logDir "task_$today.log"

function Write-Log {
    param(
        [string]$Message,
        [string]$Level = "INFO"
    )

    $now = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    Add-Content -Path $logPath -Value "[$now][$Level] $Message" -Encoding UTF8
}

使い方は次の通りです。

Write-Log "タスク開始"
Write-Log "処理成功"
Write-Log "エラーが発生しました" "ERROR"

ログには、時刻とレベルを付けておくと見やすくなります。

[2026-07-05 22:00:00][INFO] タスク開始
[2026-07-05 22:00:01][INFO] 処理成功
[2026-07-05 22:00:02][ERROR] エラーが発生しました

実行ユーザーと実行場所を記録する

タスクスケジューラでは、手動実行時と違うユーザー・違う場所で実行されることがあります。

そのため、ログに実行ユーザーと実行場所を残します。

Write-Log "実行ユーザー: $env:USERDOMAIN\$env:USERNAME"
Write-Log "PSScriptRoot: $PSScriptRoot"
Write-Log "CurrentDirectory: $(Get-Location)"

特に CurrentDirectory は重要です。

タスクスケジューラでは、開始場所の設定が空欄だった場合などに、想定外の場所を基準に実行されることがあります。

そのため、スクリプト内のパス指定は、できるだけ $PSScriptRoot を基準にします。

$targetDir = Join-Path $PSScriptRoot "target"

try/catch/finallyで終了ログを必ず残す

タスクスケジューラ実行では、エラーが発生したときもログに残す必要があります。

基本形は、try/catch/finally です。

Write-Log "タスク開始"

try {
    Write-Log "処理本体を開始"

    # ここに実際の処理を書く

    Write-Log "処理本体が正常終了"
}
catch {
    Write-Log "エラー発生: $($_.Exception.Message)" "ERROR"
}
finally {
    Write-Log "タスク終了"
}

finally に書いた処理は、成功しても失敗しても実行されます。

そのため、終了ログは finally に書いておくとよいです。

エラーをcatchに入れるために -ErrorAction Stop を使う

PowerShellでは、エラーが発生しても必ず catch に入るとは限りません。

たとえば、Copy-Item などで確実に catch に入れたい場合は、-ErrorAction Stop を付けます。

try {
    Copy-Item -Path ".\not_found.txt" -Destination ".\backup\not_found.txt" -ErrorAction Stop
    Write-Log "コピー成功"
}
catch {
    Write-Log "コピー失敗: $($_.Exception.Message)" "ERROR"
}

-ErrorAction Stop を付けることで、エラーを例外として扱いやすくなります。

タスクスケジューラで安定運用したい処理では、重要なコマンドに -ErrorAction Stop を付けることを検討します。

処理件数をログに残す

一括処理では、何件処理したかをログに残すと便利です。

まず、検証用ファイルを作ります。

$targetDir = Join-Path $PSScriptRoot "target"
New-Item -ItemType Directory -Path $targetDir -Force | Out-Null

Set-Content -Path (Join-Path $targetDir "a.txt") -Value "a"
Set-Content -Path (Join-Path $targetDir "b.txt") -Value "b"
Set-Content -Path (Join-Path $targetDir "c.log") -Value "c"

.txt ファイルだけを対象にして、件数をログに残します。

$files = Get-ChildItem -Path $targetDir -File -Filter "*.txt" -ErrorAction Stop

Write-Log "対象ファイル数: $($files.Count)"

foreach ($file in $files) {
    Write-Log "処理対象: $($file.FullName)"
}

処理対象と件数をログに出しておくと、「そもそも対象がなかった」のか、「処理途中で失敗した」のかが分かりやすくなります。

成功件数・失敗件数を記録する

一括処理では、成功件数と失敗件数を分けて記録すると便利です。

$successCount = 0
$errorCount = 0

$files = Get-ChildItem -Path $targetDir -File -Filter "*.txt" -ErrorAction Stop

foreach ($file in $files) {
    try {
        Write-Log "処理開始: $($file.FullName)"

        # ここにファイルごとの処理を書く
        # 例として、ファイル名だけログに出す
        Write-Log "処理成功: $($file.Name)"

        $successCount++
    }
    catch {
        Write-Log "処理失敗: $($file.FullName) / $($_.Exception.Message)" "ERROR"
        $errorCount++
    }
}

Write-Log "成功件数: $successCount"
Write-Log "失敗件数: $errorCount"

このようにしておくと、処理全体の結果を後から確認しやすくなります。

終了コードを設定する

タスクスケジューラでは、スクリプトの終了コードを見ることで成功・失敗を判断しやすくなります。

PowerShellでは、最後に exit を使って終了コードを返せます。

一般的には、次のように考えます。

終了コード 意味
0 正常終了
1 異常終了

たとえば、失敗件数が1件以上ある場合は exit 1 にします。

if ($errorCount -gt 0) {
    Write-Log "異常終了: 失敗件数 $errorCount" "ERROR"
    exit 1
}
else {
    Write-Log "正常終了"
    exit 0
}

タスクスケジューラの履歴や実行結果と組み合わせると、異常終了を検知しやすくなります。

実務で使いやすいログ設計テンプレート

ここまでの内容をまとめたテンプレートです。

$logDir = Join-Path $PSScriptRoot "logs"
$targetDir = Join-Path $PSScriptRoot "target"

New-Item -ItemType Directory -Path $logDir -Force | Out-Null
New-Item -ItemType Directory -Path $targetDir -Force | Out-Null

$today = Get-Date -Format "yyyyMMdd"
$logPath = Join-Path $logDir "task_$today.log"

function Write-Log {
    param(
        [string]$Message,
        [string]$Level = "INFO"
    )

    $now = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    Add-Content -Path $logPath -Value "[$now][$Level] $Message" -Encoding UTF8
}

$successCount = 0
$errorCount = 0

Write-Log "タスク開始"
Write-Log "実行ユーザー: $env:USERDOMAIN\$env:USERNAME"
Write-Log "PSScriptRoot: $PSScriptRoot"
Write-Log "CurrentDirectory: $(Get-Location)"

try {
    $files = Get-ChildItem -Path $targetDir -File -Filter "*.txt" -ErrorAction Stop

    Write-Log "対象ファイル数: $($files.Count)"

    foreach ($file in $files) {
        try {
            Write-Log "処理開始: $($file.FullName)"

            # ここに実際の処理を書く
            # 例: ファイル名をログに出す
            Write-Log "処理成功: $($file.Name)"

            $successCount++
        }
        catch {
            Write-Log "処理失敗: $($file.FullName) / $($_.Exception.Message)" "ERROR"
            $errorCount++
        }
    }

    Write-Log "成功件数: $successCount"
    Write-Log "失敗件数: $errorCount"
}
catch {
    Write-Log "全体処理エラー: $($_.Exception.Message)" "ERROR"
    $errorCount++
}
finally {
    Write-Log "タスク終了"
}

if ($errorCount -gt 0) {
    Write-Log "異常終了: 失敗件数 $errorCount" "ERROR"
    exit 1
}
else {
    Write-Log "正常終了"
    exit 0
}

このテンプレートでは、次の情報を残します。

ログ項目 内容
タスク開始 タスクが起動したか確認
実行ユーザー 権限問題の切り分け
PSScriptRoot スクリプト基準パスの確認
CurrentDirectory カレントディレクトリの確認
対象ファイル数 処理対象の確認
処理開始 ファイルごとの開始確認
処理成功 ファイルごとの成功確認
処理失敗 ファイルごとの失敗確認
成功件数 全体結果の確認
失敗件数 異常有無の確認
タスク終了 最後まで到達したか確認
終了コード 外部から成功・失敗を判断

ログファイルを日付で分ける

上記テンプレートでは、ログファイル名を日付単位にしています。

$today = Get-Date -Format "yyyyMMdd"
$logPath = Join-Path $logDir "task_$today.log"

これにより、次のようなログになります。

logs
├─ task_20260705.log
├─ task_20260706.log
└─ task_20260707.log

日次実行のタスクでは、この形式が管理しやすいです。

1回の実行ごとにログを分けたい場合は、秒まで含めます。

$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$logPath = Join-Path $logDir "task_$timestamp.log"

ログが増えすぎる場合

タスクを毎日実行すると、ログファイルが増えていきます。

古いログを削除したい場合は、たとえば30日より古いログを削除します。

Get-ChildItem -Path $logDir -File -Filter "*.log" |
    Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
    Remove-Item -WhatIf

最初は必ず -WhatIf を付けて、削除対象を確認します。

問題なければ、-WhatIf を外します。

Get-ChildItem -Path $logDir -File -Filter "*.log" |
    Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
    Remove-Item

ログ設計では、「残す」だけでなく「いつまで残すか」も考える必要があります。

よくあるつまずき

ログが出ない

まず、ログ出力先のパスを確認します。

Write-Log "PSScriptRoot: $PSScriptRoot"
Write-Log "CurrentDirectory: $(Get-Location)"

また、ログフォルダを作っているか確認します。

New-Item -ItemType Directory -Path $logDir -Force | Out-Null

エラーがcatchに入らない

重要なコマンドには -ErrorAction Stop を付けます。

Get-ChildItem -Path $targetDir -File -ErrorAction Stop

タスクは成功扱いなのに処理が失敗している

スクリプト内でエラーをログに出していても、最後に exit 0 になっていると正常終了扱いになります。

失敗件数がある場合は、exit 1 にします。

if ($errorCount -gt 0) {
    exit 1
}
else {
    exit 0
}

ログが文字化けする

日本語ログを扱う場合は、-Encoding を指定します。

Add-Content -Path $logPath -Value "処理開始" -Encoding UTF8

環境によっては、PowerShell 7で utf8BOM を使うこともあります。

Add-Content -Path $logPath -Value "処理開始" -Encoding utf8BOM

ログが長くなりすぎる

日付別ログにするか、古いログを削除する処理を入れます。

$today = Get-Date -Format "yyyyMMdd"
$logPath = Join-Path $logDir "task_$today.log"

まとめ

タスクスケジューラでPowerShellを実行する場合、ログ設計は必須です。

最低限残したいログは次の通りです。

ログ 目的
タスク開始 起動確認
タスク終了 最後まで到達したか確認
実行ユーザー 権限問題の切り分け
PSScriptRoot スクリプト基準パスの確認
CurrentDirectory 相対パス問題の確認
対象件数 処理対象の確認
成功件数 正常処理数の確認
失敗件数 異常有無の確認
エラー内容 原因調査
終了コード 外部から成功・失敗を判断

特に重要なのは、次の3つです。

Write-Log "タスク開始"
Write-Log "エラー発生: $($_.Exception.Message)" "ERROR"
Write-Log "タスク終了"

そして、失敗時には終了コードを返します。

exit 1

成功時は次のようにします。

exit 0

タスクスケジューラで動かすPowerShellは、画面で見えない場所で動きます。
だからこそ、ログに「いつ、誰が、どこで、何を、何件処理し、成功したのか失敗したのか」を残すことが重要です。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?