Functions/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
function Write-Log {
    <#
        .SYNOPSIS
            Write to the log.
        .DESCRIPTION
            The Write-Log function is used to write to the log. It is using the log object created by New-Log
            to determine if it's going to write to a log file or to a Windows Event log.
        .EXAMPLE
            Write-Log 'Finished running WMI query'
            Get the log object from $global:PSLOG and write to the log.
        .EXAMPLE
            $myLog | Write-Log 'Finished running WMI query'
            Use the log object saved in $myLog and write to the log.
        .EXAMPLE
            Write-Log 'WMI query failed - Access denied!' -LogType Error -PassThru | Write-Warning
            Will write an error to the event log, and then pass the log entry to the Write-Warning cmdlet.
        .NOTES
            Author: Øyvind Kallstad
            Date: 21.11.2014
            Version: 1.0
            Dependencies: Invoke-LogRotation
    #>

    [CmdletBinding()]
    param (
        # The text you want to write to the log.
        [Parameter(Position = 0)]
        [string] $LogEntry,

        # The type of log entry. Valid choices are 'Error', 'FailureAudit','Information','SuccessAudit' and 'Warning'.
        # Note that the CMTrace format only supports 3 log types (1-3), so 'Error' and 'FailureAudit' are translated to CMTrace log type 3, 'Information' and 'SuccessAudit'
        # are translated to 1, while 'Warning' is translated to 2. 'FailureAudit' and 'SuccessAudit' are only really included since they are valid log types when
        # writing to the Windows Event Log.
        [Parameter()]
        [ValidateSet('Error','FailureAudit','Information','SuccessAudit','Warning')]
        [string] $LogType = 'Information',

        # Event ID. Only applicable when writing to the Windows Event Log.
        [Parameter()]
        [string] $EventID,

        # The log object created using the New-Log function. Defaults to reading the global PSLOG variable.
        [Parameter(ValueFromPipeline)]
        [ValidateNotNullorEmpty()]
        [object] $Log = $global:PSLOG,

        # PassThru passes the log entry to the pipeline for further processing.
        [Parameter()]
        [switch] $PassThru
    )

    try {

        # get information from log object
        $logObject = $Log

        # translate event types to CMTrace format
        if ($logObject.LogFormat -eq 'CMTrace') {
            switch ($LogType) {
                'Error' {$cmType = '3';break}
                'FailureAudit' {$cmType = '3';break}
                'Information' {$cmType = '1';break}
                'SuccessAudit' {$cmType = '1';break}
                'Warning' {$cmType = '2';break}
                DEFAULT {$cmType = '1'}
            }
        }

        # get invocation information
        $thisInvocation = (Get-Variable -Name 'MyInvocation' -Scope 1).Value

        # get calling script info
        if(-not ($thisInvocation.ScriptName)){
            $scriptName = $thisInvocation.MyCommand
            $file = "$($scriptName)"
        }
        else{
            $scriptName = Split-Path -Leaf ($thisInvocation.ScriptName)
            $file = "$($scriptName):$($thisInvocation.ScriptLineNumber)"
        }

        # get calling command info
        $component = "$($thisInvocation.MyCommand)"

        if ($logObject.LogType -eq 'EventLog') {
            if($logObject.Elevated) {

                # if EventID is not specified use default event id from the log object
                if([system.string]::IsNullOrEmpty($EventID)) {
                    $EventID = $logObject.DefaultEventID
                }

                Write-EventLog -LogName $logObject.EventLogName -Source $logObject.EventLogSource -EntryType $LogType -EventId $EventID -Message $LogEntry
            }

            else {
                Write-Warning 'When writing to the Windows Event Log you need to run as a user with elevated rights!'
            }
        }

        else {
            # create a mutex, so we can lock the file while writing to it
            $mutex = New-Object System.Threading.Mutex($false, 'LogMutex')

            # handle the different log file formats
            switch ($logObject.LogFormat) {

                'Minimal' { $logEntryString = $LogEntry; break }

                'PlainText' {
                    $logEntryString = "$((Get-Date).ToString()) $($LogType.ToUpper()) $($LogEntry)"
                    # when component and file are equal
                    #if($component -eq $file){
                        #$logEntryString = "$((Get-Date).ToString()) $($LogType.ToUpper()) [$($file)] $($LogEntry)"
                        #Write-Verbose $logEntryString ####
                    #}

                    # log entry when component and file are not equal
                    #else{
                        #$logEntryString = "$((Get-Date).ToString()) $($LogType.ToUpper()) [$($component) - $($file)] $($LogEntry)"
                    #}
                    break
                }

                'CMTrace' {
                    $date = Get-Date -Format 'MM-dd-yyyy'
                    $time = Get-Date -Format 'HH:mm:ss.ffffff'
                    #$logEntryString = "<![LOG[$LogEntry]LOG]!><time=""$time"" date=""$date"" component=""$component"" context="""" type=""$cmType"" thread=""$pid"" file=""$file"">"
                    $logEntryString = "<![LOG[$LogEntry]LOG]!><time=""$time"" date=""$date"" component="""" context="""" type=""$cmType"" thread=""$pid"" file="""">"
                    break
                }
            }

            # write to the log file
            [void]$mutex.WaitOne()
            Add-Content -Path $logObject.Path -Value $logEntryString
            $mutex.ReleaseMutex()

            # invoke log rotation if log is file
            if ($logObject.LogType -eq 'LogFile') {
                Invoke-LogRotation
            }

            # handle PassThru
            if ($PassThru) {
                Write-Output $LogEntry
            }
        }
    }

    catch {
        Write-Warning $_.Exception.Message
    }
}