Log.ps1


#
# Logging
#

$Global:LogFile = $null
$Global:LogVerbose = $False


# Replace with Convert-Object when updating code.
Function Convert-ObjectToHashtable([psobject]$object)
{
    $output = @{}

    $object.PSObject.Properties | ForEach-Object {
        if ($_.Value -is [PSCustomObject])
        {
            $_.Value = Convert-ObjectToHashtable($_.Value)
        }
        $output.($_.Name) = $_.Value
    }

    return $output
}

function Convert-ObjectToString([psobject]$object)
{
    $objectString = $object.PSObject.Properties | ForEach-Object {
        if ($_.Value -is [PSCustomObject])
        {
            "$($_.Name) = $(Convert-ObjectToString($_.Value))"
        }
        elseif ($value -is [hashtable])
        {
            "$_ = $(Convert-HashToString $value)"
        }
        elseif ($value -is [array])
        {
            "$_ = $(Convert-ArrayToString $value)"
        }
        else
        {
            "$($_.Name) = $($_.Value)"
        }
    }
    return "@{" + ($objectString -join ', ') + "}"
}

Function Convert-ArrayToString([array] $array)
{
    return "[" + (
        $array.foreach(
            {
                if ($_ -is [PSCustomObject])
                {
                    "$(Convert-HashToString( Convert-ObjectToHashtable $_))"
                }
                elseif ($_ -is [hashtable])
                {
                    "$(Convert-HashToString $_)"
                }
                elseif ($_ -is [array])
                {
                    "$(Convert-ArrayToString $_)"
                }
                else
                {
                    "$_"
                }
            }) -join ', ') + "]"
}

Function Convert-HashToString([psobject]$hashtable)
{
    return "@{" + (
        $hashtable.keys.foreach(
            {
                $value = $hashtable[$_]
                if ($Null -ne $value)
                {
                    $type = $value.GetType()
                }
                else
                {
                    $type = "Null"
                }
                if ($value -is [hashtable])
                {
                    "[$type]$_=$(Convert-HashToString $value)"
                }
                elseif ($value -is [array])
                {
                    "[$type]$_=$(Convert-ArrayToString $value)"
                }
                else
                {
                    "[$type]$_=$value"
                }
            }) -join ', '
    ) + "}"
}

function Convert-Object([psobject]$object)
{
    if (($object -is [string]) -or ($object -is [int]))
    {
        return $object
    }
    elseif ($object -is [array])
    {
        $output = @()
        $object | ForEach-Object {
            $output += Convert-Object $_
        }
        return $output
    }
    elseif ($object -is [hashtable])
    {
        $output = @{}
        $object.Keys | ForEach-Object {
            $value = $object[$_]
            $output.$_ = Convert-Object $value
        }
        return $output
    }
    elseif ($object -is [PSCustomObject])
    {
        $output = @{}
        $object.PSObject.Properties | ForEach-Object {
            $output.($_.Name) = Convert-Object $_.Value
        }
        return $output
    }
}

function GetParameters([psobject]$invocation)
{
    $parameters = @{}

    foreach ($name in (Get-Command -Name $invocation.InvocationName).Parameters.keys)
    {
        $variable = Get-Variable -Name $name -ErrorAction SilentlyContinue
        $parameters[$name] = if ($variable) { $variable.value } else { $Null }
    }

    return $parameters
}

function GetVariablesAsHash()
{
    $variables = @{}
    Get-Variable -Scope 1 | ForEach-Object { $variables[$_.Name] = $_.Value }
    return $variables
}

Function LogInit([psobject]$invocation, [string]$logDir, [string]$logFile, [bool]$overwrite, [bool]$Verbose)
{
    if ([string]::IsNullOrEmpty($logDir))
    {
        $Global:LogFile = "$(Get-Location)\$logFile"
    }
    else
    {
        $Global:LogFile = "$logDir\$logFile"
    }
    $Global:LogVerbose = $Verbose
    if ($overwrite)
    {
        $timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.ffffZ")
        "$($timestamp): New log" | Out-File -FilePath $Global:LogFile
    }
    else
    {
        $timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.ffffZ")
        "$($timestamp): New log" | Out-File -FilePath $Global:LogFile -Append
    }
    Write-Host "Logging to $($Global:LogFile) Verbose=$Verbose"
    if ($null -ne $invocation)
    {
        $parameters = GetParameters $invocation
        if ($parameters.ContainsKey('SecureSecret') -And -not([string]::IsNullOrEmpty($parameters.SecureSecret)))
        {
            $parameters['SecureSecret'] = "***"
        }
        LogIt -message "$($invocation.MyCommand) called with params: $(Convert-HashToString $parameters) from Powershell $($PSVersionTable.PSVersion)" -echoToScreen $Verbose
    }
    LogIt -message "$($invocation.MyCommand.Module.Name) version $($invocation.MyCommand.Module.Version) from $($invocation.MyCommand.Module.Path)" -echoToScreen $Verbose
}

Function LogIt([string]$message, [bool]$echoToScreen = $True)
{
    if ($null -ne $Global:LogFile)
    {
        if ($echoToScreen)
        {
            Write-Host $message
        }
        $timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.ffffZ")
        "$($timestamp): $message" | Out-File -FilePath $Global:LogFile -Append
    }
}

Function LogItWarning([string]$message)
{
    if ($null -ne $Global:LogFile)
    {
        Write-Warning $message
        $timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.ffffZ")
        "$($timestamp): $message" | Out-File -FilePath $Global:LogFile -Append
    }
}


Function LogItError([string]$message)
{
    if ($null -ne $Global:LogFile)
    {
        $Host.UI.WriteErrorLine("ERROR: $message")
        $timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.ffffZ")
        "$($timestamp): $message" | Out-File -FilePath $Global:LogFile -Append
    }
}


Function LogFatal([string]$message, [bool]$echoToScreen = $True)
{
    LogIt $message $echoToScreen
    throw $message
}


Function LogFatalException([string]$Message, [System.Management.Automation.RuntimeException]$Exception)
{
    LogIt "$($Message): $($Exception.message) at $($Exception.StackTrace)"
    throw "$($Message): $($Exception.message)"
}