core/utils/Write-Log.ps1
<#
The MIT License (MIT) Copyright (c) 2015 Objectivity Bespoke Software Specialists Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #> Function Write-Log { <# .SYNOPSIS Writes a nicely formatted Message to stdout/file/event log. .DESCRIPTION Writes according to $PSCIGlobalConfiguration.Log* variables. .PARAMETER Critical DEPRECATED - throw an exception instead. If specified, an error will be logged and an exception will be thrown. .PARAMETER Error If specified, an error will be logged. .PARAMETER Warn If specified, a warning will be logged. .PARAMETER Info If specified, an information will be logged. .PARAMETER _debug If specified, a debug Message will be logged. .PARAMETER Emphasize If set, the Message at console will be made more visible (using colors). .PARAMETER NoHeader If specified, Header information will not be logged (e.g. '[ERROR]: (function_name)'). .PARAMETER Indent Additional indent (optional). .PARAMETER Message Message to output. .PARAMETER PassThru If enabled, all log output will be available as return value. .PARAMETER CustomCallerInfo Custom string containing caller information, used in logging exceptions. .EXAMPLE Write-Log -Error "A disaster has occurred." #> [CmdletBinding()] [OutputType([void])] param( [Parameter(Mandatory=$false)] [switch] $Critical = $false, ## deprecated [Parameter(Mandatory=$false)] [switch] $Error = $false, [Parameter(Mandatory=$false)] [switch] $Warn = $false, [Parameter(Mandatory=$false)] [switch] $Info = $false, [Parameter(Mandatory=$false)] [switch] $_debug = $false, [Parameter(Mandatory=$false)] [switch] $Emphasize = $false, [Parameter(Mandatory=$false)] [switch] $NoHeader = $false, [Parameter(Mandatory=$true, ValueFromPipeline=$true)] [string[]] $Message, [Parameter(Mandatory=$false)] [int] $Indent = 0, [Parameter(Mandatory=$false)] [switch] $PassThru = $false, [Parameter(Mandatory=$false)] [object] $CustomCallerInfo = $false ) Begin { $severityNotSet = $false; if ($Critical) { $Severity = [PSCI.LogSeverity]::CRITICAL $severityChar = 'C' } elseif ($Error) { $Severity = [PSCI.LogSeverity]::ERROR $severityChar = 'E' } elseif ($warn) { $Severity = [PSCI.LogSeverity]::WARN $severityChar = 'W' } elseif ($info) { $Severity = [PSCI.LogSeverity]::INFO $severityChar = 'I' } elseif ($_debug) { $Severity = [PSCI.LogSeverity]::DEBUG $severityChar = 'D' } else { $severityNotSet = $true; } if (!$severityNotSet -and [int]$Severity -lt [int]$PSCIGlobalConfiguration.LogLevel) { return } $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' if ($CustomCallerInfo) { $callerInfo = $CustomCallerInfo } else { $callerInfo = Get-CallerInfo } if ($severityNotSet) { $output = "[Write-Log error / $callerInfo]: At least one of switches (critical / error / warn / info / debug) must be on for Write-Log." throw $output } if ($NoHeader) { $outputHeader = "" } else { $currentHostname = [system.environment]::MachineName $currentUsername = $env:USERNAME if ($PSCIGlobalConfiguration.RemotingMode) { $remotingFlag = '[R] ' } else { $remotingFlag = '' } $outputHeader = "[$severityChar] $timestamp ${remotingFlag}[$currentHostname/${currentUsername}]: ($callerInfo) " } if ($Critical) { throw $Message } } Process { if (!$severityNotSet -and [int]$Severity -lt [int]$PSCIGlobalConfiguration.LogLevel) { return } Write-LogMessage -Header (" " * $Indent + $outputHeader) -Message $Message -Severity $Severity -Emphasize:$Emphasize -PassThru:$PassThru } End { } } function Write-LogMessage() { <# .SYNOPSIS Outputs the Message to stdout/log file/event log. Helper function. .PARAMETER Header Message Header .PARAMETER Message Message body .PARAMETER Severity Severity .PARAMETER Emphasize Emphasize .PARAMETER PassThru If enabled, all log output will be available as return value. .EXAMPLE Write-LogMessage -Header "Header" -Message "Message" -Severity $Severity #> [CmdletBinding()] [OutputType([void])] param( [string] $Header, [string[]] $Message, [PSCI.LogSeverity] $Severity, [switch] $Emphasize, [Parameter(Mandatory=$false)] [switch] $PassThru ) try { Write-LogToStdOut -Header $Header -Message $Message -Severity $Severity -Emphasize:$Emphasize Write-LogToFile -Header $Header -Message $Message -Severity $Severity Write-LogToEventLog -Header $Header -Message $Message -Severity $Severity Write-LogToPSOutput -Header $Header -Message $Message -Severity $Severity -PassThru:$PassThru } catch { $exception = $_.Exception $Message = "Writing to log failed - script will terminate.`r`n" $currentUser = Get-CurrentUser if ($PSCIGlobalConfiguration.LogFile) { $Message += "`r`nPlease ensure that current user ('{0}') has access to file '{1}' or change the path to log file in GlobalSettings.LogFile." -f $currentUser, $PSCIGlobalConfiguration.LogFile } if ($PSCIGlobalConfiguration.LogEventLogSource) { if (!$PSCIGlobalConfiguration.LogEventLogCreateIfNotExists) { $Message += "`r`nPlease ensure that Event Log source named '{0}' exists in Application log or switch on 'GlobalSettings.LogEventLogCreateIfNotExists' setting (needs Administrator)." -f $PSCIGlobalConfiguration.LogEventLogSource } else { $Message += "`r`nPlease ensure that current user ('{0}') is able to create Event Log sources or create the source manually and switch off 'GlobalSettings.LogEventLogCreateIfNotExists' setting." -f $currentUser } } $Message += "`n" + ($_ | Format-List -Force | Out-String) + ($exception | Format-List -Force | Out-String) Write-Host $Message [void](New-Item -Path "error.txt" -ItemType file -Value $Message -Force) Stop-Execution } } function Write-LogToStdOut() { <# .SYNOPSIS Outputs the Message to stdout. Helper function. .PARAMETER Header Message Header .PARAMETER Message Message body .PARAMETER Severity Severity .PARAMETER Emphasize Emphasize .EXAMPLE Write-LogToStdOut -Header "Header" -Message "Message" -Severity $Severity #> [CmdletBinding()] [OutputType([void])] param( [string] $Header, [string[]] $Message, [PSCI.LogSeverity] $Severity, [switch] $Emphasize ) if (Test-WebDeployRemotingMode) { $msg = $Message -join "`r`n" Write-Host "$Header$msg" } else { Write-Host $Header -NoNewline -Fore "Gray" $color = switch ($Severity) { ([PSCI.LogSeverity]::CRITICAL) { [ConsoleColor]::Red } ([PSCI.LogSeverity]::ERROR) { [ConsoleColor]::Red } ([PSCI.LogSeverity]::WARN) { [ConsoleColor]::Yellow } ([PSCI.LogSeverity]::INFO) { if ($PSCIGlobalConfiguration.RemotingMode) { if ($Emphasize) { [ConsoleColor]::DarkCyan } else { [ConsoleColor]::Gray } } else { if ($Emphasize) { [ConsoleColor]::Cyan } else { [ConsoleColor]::White } } } ([PSCI.LogSeverity]::DEBUG) { if ($PSCIGlobalConfiguration.RemotingMode) { [ConsoleColor]::DarkGray } else { [ConsoleColor]::Gray } } default { [ConsoleColor]::Red } } foreach ($msg in $Message) { Write-Host $msg -Fore $color } } } function Write-LogToFile() { <# .SYNOPSIS Outputs the Message to file. Helper function. .PARAMETER Header Message Header .PARAMETER Message Message body .PARAMETER Severity Severity .EXAMPLE Write-LogToFile -Header "Header" -Message "Message" -Severity $Severity #> [CmdletBinding()] [OutputType([void])] param( [string] $Header, [string[]] $Message, [PSCI.LogSeverity] $Severity ) if ($PSCIGlobalConfiguration.LogFile) { if (![System.IO.Path]::IsPathRooted($PSCIGlobalConfiguration.LogFile)) { # we need to set absolute path to log file as .NET working directory would be c:\windows\system32 $PSCIGlobalConfiguration.LogFile = Join-Path -Path ((Get-Location).ProviderPath) -ChildPath $PSCIGlobalConfiguration.LogFile } $strBuilder = New-Object System.Text.StringBuilder [void]($strBuilder.Append($Header)) foreach ($msg in $Message) { [void]($strBuilder.Append($msg).Append("`r`n")) } [io.file]::AppendAllText($PSCIGlobalConfiguration.LogFile, ($strBuilder.ToString()), [System.Text.Encoding]::Unicode) } } function Write-LogToEventLog() { <# .SYNOPSIS Outputs the Message to event log. .DESCRIPTION Creates new event log source if not exists and $PSCIGlobalConfiguration.LogEventLogCreateSourceIfNotExists is set. Helper function. .PARAMETER Header Message Header .PARAMETER Message Message body .PARAMETER Severity Severity .EXAMPLE Write-LogToEventLog -Header "Header" -Message "Message" -Severity $Severity #> [CmdletBinding()] [OutputType([string])] param( [string] $Header, [string[]] $Message, [PSCI.LogSeverity] $Severity ) if ($PSCIGlobalConfiguration.LogEventLogSource) { if ([int]$Severity -ge [int]$PSCIGlobalConfiguration.LogEventLogThreshold) { if ($Severity -eq [PSCI.LogSeverity]::ERROR -or $Severity -eq [PSCI.LogSeverity]::CRITICAL) { $entryType = [System.Diagnostics.EventLogEntryType]::Error } elseif ($Severity -eq [PSCI.LogSeverity]::WARN) { $entryType = [System.Diagnostics.EventLogEntryType]::Warning } else { $entryType = [System.Diagnostics.EventLogEntryType]::Information } if ($PSCIGlobalConfiguration.LogEventLogCreateSourceIfNotExists -and ![System.Diagnostics.EventLog]::SourceExists($PSCIGlobalConfiguration.LogEventLogSource)) { [void](New-EventLog -LogName Application -Source $PSCIGlobalConfiguration.LogEventLogSource) } $strBuilder = New-Object System.Text.StringBuilder [void]($strBuilder.Append($Header)) foreach ($msg in $Message) { [void]($strBuilder.Append($msg).Append("`r`n")) } Write-EventLog -LogName Application -Source $PSCIGlobalConfiguration.LogEventLogSource -EntryType $entryType -EventID 1 -Message ($strBuilder.ToString()) } } } function Write-LogToPSOutput() { <# .SYNOPSIS Outputs the Message using Write-Output function. Helper function. .PARAMETER Header Message Header .PARAMETER Message Message body .PARAMETER Severity Severity .PARAMETER PassThru If enabled, all log output will be available as return value. .EXAMPLE Write-LogToPSOutput -Header "Header" -Message "Message" #> [CmdletBinding()] [OutputType([void])] param( [string] $Header, [string[]] $Message, [PSCI.LogSeverity] $Severity, [Parameter(Mandatory=$false)] [switch] $PassThru ) if ($PassThru) { $msg = $Message -join "`r`n" Write-Output -InputObject "$Header$msg" } } function Test-WebDeployRemotingMode() { <# .SYNOPSIS Tests whether we're running in WebDeploy remoting mode. .EXAMPLE Test-WebDeployRemotingMode #> [CmdletBinding()] [OutputType([bool])] param () return $PSCIGlobalConfiguration.RemotingMode -in @("WebDeployHandler", "WebDeployAgentService") } |