[PowerShell] Log Functions for Custom Modules

Posted by Justin C. on December 17, 2019 · 18 mins read Category: Programming Tags: Powershell

Logging activity within custom functions can often be repetitive and messy. The following two log functions for custom modules are intended to make activity logging simple and easy. Include these two functions in your module, update the default LogPath parameter and you’re well on your way.

The New-Log function is designed to be embedded into the functions of your custom module. The only mandatory parameter is the log type. Other information such as the module name, function name, and current user is automatically captured and logged. New log entries are automatically appended to a daily log located at the location specified by LogPath. If no log path is provided, the function will use the default LogPath location embedded in the function.

New-Log.ps1

<#
.Synopsis
   This cmdlet creates a new log entry and appends it to the daily log file on the specified log server.
.DESCRIPTION
   This cmdlet creates a new log entry and appends it to the daily log file on the specified log server.
.EXAMPLE
   New-Log -Type "Pass"
.EXAMPLE
   New-Log -Type "Fail" -Target TargetComputerName
.EXAMPLE
   New-Log -Type "Fail" -Target TargetComputerName -Throw
.NOTES

Disclaimer:
  This code is provided "as is" and with no expressed guarantees. The code provider makes no representations or warranties of any kind concerning
the safety, suitability, inaccuracies, typographical errors, or other harmful components of this code. There are inherent dangers in the use of
any code, and you are solely responsible for determining whether this code is compatible with your equipment and other software installed on your
equipment. You are also solely responsible for the protection of your equipment and backup of your data, and the provider will not be liable for
any damages you may suffer in connection with using, modifying, or distributing this code.


AUTHOR:
Justin C.
#>

Function New-Log
{
    [Alias()]
    Param
    (
        #Log Entry Type
        [Alias("Type")]
        [ValidateSet("Pass", "Fail", "Info", "Test")]
        [Parameter(Mandatory, Position=0, ValueFromPipelineByPropertyName)]
        [String] $LogType,

        #Target Object
        [Alias("ComputerName","Identity","Name")]
        [Parameter(Position=1, ValueFromPipelineByPropertyName)]
        [String] $Target,

        #Remarks
        [Alias("Remark", "Comment", "Comments", "Note", "Notes")]
        [Parameter(Position=2, ValueFromPipelineByPropertyName)]
        [String] $Remarks="None",

        #Error Message
        [Parameter(Position=3, ValueFromPipelineByPropertyName)]
        $ErrorMsg,

        #Log Path
        [Parameter(Position=4, ValueFromPipelineByPropertyName)]
        [String] $LogPath = "\LogServerNameLogs",

        [Switch] $WriteHost,

        [Switch] $Throw
        )
    Begin {
        $DateFormat = Get-Date -UFormat %b-%d-%Y
        $LogFile = Join-Path $LogPath "$DateFormat.csv"
        If(Test-Path $LogPath -ErrorAction SilentlyContinue){}
        Else{New-Item -ItemType Directory -Path $LogPath}
    }
    Process {
        $Stack = (Get-PSCallStack | Select-Object -Property *)[1]
        #If ($null -ne $Parameters){$ParameterString = Convert-HashToString $PsBoundParameters}Else{$ParameterString="None"}
        If ($null -eq $Target){$Target = "None"}
        If ($null -eq $Stack.Arguments) {$FunctionArguments = "None"}Else{$FunctionArguments = $Stack.Arguments}
        $LogEntry = New-Object -TypeName PSObject -Property ([Ordered]@{
            Type = $LogType;
            Module = $Stack.ScriptName;
            Function = $Stack.Command;
            Target = $Target;
            Arguments = $FunctionArguments;
            User = $env:USERNAME;
            Computer = $env:COMPUTERNAME;
            Timestamp = Get-Date -Format G;
            ErrorData = $ErrorMsg;
            Remarks = $Remarks;
        })
        If ($Target){
            If ($WriteHost){Write-Host "$Target - $Remarks"}
            Write-Verbose "LOGGED: $LogType Event for $($Stack.Command) on $Target logged to $LogFile"
        } Else {
            If ($WriteHost){Write-Host "$Remarks"}
            Write-Verbose "LOGGED: $LogType Event for $($Stack.Command) logged to $LogFile"
        }
        $LogEntry | Export-Csv $LogFile -Append -NoTypeInformation -Force
    }
	End {
        If ($Throw) {Throw "$($Stack.Command) - $Remarks" }
    }
}

Function Convert-HashToString
{
    [OutputType([String])]

    Param (
    [Parameter(Mandatory)]
    [System.Collections.Hashtable] $Hash
    )
    $HashString = "@{"
    $Keys = $Hash.Keys
    foreach ($Key in $Keys) {
        $Value = $Hash[$Key]
        If ($Key -match "s"){$HashString += "'$Key'" + "=" + "'$Value'" + ";"}
        Else {$HashString += $Key + "=" + "'$Value'" + ";"}
    }
    $HashString += "}"
    return $HashString
}
Function Convert-ArrayToString
{
    [OutputType([String])]

    Param (
    [Parameter(Mandatory)]
    [System.Array] $Array
    )
    $ArrayString = $Array.join(", ")
    return $ArrayString
}

The Get-Log function fetches the log files at the provided LogPath and filter the entries by any applicable filter parameters. If no LogPath is provided, the function will use to the default value embedded in the function.

Get-Log.ps1

<#
.Synopsis
   This cmdlet returns a collection of filtered log entries retrieved from log server.
.DESCRIPTION
   This cmdlet returns a collection of filtered log entries retrieved from log server.
.EXAMPLE
   Get-Log -Type Fail
.EXAMPLE
   Get-Log -Type Info -Module MyCustomModuleName -Target TargetComputer

.NOTES

Disclaimer:
  This code is provided "as is" and with no expressed guarantees. The code provider makes no representations or warranties of any kind concerning
the safety, suitability, inaccuracies, typographical errors, or other harmful components of this code. There are inherent dangers in the use of
any code, and you are solely responsible for determining whether this code is compatible with your equipment and other software installed on your
equipment. You are also solely responsible for the protection of your equipment and backup of your data, and the provider will not be liable for
any damages you may suffer in connection with using, modifying, or distributing this code.


AUTHOR:
Justin C.
#>

Function Get-Log
{
    [Alias()]
    Param
    (
        #Log Type Filter
        [Alias("Type")]
        [ValidateSet("Pass", "Fail", "Info", "Test")]
        [Parameter(ValueFromPipelineByPropertyName)]
        [String] $LogType,

        #Module Filter
        [Parameter(ValueFromPipelineByPropertyName)]
        [String] $Module,

        #Function Filter
        [Parameter(ValueFromPipelineByPropertyName)]
        [String] $Function,

        #Target Object Filter
        [Alias("ComputerName","Identity","Name")]
        [Parameter(ValueFromPipelineByPropertyName)]
        [String] $Target,

        #Arguments Filter
        [Parameter(ValueFromPipelineByPropertyName)]
        [String] $Arguments,

        #User Filter
        [Parameter(ValueFromPipelineByPropertyName)]
        [String] $User,

        #Computer Filter
        [Parameter(ValueFromPipelineByPropertyName)]
        [String] $Computer,

        #Remarks
        [Alias("Remark", "Comment", "Comments", "Note", "Notes")]
        [Parameter(ValueFromPipelineByPropertyName)]
        [String] $Remarks="",

        #Error Message
        [Parameter(ValueFromPipelineByPropertyName)]
        [String] $ErrorMsg,

        #Log Path
        [Parameter(ValueFromPipelineByPropertyName)]
        [String] $LogPath = "\LogServerNameLogs"

    )
    Begin {
        If ((Get-Item $LogPath) -is [System.IO.DirectoryInfo]){
            $LogFiles = Get-Item (Join-Path $LogPath "*.csv")
        } Else {
            $LogFiles = Get-Item $LogPath
        }
        $Results = @()
    }
    Process {
        ForEach ($LogFile in $LogFiles){
            Write-Verbose "Log File: $($LogFile.FullName)"
            $Logs = Import-Csv $LogFile
            Write-Verbose "Filters:"
            If ($LogType){
                Write-Verbose "Type: $LogType"
                $Logs = ($Logs | Where-Object -Property Type -Match $LogType)}
            If ($Module -ne $null){
                Write-Verbose "Module: $Module"
                $Logs = ($Logs | Where-Object -Property Module -Match ".*$Module.*")}
            If ($Function -ne $null){
                Write-Verbose "Function: $Function"
                $Logs = ($Logs | Where-Object -Property Function -Match ".*$Function.*")}
            If ($Target -ne $null){
                Write-Verbose "Target: $Target"
                $Logs = ($Logs | Where-Object -Property Target -Match ".*$Target.*")}
            If ($Arguments -ne $null){
                Write-Verbose "Arguments: $Arguments"
                $Logs = ($Logs | Where-Object -Property Arguments -Match ".*$Arguments.*")}
            If ($User -ne $null){
                Write-Verbose "User: $User"
                $Logs = ($Logs | Where-Object -Property User -Match $User)}
            If ($Computer -ne $null){
                Write-Verbose "Computer: $Computer"
                $Logs = ($Logs | Where-Object -Property Computer -Match ".*$Computer.*")}
            If ($Remarks -ne $null){
                Write-Verbose "Remarks: $Remarks"
                $Logs = ($Logs | Where-Object -Property Remarks -Match ".*$Remarks.*")}
            If ($ErrorMsg -ne $null){
                Write-Verbose "Error Message: $ErrorMsg"
                $Logs = ($Logs | Where-Object -Property ErrorData -Match ".*$ErrorMsg.*")}
            $Results += $Logs
        }
    }
	End {Return $Results}
}