In the event you’re writing PowerShell scripts that do something significant, you want logging. Whether or not you’re deploying software program, managing companies, or automating duties, having a report of what your script did (or didn’t do) is essential. On this tutorial, you’ll learn to create a easy however efficient PowerShell logging operate.
Conditions
In the event you’d wish to comply with together with this tutorial, make certain you have got:
- Home windows 10 or Home windows Server with PowerShell 5.1 or PowerShell 7+
- A textual content editor (VSCode advisable)
- Fundamental understanding of PowerShell capabilities
The Drawback with Fundamental Logging
Let’s say you’re writing a script to silently set up some software program. The essential method may look one thing like this:
Add-Content material -Path "C:Scriptsset up.log" -Worth "Beginning set up..." Begin-Course of -FilePath 'installer.exe' -ArgumentList '/i /s' -Wait -NoNewWindow Add-Content material -Path "C:Scriptsset up.log" -Worth "Completed set up."
This works, however it has some points:
- No timestamps
- Repetitive code
- Inconsistent logging format
- Laborious-coded log path
Let’s repair these issues by constructing a correct logging operate.
Constructing a Fundamental Write-Log Operate
First, let’s create a easy operate that provides timestamps to our log entries:
operate Write-Log { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$Message ) $timeGenerated = Get-Date -Format HH:mm:ss Add-Content material -Path "C:Scriptsscript.log" -Worth "$timeGenerated - $Message" }
Now you should use it like this:
Write-Log -Message "Beginning set up..." Begin-Course of -FilePath 'installer.exe' -ArgumentList '/i /s' -Wait -NoNewWindow Write-Log -Message "Completed set up."
The log file (C:Scriptsscript.log) will comprise entries that appear like:
09:42:15 - Beginning set up... 09:43:22 - Completed set up.
A lot cleaner! However we will do higher.
Including Extra Performance
Let’s improve our logging operate with some helpful options:
- Customized log paths
- Completely different log ranges (Information, Warning, Error)
- Date in filename
- Error dealing with
Right here’s the improved model:
operate Write-Log { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Message, [Parameter()] [ValidateNotNullOrEmpty()] [string]$LogFilePath = "C:ScriptsLogs", [Parameter()] [ValidateSet('Information','Warning','Error')] [string]$Stage = "Data" ) # Create the log listing if it would not exist if (!(Take a look at-Path $LogFilePath)) Out-Null # Construct the log file path with date $date = Get-Date -Format "MM-dd-yyyy" $logFile = Be part of-Path $LogFilePath "log-$date.txt" # Get the present timestamp $timeStamp = Get-Date -Format "HH:mm:ss" # Create the log entry $logEntry = "$timeStamp [$Level] - $Message" attempt { Add-Content material -Path $logFile -Worth $logEntry -ErrorAction Cease } catch { Write-Error "Failed to write down to log file: $_" } }
This enhanced model provides you far more flexibility. Right here’s find out how to use it:
# Fundamental info logging Write-Log -Message "Beginning software program set up" # Warning a couple of non-critical problem Write-Log -Message "Config file not discovered, utilizing defaults" -Stage Warning # Log an error Write-Log -Message "Set up failed!" -Stage Error # Use a customized log path Write-Log -Message "Customized path log" -LogFilePath "D:CustomLogs"
The ensuing log file (log-03-12-2024.txt) will appear like this:
10:15:22 [Information] - Beginning software program set up 10:15:23 [Warning] - Config file not discovered, utilizing defaults 10:15:25 [Error] - Set up failed!
And in D:CustomLogslog-03-12-2024.txt:
10:15:26 [Information] - Customized path log
Discover how every entry contains the timestamp, log degree in brackets, and the message. This structured format makes it straightforward to parse logs and rapidly determine points.
Actual-World Instance: Software program Set up Script
Let’s put our logging operate to work in an actual script that installs software program silently:
# First, dot-source the logging operate . .Write-Log.ps1 # Script variables $installer = "C:Installerssoftware program.exe" $logPath = "C:ScriptsInstallLogs" # Begin logging Write-Log -Message "Starting set up course of" -LogFilePath $logPath # Examine if installer exists if (Take a look at-Path $installer) { Write-Log -Message "Discovered installer at: $installer" attempt { # Try set up Write-Log -Message "Beginning set up..." $course of = Begin-Course of -FilePath $installer -ArgumentList '/i /s' -Wait -NoNewWindow -PassThru # Examine the exit code if ($course of.ExitCode -eq 0) { Write-Log -Message "Set up accomplished efficiently" } else { Write-Log -Message "Set up failed with exit code: $($course of.ExitCode)" -Stage Error } } catch { Write-Log -Message "Set up failed with error: $_" -Stage Error } } else { Write-Log -Message "Installer not discovered at: $installer" -Stage Error } Write-Log -Message "Set up script accomplished"
The ensuing log file will look one thing like this:
09:15:22 [Information] - Starting set up course of 09:15:22 [Information] - Discovered installer at: C:Installerssoftware program.exe 09:15:22 [Information] - Beginning set up... 09:16:45 [Information] - Set up accomplished efficiently 09:16:45 [Information] - Set up script accomplished
Helpful Suggestions
Listed here are some finest practices when utilizing this logging operate:
-
All the time log the beginning and finish of your script – This helps monitor script execution time and completion standing.
-
Use applicable log ranges – Don’t mark the whole lot as an error; use the appropriate degree for the scenario:
- Data: Regular operations
- Warning: Non-critical points
- Error: Essential issues that want consideration
-
Embrace related particulars – Log sufficient info to grasp what occurred:
# Dangerous Write-Log "Failed to attach" # Good Write-Log "Failed to connect with server 'SQL01' - timeout after 30 seconds" -Stage Error
-
Clear up previous logs – Contemplate including log rotation to stop filling up disk house:
# Delete logs older than 30 days Get-ChildItem -Path $LogFilePath -Filter "*.txt" | The place-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } | Take away-Merchandise
Conclusion
A great logging operate is crucial for any severe PowerShell script. With the Write-Log
operate we’ve constructed, you now have a versatile and reusable approach so as to add correct logging to all of your scripts. Keep in mind to adapt the operate to your particular wants – you may wish to add options like:
Log Rotation
operate Write-Log { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Message, [Parameter()] [int]$MaxLogFiles = 30 # Preserve final 30 days of logs ) # Take away previous log recordsdata Get-ChildItem -Path $LogFilePath -Filter "*.txt" | The place-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$MaxLogFiles) } | Take away-Merchandise -Power # Proceed with regular logging... }
Completely different Output Codecs (CSV, JSON)
operate Write-Log { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Message, [Parameter()] [ValidateSet('TXT','CSV','JSON')] [string]$Format="TXT" ) $logEntry = [PSCustomObject]@{ Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" Stage = $Stage Message = $Message } swap ($Format) { 'CSV' Export-Csv -Path "$LogFilePathlog.csv" -Append -NoTypeInformation 'JSON' Add-Content material -Path "$LogFilePathlog.json" 'TXT' Add-Content material -Path "$LogFilePathlog.txt" } }
Community Path Assist
operate Write-Log { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Message, [Parameter()] [string]$NetworkPath = "\serverlogs" ) # Take a look at community path connectivity if (!(Take a look at-Path $NetworkPath)) { # Fallback to native logging if community is unavailable $NetworkPath = "C:ScriptsLogs" Write-Warning "Community path unavailable. Utilizing native path: $NetworkPath" } # Proceed with regular logging... }
E mail Notifications for Errors
operate Write-Log { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Message, [Parameter()] [string]$SmtpServer = "smtp.firm.com", [Parameter()] [string[]]$NotifyOnError = "[email protected]" ) # Regular logging first... # Ship electronic mail if that is an error if ($Stage -eq 'Error' -and $NotifyOnError) { $emailParams = @{ From = "[email protected]" To = $NotifyOnError Topic = "PowerShell Script Error" Physique = "Error occurred at $timeStamp`n`nMessage: $Message" SmtpServer = $SmtpServer } attempt { Ship-MailMessage @emailParams } catch { Write-Warning "Didn't ship error notification: $_" } } }
The secret’s to start out with a strong basis and construct up from there based mostly in your particular wants. These examples ought to provide you with start line for extending the essential logging operate with extra superior options.
For instance, you may mix a number of of those options right into a single, complete logging resolution:
Write-Log -Message "Essential error in fee processing" ` -Stage Error ` -Format CSV ` -NetworkPath "\serverlogs" ` -NotifyOnError "[email protected]","[email protected]" ` -MaxLogFiles 90
This could:
- Log the error in CSV format
- Retailer it on a community share
- E mail a number of recipients
- Keep 90 days of log historical past
Keep in mind to check totally, particularly when implementing community paths or electronic mail notifications, as these exterior dependencies can have an effect on your script’s reliability. Completely happy scripting!