I am trying to add some logging into my script. Any advice would be much appreciated. To start out- I'd like to add an error log when something goes amiss.
For instance when a user cannot be found the following error throws:
The operation couldn't be performed because object 'asdfa' couldn't be found on 'HQ-DC-6.domain.com'. + CategoryInfo : NotSpecified: (0:Int32) [Set-RemoteMailbox], ManagementObjectNotFoundException + FullyQualifiedErrorId : 47285FC7,Microsoft.Exchange.Management.RecipientTasks.SetRemoteMailbox + PSComputerName : hq-cas2.domain.com
==============================
$users = ForEach ($user in $(Get-Content 'C:\Users\test\Documents\Powershell Scripts\OffboardUsers.txt')) {
$tmpname = Get-RemoteMailbox -Identity $user | Select-Object -ExpandProperty Name
$tmpDisplayName = Get-RemoteMailbox -Identity $user | Select-Object -ExpandProperty DisplayName
Set-RemoteMailbox -Identity $user -Name ("_" + "$tmpname") >> error.log
Set-RemoteMailbox -Identity $user -DisplayName ("_" + "$tmpDisplayName") >> error.log
Set-RemoteMailbox -Identity $user -HiddenFromAddressListsEnabled $true >> error.log
}
This article is completely revised 2017-07-18 as the new
Log-Entry
solution replaces the formerWrite-Log
solution which will not be further updated. See also: Migrating from Write-Log.In general, I find that logging is underestimated for Microsoft scripting languages. Not only at design time of a script (or cmdlet) logging comes in handy but when the script gets deployed and something goes wrong you often wish that you had much better logging in place.
That's why I think that scripting languages as PowerShell (as well as its predecessor VBScript) should actually come with a more sophisticated native logging capabilities than what is available now.
Best practice
Even before PowerShell existed, I had a similar need for a adequate logging function in VBScript. As a matter of fact, some of the concepts I was using for VBScript, I am still using in PowerShell. Meanwhile, I have extended my logging solution with a whole list of improvements and requirements as I expect a log function to be:
Robust and never cause the actual cmdlet to fail unexpectedly (even
when e.g. the access to the log file is for some reason denied)
Simple to invoke and possibly be used as a
Write-Host
command replacementResolve all data types and reveal the content
Capture unexpected native script errors
Capable to pass-through objects for inline logging to minimize additional code lines
Have an accurate (10ms) timestamp per entry for performance trouble
shooting
Standard capturing troubleshooting information like:
Script version
PowerShell version
When it was ran (process start time)
How (parameters) and from where (location) it was ran
Appended append information to a configurable log file which doesn't grow indefinitely
Downwards compatible with PowerShell version 2
Robust
If you want to go for a robust logging solution, you probably want to go with the native Start-Transcript cmdlet but you will probably find out that the
Start-Transcript
lacks features, like timestamps, that you might expect from a proper logging cmdlet. You could go for a 3rd party solution but this usually means extra installation procedures and dependencies.So you decide to write it yourself but even the simplest solution where you just write information to a file might already cause an issue in the field: the file might not be accessible. It might even exist but your script is triggered twice and multiple instances run at the same time the log file might be open by one of instances and access is denied from the other instance (see e.g.: Powershell Scheduled Tasks conflicts?). And just at this point, logging should actually help you to troubleshoot what is going on as a repetitive trigger might also cause unexpected behavior in the script itself. For this particular example, the solution I present here buffers the output until it is able to write. But there are a lot more traps in writing a logging cmdlet and correctly formatting the output.
Log-Entry
I have put the whole solution in a
Log-Entry.ps1
framework consisting out of a few major parts:Main
function template with a few examplesMy
object that contains some script - and logging definitionsLog-Entry
(aliasLog
) to log information and objectsSet-LogFile
(aliasLogFile
) to set the location of the log fileEnd-Script
(aliasEnd
) which might be used to nicely close the sessionConvertTo-Text
(aliasCText
) to resolve objectsFor the latest
Log-Entry.ps1
version, see: https://github.com/iRon7/Log-Entry.Usage
Download the above
Log-Entry.ps1
framwork and replace the examples in theMain {}
function with your own script. Everywhere you would like to display and log information, use theLog
command (similar to theWrite-Host
command syntax).Run the script and check the log file at:
%Temp%\<ScriptName>.Log
Syntax
For details on the syntax, see: the readme.md at https://github.com/iRon7/Log-Entry
Example
Here are a few commands that show some of the features of the
Log-Entry
framework:Display
The example commands are displayed in the following format:
Log file
The example commands record the following information in the log file:
You can use the Write-Log function. That would be the best way in your case I believe.
This function has been written by Jason Wasser:
Usage:
Note: You can use a get-help for this function always for all the details.
Hope it helps.