Powershell logging setup that supports files, console output and event logs - powershell

Is there a way to do logging in a Powershell script so that
a single call can be made to log a message
this message is then routed to none/any/multiple (depending on config) of the following
standard console output
a log file
windows event logs
Sort of like python's logging framework or log4j in Java, where the logging targets are configured separately from actual the logging of messages.

You can use logger module or script.
<#
.Synopsis
Write-Log writes a message to a specified log file with the current time stamp.
.DESCRIPTION
The Write-Log function is designed to add logging capability to other scripts.
In addition to writing output and/or verbose you can write to a log file for
later debugging.
Changelog:
* Code simplification and clarification
* Added documentation.
* Renamed LogPath parameter to Path to keep it standard.
* Revised the Force switch to work as it should.
To Do:
* Add error handling if trying to create a log file in a inaccessible location.
* Add ability to write $Message to $Verbose or $Error pipelines to eliminate
duplicates.
.PARAMETER Message
Message is the content that you wish to add to the log file.
.PARAMETER Path
The path to the log file to which you would like to write. By default the function will
create the path and file if it does not exist.
.PARAMETER Level
Specify the criticality of the log information being written to the log (i.e. Error, Warning, Informational)
.PARAMETER NoClobber
Use NoClobber if you do not wish to overwrite an existing file.
.EXAMPLE
Write-Log -Message 'Log message'
Writes the message to c:\Logs\PowerShellLog.log.
.EXAMPLE
Write-Log -Message 'Restarting Server.' -Path c:\Logs\Scriptoutput.log
Writes the content to the specified log file and creates the path and file specified.
.EXAMPLE
Write-Log -Message 'Folder does not exist.' -Path c:\Logs\Script.log -Level Error
Writes the message to the specified log file as an error message, and writes the message to the error pipeline.
#>
function Write-Log
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[Alias("LogContent")]
[string]$Message,
[Parameter(Mandatory=$false)]
[Alias('LogPath')]
[string]$Path='C:\Logs\PowerShellLog.log',
[Parameter(Mandatory=$false)]
[ValidateSet("Error","Warn","Info")]
[string]$Level="Info",
[Parameter(Mandatory=$false)]
[switch]$NoClobber
)
Begin
{
# Set VerbosePreference to Continue so that verbose messages are displayed.
$VerbosePreference = 'Continue'
}
Process
{
# If the file already exists and NoClobber was specified, do not write to the log.
if ((Test-Path $Path) -AND $NoClobber) {
Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name."
Return
}
# If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
elseif (!(Test-Path $Path)) {
Write-Verbose "Creating $Path."
$NewLogFile = New-Item $Path -Force -ItemType File
}
else {
# Nothing to see here yet.
}
# Format Date for our Log File
$FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
# Write message to error, warning, or verbose pipeline and specify $LevelText
switch ($Level) {
'Error' {
Write-Error $Message
$LevelText = 'ERROR:'
}
'Warn' {
Write-Warning $Message
$LevelText = 'WARNING:'
}
'Info' {
Write-Verbose $Message
$LevelText = 'INFO:'
}
}
# Write log entry to $Path
"$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
}
End
{
}
}
Then just call it out wherever you want in script :
Write-Log -Message "################ Script Started #############################" -Level Info
and if you want to redirect the error then call it in catch block:
try{ }
catch
{
{Write-Log -Message $_.Exception.Message -Level Error}
}
Note: Make sure in that case you compile the function in the beginning, else PS will not be able to use it inside the blocks because the scope will vary.
This will give you console and file output and you can script to direct to eventlog even. Hope it helps.

Related

Powershell not creating log file

Ive got a powershell script where I am updating alot of stuff and I am wanting to write everything to a log file. I've used a similar method as this to write a CSV file but I can't get this to work. Can anyone help me out on this? Everything works except the log file does not get created.
$path = Split-Path -parent "\\srv02file\Public\Information Technology\IT Reports\*.*"
$LogDate = Get-Date -f yyyyMMddhhmm
$LogFile = $path + "Remove_Formatting_$LogDate.log"
#Log function with levels of logging and time entry. Default level is INFO when not set
#LogWrite -LEVEL INFO -Message "Log entry"
#HH:MM:SS INFO Log entry
Function LogWrite
{
[CmdletBinding()]
Param (
[Parameter(Mandatory=$False)]
[ValidateSet("INFO","WARN","ERROR","FATAL","DEBUG")]
[String]
$Level = "INFO",
[Parameter(Mandatory=$True)]
[string]
$Message
#[Parameter(Mandatory=$False)]
#[string]
#$LogPath
)
$Stamp = (Get-Date).toString("hh:mm:ss")
$Line = "$Stamp $Level $Message"
Add-Content $LogFile -Value $Line
Write-Output $Line
}
You're missing a trailing slash after the folder in $LogFile
Use Join-Path to combine a folder and filename to avoid that issue in future.
You'll also need to uncomment the $LogPath parameter and use that, since $LogFile doesn't exist in the function scope.

Powershell bind argument path

When I execute a particular script, the Write-Log function throws an error. This appears to be before I call the function. It is loaded from an external file. This problem is new, it worked previously with no issues.
Error
There are two errors, split by a VERBOSE output statement. The 2nd VERBOSE line is the last script line I included. The format (not colours) is the best I can recreate
C:\Scripts>powershell -file tca_export_tt_ftps.ps1
Test-Path : Cannot bind argument to parameter 'Path' because it is an empty string.
At C:\scripts\Function-Write-Log.ps1:75 char:24
+ if ((Test-Path $Path) -AND $NoClobber) {
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Test-Path], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAl lowed,Microsoft.PowerShell.Commands.TestPathCommand
VERBOSE: Testing C:\Scripts\log\ exists
Out-File : Cannot bind argument to parameter 'FilePath' because it is an empty string.
At C:\scripts\Function-Write-Log.ps1:110 char:67
+ ... FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Out-File], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAl lowed,Microsoft.PowerShell.Commands.OutFileCommand
VERBOSE: Logfile is: C:\Scripts\log\kh_tca_out_20210222.log
Start of my Script
Function Upload-TCA
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$false)]
[Alias('StartDate')]
[Alias('SeedDate')]
[DateTime]$dateSeed = (Get-Date),
#[string]$dateSeedStr = (Get-Date -format "yyyy-MM-dd"),
[Parameter(Mandatory=$false)]
[Alias('DisableFtp')]
[bool]$ftpDisabled=$false,
[Parameter(Mandatory=$false)]
[Alias('DisplayConfig')]
[bool]$emitConfig = $false,
[Parameter(Mandatory=$true)]
[string]$orgCode
)
#########################################################################
# Function Imports
#########################################################################
. c:\scripts\Function-Generate-Folder.ps1
. c:\scripts\Function-Write-Log.ps1
. c:\scripts\Function-Upload-WinSCP-FTPS.ps1
. c:\scripts\Function-Create-CredentialFromFile.ps1
#########################################################################
# Logging Setup
#########################################################################
$logDir = "C:\Scripts\log\"
Generate-Folder $logDir
$logName = "$orgCode_tca_out_$(Get-Date -Format "yyyyMMdd").log"
$logFile = Join-Path -Path $logDir -ChildPath $logName
Write-Log "Logfile is: $logFile" -Path $logFile
Write-Log
The Write-Log function is from the Microsoft Script Center Included here in case the link is broken
<#
.Synopsis
Write-Log writes a message to a specified log file with the current time stamp.
.DESCRIPTION
The Write-Log function is designed to add logging capability to other scripts.
In addition to writing output and/or verbose you can write to a log file for
later debugging.
.NOTES
Created by: Jason Wasser #wasserja
Modified: 11/24/2015 09:30:19 AM
Changelog:
* Code simplification and clarification - thanks to #juneb_get_help
* Added documentation.
* Renamed LogPath parameter to Path to keep it standard - thanks to #JeffHicks
* Revised the Force switch to work as it should - thanks to #JeffHicks
To Do:
* Add error handling if trying to create a log file in a inaccessible location.
* Add ability to write $Message to $Verbose or $Error pipelines to eliminate
duplicates.
.PARAMETER Message
Message is the content that you wish to add to the log file.
.PARAMETER Path
The path to the log file to which you would like to write. By default the function will
create the path and file if it does not exist.
.PARAMETER Level
Specify the criticality of the log information being written to the log (i.e. Error, Warning, Informational)
.PARAMETER NoClobber
Use NoClobber if you do not wish to overwrite an existing file.
.EXAMPLE
Write-Log -Message 'Log message'
Writes the message to c:\Logs\PowerShellLog.log.
.EXAMPLE
Write-Log -Message 'Restarting Server.' -Path c:\Logs\Scriptoutput.log
Writes the content to the specified log file and creates the path and file specified.
.EXAMPLE
Write-Log -Message 'Folder does not exist.' -Path c:\Logs\Script.log -Level Error
Writes the message to the specified log file as an error message, and writes the message to the error pipeline.
.LINK
https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
#>
function Write-Log
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[Alias("LogContent")]
[string]$Message,
[Parameter(Mandatory=$false)]
[Alias('LogPath')]
[string]$Path='C:\Logs\PowerShellLog.log',
[Parameter(Mandatory=$false)]
[ValidateSet("Error","Warn","Info")]
[string]$Level="Info",
[Parameter(Mandatory=$false)]
[switch]$NoClobber
)
Begin
{
# Set VerbosePreference to Continue so that verbose messages are displayed.
$VerbosePreference = 'Continue'
}
Process
{
# If the file already exists and NoClobber was specified, do not write to the log.
if ((Test-Path $Path) -AND $NoClobber) {
Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name."
Return
}
# If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
elseif (!(Test-Path $Path)) {
Write-Verbose "Creating $Path."
$NewLogFile = New-Item $Path -Force -ItemType File
}
else {
# Nothing to see here yet.
}
# Format Date for our Log File
$FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
# Write message to error, warning, or verbose pipeline and specify $LevelText
switch ($Level) {
'Error' {
Write-Error $Message
$LevelText = 'ERROR:'
}
'Warn' {
Write-Warning $Message
$LevelText = 'WARNING:'
}
'Info' {
Write-Verbose $Message
$LevelText = 'INFO:'
}
}
# Write log entry to $Path
"$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
}
End
{
}
}
I hadn't read the Generate-Folder method correctly. I changed the calls in there for Write-Log to Write-Host. It was trying to use the log before it was fully setup.
Function Generate-Folder()
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string]$path='C:\scripts\logs'
)
Write-Host "Testing $path exists" # cant use write-log as not setup yet
$global:foldPath=$null
foreach($foldername in $path.split("\"))
{
$global:foldPath+=($foldername+"\")
if(!(Test-Path $global:foldPath))
{
New-Item -ItemType Directory -Path $global:foldPath
Write-Host "$global:foldPath Folder Created Successfully" #-Path $logFile
}
}
}

How to Log Errors in Powershell?

[1] I am trying to create Error logs for my PowerShell script. I need to create a function so that instead of using Write-Host I can directly call that function and whatever the error I am getting in the Script can be directly logged into the Log File.
[2] I have used the following method but it doesn't seem that will work for every PowerShell script.
function WriteLog
{
Param ([string]$LogString)
$LogFile = "C:\$(gc env:computername).log"
$DateTime = "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date)
$LogMessage = "$Datetime $LogString"
Add-content $LogFile -value $LogMessage
}
WriteLog "This is my log message"
[3] Can Anyone Suggest a more easy way to handle logging of Error into the file?
You are not far off for your use case.
You don't need Write-Host at all for this at all. Depending on what PS version you are using, Write-Host is not prudent when you are sending data down the pipe or elsewhere
Write-Host is just bad in legacy, pre-v5x versions if you were sending stuff in the pipeline because it cleared the buffer, so they'd be noting to send. In v5x
and higher. As per the founder/creator of Monad/PowerShell.
• Write-Host Harmful
https://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/
https://devblogs.microsoft.com/scripting/understanding-streams-redirection-and-write-host-in-powershell
https://powershell.org/2012/06/how-to-use-write-host-without-endangering-puppies-or-a-manifesto-for-modularizing-powershell-scripts
PowerShell Best Practice #3: Avoid Write-Host
it now writes to the Information stream, as per the founder/creator of Monad/Powershell.
• However, this thought has been changed since the v5x stuff.
You can use direct logging using either
Tee-Object
Export-Csv -Append
Out-File -Append
... and other redirection options.
You can also, create and write to your own event log...
New-EventLog -LogName LogonScripts -Source ClientScripts
Write-EventLog LogonScripts -Source ClientScripts -Message 'Test Message' -EventId 1234 -EntryType Warning
...then read from it later as you would any other event log.
There are lots of examples (scripts and modules) all of the web to use as is and start with or tweak as needed, even the ones via the Microsoft powershellgallery.com.
ScriptLogger 2.0.0
Write-Log: A Simple Logging Function for your PowerShell - Scripts: Download - Write-Log.ps1:
Write-Log PowerShell Logging Function: Download - Function-Write-Log.ps1:
Logging 2.4.9
Point of note in your post. This...
$LogFile = "C:\$(gc env:computername).log"
... just simplify to this...
$LogFile = "C:\$($Env:COMPUTERNAME).log"
#set logfile-path on global scope
$Logfile = "C:\Temp\Logfile.log" # set your Logfile-Path here
function Write-Log {
param
(
[Parameter(ValueFromPipeline)]
[string]$content
)
$FileExists = Test-Path -Path $LogFile
$DateNow = Get-Date -Format 'dd.MM.yyyy HH:mm'
$FileInp = $DateNow + ' | ' + $content      
if ($FileExists -eq $True){
Add-Content -Path $LogFile -Value $FileInp 
}
else {
New-Item -Path $Logfile -ItemType file
Add-Content -Path $LogFile -Value $FileInp
}
}
#then you only have to pipe it to Write-log like this:
Write-Output "hello world" | Write-Log

How to I apply the Write-Log function to a script

I have the latest Write-Log function from this module installed and wondered if it's possible to apply that function here rather than and or while using the verbose part of the transcript.
I want to now add in a try..catch where if it fails it using the Write-Log function to insert a record into the log file
$varfullpath = "C:\Users\Simon.Evans\Documents\ReferenceData__logfile.txt"
Start-Transcript -Path $varfullpath -Append
Write-Output $varfullpath
[string]$sourceDirectory = "C:\Users\Simon.Evans\Documents\Source Data\LNAT\Code_Mapping.txt"
[string]$destinationDirectory = "I:\Dev\BI\Projects\Powershell\Test Area\Source Data\LNAT\Code_Mapping.txt"
Copy-Item -Force -Verbose $sourceDirectory -Destination $destinationDirectory
Stop-Transcript
For info this is the Write-Log function installed which still not sussed how it works given I've never used functions.
<#
.Synopsis
Write-Log writes a message to a specified log file with the current time stamp.
.DESCRIPTION
The Write-Log function is designed to add logging capability to other scripts.
In addition to writing output and/or verbose you can write to a log file for
later debugging.
.NOTES
Created by: Jason Wasser #wasserja
Modified: 11/24/2015 09:30:19 AM
Changelog:
* Code simplification and clarification - thanks to #juneb_get_help
* Added documentation.
* Renamed LogPath parameter to Path to keep it standard - thanks to #JeffHicks
* Revised the Force switch to work as it should - thanks to #JeffHicks
To Do:
* Add error handling if trying to create a log file in a inaccessible location.
* Add ability to write $Message to $Verbose or $Error pipelines to eliminate
duplicates.
.PARAMETER Message
Message is the content that you wish to add to the log file.
.PARAMETER Path
The path to the log file to which you would like to write. By default the function will
create the path and file if it does not exist.
.PARAMETER Level
Specify the criticality of the log information being written to the log (i.e. Error, Warning, Informational)
.PARAMETER NoClobber
Use NoClobber if you do not wish to overwrite an existing file.
.EXAMPLE
Write-Log -Message 'Log message'
Writes the message to c:\Logs\PowerShellLog.log.
.EXAMPLE
Write-Log -Message 'Restarting Server.' -Path c:\Logs\Scriptoutput.log
Writes the content to the specified log file and creates the path and file specified.
.EXAMPLE
Write-Log -Message 'Folder does not exist.' -Path c:\Logs\Script.log -Level Error
Writes the message to the specified log file as an error message, and writes the message to the error pipeline.
.LINK
https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
#>
function Write-Log
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[Alias("LogContent")]
[string]$Message,
[Parameter(Mandatory=$false)]
[Alias('LogPath')]
[string]$Path='C:\Logs\PowerShellLog.log',
[Parameter(Mandatory=$false)]
[ValidateSet("Error","Warn","Info")]
[string]$Level="Info",
[Parameter(Mandatory=$false)]
[switch]$NoClobber
)
Begin
{
# Set VerbosePreference to Continue so that verbose messages are displayed.
$VerbosePreference = 'Continue'
}
Process
{
# If the file already exists and NoClobber was specified, do not write to the log.
if ((Test-Path $Path) -AND $NoClobber) {
Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name."
Return
}
# If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
elseif (!(Test-Path $Path)) {
Write-Verbose "Creating $Path."
$NewLogFile = New-Item $Path -Force -ItemType File
}
else {
# Nothing to see here yet.
}
# Format Date for our Log File
$FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
# Write message to error, warning, or verbose pipeline and specify $LevelText
switch ($Level) {
'Error' {
Write-Error $Message
$LevelText = 'ERROR:'
}
'Warn' {
Write-Warning $Message
$LevelText = 'WARNING:'
}
'Info' {
Write-Verbose $Message
$LevelText = 'INFO:'
}
}
# Write log entry to $Path
"$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
}
End
{
}
}
Thanks for the input chaps finally got there in the end but made a few silly errors on the way.
$varfullpath = "C:\Users\Simon.Evans\Documents\ReferenceData__logfile.txt"
$sourceDirectory = "C:\Users\Simon.Evans\Documents\Source Data\LNAT\Code_Maping.txt"
$destinationDirectory = "I:\Dev\BI\Projects\Powershell\Test Area\Source Data\LNAT\Code_Mapping.txt"
try{
Copy-item -Force -Verbose $sourceDirectory -Destination $destinationDirectory -ErrorAction Stop
Write-log -Message "Copy from $sourceDirectory to $destinationDirectory suceeded" -path $varfullpath
}
catch{
$Error[0] | Write-Log -path $varfullpath ##Example Error 1
Write-log -Message "Copy from $sourceDirectory to $destinationDirectory Failed" -Level Error -path $varfullpath ##Example Error 2
}

Powershell Log file

Firstly I want to point out that I'm a PowerShell ScrapBooker and not really terribly knowledgeable about PowerShell.
I've been working on a script that installs BGInfo ... I have the actual install and uninstall working perfectly, and now I'm moving on to getting the logging sorted out nicely.
I found this article "Create Log File in Powershell" which was wonderful and have incorporated this Function into my script.
Function Write-Log {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$False)]
[ValidateSet("LABEL","INFO","WARN","ERROR","FATAL","DEBUG")]
[String]
$Level = "INFO",
[Parameter(Mandatory=$True)]
[string]
$Message,
[Parameter(Mandatory=$False)]
[string]
$logfile
)
$Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss")
IF ($Level -eq "LABEL") {
$Line = "$Message"
}
ELSE {
$Line = "$Stamp $Level $Message"
}
If($logfile) {
Add-Content $logfile -Value $Line
}
Else {
Write-Output $Line
}
}
What I need to know is how to use this to log output from commands.
For Example:
In My script I have this command:
New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run' -Name BgInfo -Value """$InstPath\Bginfo.exe"" $InstPath\$BGTemplateFile $InstOptions" -PropertyType 'String' -Force
or this one:
Copy $SourcePath\Bginfo.exe $InstPath
What I would like to know is how I can use my function to capture ANY output from that command and log it in my log file.
I guess that I would also like to use this information and apply it to any other commands where I want to log something.
Hopefully that's all clear and makes sense and that someone can help me out.
Cheers,
Dave.
:)
I believe you are referring to this function:
function Write-Log
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[Alias("LogContent")]
[string]$Message,
[Parameter(Mandatory=$false)]
[Alias('LogPath')]
[string]$Path='C:\Logs\PowerShellLog.log',
[Parameter(Mandatory=$false)]
[ValidateSet("Error","Warn","Info")]
[string]$Level="Info",
[Parameter(Mandatory=$false)]
[switch]$NoClobber
)
Begin
{
# Set VerbosePreference to Continue so that verbose messages are displayed.
$VerbosePreference = 'Continue'
}
Process
{
# If the file already exists and NoClobber was specified, do not write to the log.
if ((Test-Path $Path) -AND $NoClobber) {
Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name."
Return
}
# If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
elseif (!(Test-Path $Path)) {
Write-Verbose "Creating $Path."
$NewLogFile = New-Item $Path -Force -ItemType File
}
else {
# Nothing to see here yet.
}
# Format Date for our Log File
$FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
# Write message to error, warning, or verbose pipeline and specify $LevelText
switch ($Level) {
'Error' {
Write-Error $Message
$LevelText = 'ERROR:'
}
'Warn' {
Write-Warning $Message
$LevelText = 'WARNING:'
}
'Info' {
Write-Verbose $Message
$LevelText = 'INFO:'
}
}
# Write log entry to $Path
"$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
}
End
{
}
}
Usage:
Write-Log -Message 'My log Error Message' -Path c:\Logs\Script.log -Level Error
Write-Log -Message 'My log Warning message' -Level Warn
Note: By default its storing in C:\logs\PowershellLog.log. You can change it on the fly during using also and you can hardcode it in function itself.
Hope it helps.