Public/Log/Write-Log.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
Function Write-Log {
    <#
    .SYNOPSIS
        Writes a nicely formatted Message to stdout/file/event log.

    .DESCRIPTION
        It uses optional $LogConfiguration object which describes logging configuration (see LogConfiguration.ps1).
        It can be set using Set-LogConfiguration function.

    .INPUTS
        A string message to log.

    .EXAMPLE
        Write-Log -Info "Generating file test.txt."
        [I] 2017-05-19 11:14:28 [hostName/userName]: (scriptName/commandName/1) Generating file test.txt.

    .EXAMPLE
        Set-LogConfiguration -LogLevel Warn
        Write-Log -Info "Generating file test.txt."
        <nothing will be logged>
        Write-Log -Error "Failed to generate file test.txt."
        [E] 2017-05-19 11:14:28 [hostName/userName]: (scriptName/commandName/1) Failed to generate file test.txt.

    .EXAMPLE
        Set-LogConfiguration -LogFile 'log.txt'
        Write-Log -Info "Generating file test.txt."
        Logs message to stdout and log.txt file.

    .EXAMPLE
        Set-LogConfiguration -LogEventLogSource 'MyApplication' -LogEventLogThreshold Warn
        Write-Log -Warn "Failed to generate file test.txt."
    #>


    [CmdletBinding()]
    [OutputType([string[]])]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '')]
    param(
        # If specified, an error will be logged.
        [Parameter(Mandatory=$false)]
        [switch]
        $Error = $false,

        # If specified, a warning will be logged.
        [Parameter(Mandatory=$false)]
        [switch]
        $Warn = $false,

        # If specified, an info message will be logged (default).
        [Parameter(Mandatory=$false)]
        [switch]
        $Info = $false,

        # If specified, a debug message will be logged.
        [Parameter(Mandatory=$false)]
        [switch]
        $_debug = $false,

        # If set, the message at console will be emphasized using colors.
        [Parameter(Mandatory=$false)]
        [switch]
        $Emphasize = $false,

        # If specified, header information will not be logged (e.g. '[ERROR]: (function_name)').
        [Parameter(Mandatory=$false)]
        [switch]
        $NoHeader = $false,

        # Message to output.
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [string[]]
        $Message,

        # Additional indent.
        [Parameter(Mandatory=$false)]
        [int]
        $Indent = 0,

        # If enabled, all log output will be available as return value (will use Write-Output instead of Write-Host).
        [Parameter(Mandatory=$false)]
        [switch]
        $PassThru = $false
    )

    Begin {
        if ($Error) {
            $severity = 3;
            $severityChar = 'E'
        }
        elseif ($warn) {
            $severity = 2;
            $severityChar = 'W'
        }
        elseif ($_debug) {
            $severity = 0;
            $severityChar = 'D'
        }
        else {
            $severity = 1;
            $severityChar = 'I'
        }

        if (!(Test-LogSeverity -MessageSeverity $severity)) {
            return
        }

        $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
        $callerInfo = (Get-PSCallStack)[1]
        if ($callerInfo.InvocationInfo -and $callerInfo.InvocationInfo.MyCommand -and $callerInfo.InvocationInfo.MyCommand.Name) {
            $callerCommandName = $callerInfo.InvocationInfo.MyCommand.Name
        }
        else {
            $callerCommandName = '';
        }
        if ($callerInfo.ScriptName) {
            $callerScriptName = Split-Path -Leaf $callerInfo.ScriptName
        }
        else {
            $callerScriptName = 'no script';
        }
        if ($callerInfo.ScriptLineNumber) {
            $callerLineNumber = $callerInfo.ScriptLineNumber
        }
        else {
            $callerLineNumber = ''
        }
        $callerInfoString = "$callerScriptName/$callerCommandName/$callerLineNumber"

        if ($NoHeader) {
            $outputHeader = ""
        }
        else {
            $currentHostname = [system.environment]::MachineName
            $currentUsername = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
            if (Test-Path -Path Variable:PSSenderInfo) {
                $remotingFlag = '[R] '
            }
            else {
                $remotingFlag = ''
            }
            $outputHeader = "[$severityChar] $timestamp ${remotingFlag}[$currentHostname/${currentUsername}]: ($callerInfoString) "
        }
        $header = " " * $Indent + $outputHeader
    }
    Process {
        if (!(Test-LogSeverity -MessageSeverity $severity)) {
            return
        }
        try {
            Write-LogToStdOut -Header $Header -Message $Message -Severity $Severity -Emphasize:$Emphasize -PassThru:$PassThru
            Write-LogToFile -Header $Header -Message $Message -Severity $Severity -PassThru:$PassThru
            Write-LogToEventLog -Header $Header -Message $Message -Severity $Severity -PassThru:$PassThru
            Write-LogToPSOutput -Header $Header -Message $Message -Severity $Severity -PassThru:$PassThru
        }
        catch {
            $exception = $_.Exception
            $msg = "Writing to log failed - script will terminate.`r`n"
            $currentUsername = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
            if ($LogConfiguration -and $LogConfiguration.LogFile) {
                $msg += "`r`nPlease ensure that current user ('{0}') has access to file '{1}' or change the path to log file in `$LogConfiguration.LogFile." -f $currentUsername, $LogConfiguration.LogFile
            }
            if ($LogConfiguration -and $LogConfiguration.LogEventLogSource) {
                $msg += "`r`nPlease ensure that current user ('{0}') is able to create Event Log sources or create the source manually." -f $currentUsername
            }

            $msg += "`n" + ($_ | Format-List -Force | Out-String) + ($exception | Format-List -Force | Out-String)
            Write-Host $msg
            [void](New-Item -Path "error.txt" -ItemType file -Value $msg -Force)
            throw "Logging failure"
        }
    }

    End {
    }
}