Powershell用のロギングクラス

log4jを意識したインターフェースで作成しています。
使用方法などは実装コードのコメント、プロパティ、単体テストコードを参照いただけると幸いです。

# 実装
````Logger.ps1
<#
.SYNOPSIS
指定されたフォーマットで指定されたディレクトリにログを書き込むためのLoggerクラス。

.DESCRIPTION
Loggerクラスは、各種レベル（デバッグ、情報、警告、エラー）でログを書き込むメソッドを提供します。ログは、logDirフィールドで指定されたディレクトリに、logLayoutフィールドで指定されたフォーマットで書き込まれます。

.PARAMETER encoding
ログファイルの文字エンコーディング。デフォルトは "Default"。

.PARAMETER logLayout
ログメッセージのレイアウト。デフォルトは "%d %s %p %m"。

.PARAMETER datetimeLayout
日時のフォーマット。デフォルトは "yyyyMMddTHHmmssZ"。

.METHOD debug
デバッグレベルのログを書き込みます。

.METHOD info
情報レベルのログを書き込みます。

.METHOD warn
警告レベルのログを書き込みます。

.METHOD error
エラーレベルのログを書き込みます。

.EXAMPLE
$logger = New-Object Logger($MyInvocation)
$logger.info("これは情報レベルのログメッセージです。")
$logger.error("これはエラーレベルのログメッセージです。")
#>

class Logger {
    # Properties(Required)
    [string] $encoding = "Default"
    [string] $logLayout = "%d %s %p %m"
    [string] $datetimeLayout = "yyyyMMddTHHmmssZ"

    # Properties(Auto-Configured)
    [string] $scriptName
    [string] $logCreatedDatetime
    [string] $logDir
    [string] $logFilename

    Logger ($InvokingScriptMyInvocation) {
        $this.scriptName = (Split-Path -Leaf $InvokingScriptMyInvocation.MyCommand.Name).replace(".ps1", "")
        $this.logCreatedDatetime = Get-Date -Format yyyyMMdd
        $this.logDir = (Split-Path -Parent $InvokingScriptMyInvocation.MyCommand.Path)
        $this.logFilename = (Split-Path -Leaf $InvokingScriptMyInvocation.MyCommand.Name).replace(".ps1", "-" + $this.logCreatedDatetime + ".log")
    }

    [void] debug($msg) {
        $this.writeLog($msg, "DEBUG")
    }

    [void] info($msg) {
        $this.writeLog($msg, "INFO")
    }

    [void] warn($msg) {
        $this.writeLog($msg, "WARN")
    }

    [void] error($msg) {
        $this.writeLog($msg, "ERROR")
    }

    [void] writeLog($msg, $logLevel) {

        $replacedLayout = $this.logLayout
        $replacedLayout = $replacedLayout.replace("%d", (Get-Date -Format $this.datetimeLayout))
        $replacedLayout = $replacedLayout.replace("%p", $logLevel)
        $replacedLayout = $replacedLayout.replace("%s", $this.scriptName)
        $replacedLayout = $replacedLayout.replace("%m", $msg)

        Write-Output $replacedLayout | Out-File -FilePath (Join-Path $this.logDir $this.logFilename) -Append -Encoding $this.encoding
    }
}
````

# 使用方法と簡単な単体テスト

````Logger.Tests.ps1
# Needed "Install-Module -Name Pester -Force -SkipPublisherCheck"
# Load Logger script ( need to store the Logger.ps1 in the same directory )
. ($PSCommandPath.replace(".Tests", ""))

# $MyInvocation of this script cannot be obtained in the It section.
$testScriptMyInvocation = $MyInvocation

Describe "LoggerUnitTest" {
    
    It "Regular-TestCase" {

        $Logger = New-Object Logger($testScriptMyInvocation)
        $msg = "test-message"

        $Logger.debug($msg)
        Get-Content (Join-Path $Logger.logDir $Logger.logFilename) -Tail 1 | Should -Match ".*DEBUG.*$msg.*" 

        $Logger.info($msg)
        Get-Content (Join-Path $Logger.logDir $Logger.logFilename) -Tail 1 | Should -Match ".*INFO.*$msg.*" 

        $Logger.warn($msg)
        Get-Content (Join-Path $Logger.logDir $Logger.logFilename) -Tail 1 | Should -Match ".*WARN.*$msg.*" 

        $Logger.error($msg)
        Get-Content (Join-Path $Logger.logDir $Logger.logFilename) -Tail 1 | Should -Match ".*ERROR.*$msg.*" 

    }

    It "Exception-TestCase" {

        $Logger = New-Object Logger($testScriptMyInvocation)
        # Specific Non-Existing Directory
        $Logger.logDir = (Join-Path $Logger.logDir "DummyDummyDir")
        
        { $Logger.debug($msg) } | Should -Throw -ExceptionType System.IO.DirectoryNotFoundException

    }

}
````

```` powershell: 同一ディレクトリに実装とテストスクリプトを格納し実行
PS c:\logger> (Invoke-Pester ./Logger.Tests.ps1 -CodeCoverage ./Logger.ps1 -PassThru).CodeCoverage
````

```` Logger.Tests-20230524.log (ログ出力結果）
20230524T153012Z Logger.Tests DEBUG test-message
20230524T153012Z Logger.Tests INFO test-message
20230524T153012Z Logger.Tests WARN test-message
20230524T153012Z Logger.Tests ERROR test-message
````