カタカタブログ

SIerで働くITエンジニアがカタカタした記録を残す技術ブログ。Java, Oracle Database, Linuxが中心です。たまに数学やデータ分析なども。

PowerShell エラーの詳細をログファイルに出力する

はじめに

最近、PowerShellでスクリプトをたまに書くことがあるが、エラー発生時に内容をログに記録させたい場合がある。
エラー原因が分かるレベルの必要な情報を出力するために、以外と簡単にできなかったので、調べたことをまとめる。

検証環境のバージョンは以下の通り。

  • Windows 10
  • PowerShell 5.0 (詳細は以下)
> $PSVersionTable

Name             Value
----             -----
PSVersion           5.0.10586.122
PSCompatibleVersions     {1.0, 2.0, 3.0, 4.0...}
BuildVersion         10.0.10586.122
CLRVersion          4.0.30319.42000
WSManStackVersion       3.0
PSRemotingProtocolVersion   2.3
SerializationVersion     1.1.0.1

コンソールに表示されるエラーメッセージと同等の内容を取得する

基本は以下のように、エラーが発生する可能性のあるロジックをtry-catch構文で囲む。
$error配列に発生したエラーが先頭に追加されるため、$error[0]を参照することで
直前のtry文で発生したエラーオブジェクトが取得できる。
これをOut-Stringを経由させることで、コンソールに表示されるエラーと同じ内容のテキストが得られる。

try {
  1 / 0
} catch {
  $error[0] | Out-String | Out-File "error.log"
}

得られるerror.logファイルは以下の通り。

0 で除算しようとしました。
発生場所 C:\dev\script.ps1:14 文字:5
+  1 / 0
+  ~~~~~
  + CategoryInfo     : NotSpecified: (:) [], RuntimeException
  + FullyQualifiedErrorId : RuntimeException

コンソールに表示されるエラーメッセージと同等の内容がログファイルに出力される。

エラー内容の詳細を省略せずに取得する

単純なエラー内容であれば上で十分だが、PowerShellのコンソール出力されるログはCategoryInfoの情報が長すぎると切れてしまう場合がある。
そのため、例えばファイルパスが長すぎる場合のエラーログを出力すると、どのようなファイルなのかという情報が得られないことがある。

以下は260文字以上のパスのフォルダを作成しているためにエラーが発生しているコードの例。

try {
  # 260文字以上のパスのフォルダ作成
  $longDir = "C:\dev\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa  \aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa"
  New-Item -Path $longDir -ItemType Directory
} catch {
  $error[0] | Out-String | Out-File "error.log"
}

得られるerror.logファイルは以下の通り。

New-Item : 指定されたパス、ファイル名、またはその両方が長すぎます。完全限定型名は 260 文字未満で指定し、ディレクトリ名は 248 未満で指定してください。
発生場所 C:\dev\script.ps1 :3 文字:5
+  New-Item -Path $longDir -ItemType Directory
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  + CategoryInfo     : WriteError: (C:\dev\aaaaa\aa...aaa\aaaaa\aaaaa:String) [New-Item], PathTooLongException
  + FullyQualifiedErrorId : CreateDirectoryIOError,Microsoft.PowerShell.Commands.NewItemCommand

基本的なエラーメッセージなどは得られているが、CategoryInfoの情報はよく見るとC:\dev\aaaaa\aa...aaa\aaaaa\aaaaa:Stringとなっており、
エラー原因の一部が...と省略されてしまっていることが確認できる。

このことがエラーの原因究明に不都合な場合もあり、全文を表示させたい場合は以下のようにエラー出力ロジックを変更すると良い。
具体的には、Format-Listコマンドレットを用いてエラーオブジェクトの出力形式をリスト形式にする。

$error[0] | Format-List -force | Out-File "error.log"

この場合、得られるerror.logファイルは以下の通り。

Exception      : System.IO.PathTooLongException: 指定されたパス、ファイル名、またはその両方が長すぎます。完全限定型名は 260 文字未満で指定し、ディレクトリ名は 248 未満で指定してください。
             場所 System.IO.PathHelper.GetFullPathName()
             場所 System.IO.Path.NormalizePath(String path, Boolean fullCheck, Int32 maxPathLength, Boolean expandShortPaths)
             場所 System.IO.Path.GetFullPathInternal(String path)
             場所 System.IO.DirectoryInfo.Init(String path, Boolean checkHost)
             場所 Microsoft.PowerShell.Commands.FileSystemProvider.CreateDirectory(String path, Boolean streamOutput)
TargetObject     : C:\dev\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaa
            a\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa\aaaaa
CategoryInfo     : WriteError: (C:\dev\aaaaa\aa...aaa\aaaaa\aaaaa:String) [New-Item], PathTooLongException
FullyQualifiedErrorId : CreateDirectoryIOError,Microsoft.PowerShell.Commands.NewItemCommand
ErrorDetails     :
InvocationInfo    : System.Management.Automation.InvocationInfo
ScriptStackTrace   : <ScriptBlock>、C:\dev\script.ps1 : 行 3
PipelineIterationInfo : {}
PSMessageDetails   :

より詳細なエラー情報が得られ、先ほど省略されていた内容もTargetObjectという形で正しく表示されている。

まとめ

コンソールに出力されるエラー内容と同等のものをログに出力したい場合は

$error[0] | Out-String | Out-File "error.log"

を使う。
より詳細なエラーの情報を省略せずにログに出力したい場合は

$error[0] | Format-List -force | Out-File "error.log"

を使う。

以上