The closest is the Try / Catch / Finally
statement, because it ensure that whatever is in the Finally
statement get executed before returning.
Normally, I would not use this to wrap all my function because of an unverified assumption that it might impact my code performance.
Giant Try / Catch / Finally
(@Mclayton suggestion)
function Get-Stuff() {
try {
Write-Host 'Getting stuff... ' -ForegroundColor Cyan
$Exception = Get-Random -InputObject @($true, $false)
if ($Exception) { throw 'sss'}
return 'Success !'
}
catch {
Write-Host 'oh no .... :)' -ForegroundColor Red
}
Finally {
Write-Verbose 'Logging stuff... ' -Verbose
}
}
Instead I would probably have used a wrapper function.
Wrapper function (@Mathias suggestion)
For a module, you simply do not export the _
prefixed functions and the users will never see the underlying function. (Note: Using advanced function, you can pass down the parameters to the underlying function through splatting $PSBoundParameters
Function Get-Stuff {
[CmdletBinding()]
Param($Value)
$Output = _Get-Stuff @PSBoundParameters
Write-Verbose 'Logging stuff... ' -Verbose
return $Output
}
#Internal function
function _Get-Stuff {
[CmdletBinding()]
Param($Value)
Write-Host 'Getting stuff... ' -ForegroundColor Cyan
if ($Value) { Write-Host $Value }
}
Otherwise, I would either use a function call and / or a scriptblock invoke (useful when you don't want to repeat the code within a function but that the code itself is not used outside of that scope.
Basic way
Otherwise, I would either use a function call and / or a scriptblock invoke (useful when you don't want to repeat the code within a function but that the code itself is not used outside of that scope. You would need to think about calling the logic before each return point... If you wanted to make sure to never miss a spot, then that's where you'd implement some Pester testing and create unit tests for your functions.
Function Get-Stuff() {
$Logging = { Write-Verbose 'Logging stuff... ' -Verbose }
Write-Host 'Getting stuff... ' -ForegroundColor Cyan
&$Logging # you just have to call this bit before returning at each return point.
return 'Something'
}
Bonus
I initially read "events" in your questions and my brain kind of discarded the rest.
Should you want to have a custom logic attached to your function that is definable by the user using your module and / or set of function, you could raise an event in your function.
New-Event -SourceIdentifier 'Get-Stuff_Return' -Sender $null -EventArguments @(([PSCustomObject]@{
'Hello' = 'World'
})) -MessageData $MyArg | Out-Null
That event, which you would trigger before returning (using any of the methods highlighted above), would do nothing by itself but anybody that needed to do something could hook itself into your function by creating an event handler and adding its own custom logic (for logging or otherwise)
Register-EngineEvent -SourceIdentifier "Get-Stuff_Return" -Action {
# Write-Verbose ($Event | gm | Out-String) -Verbose
Write-Verbose @"
Generated at: $($Event.TimeGenerated)
Handling event: $($Event.SourceIdentifier)
Event Id: $($Event.EventIdentifier)
MessageData (Hello): $($Event.MessageData.Hello)
"@ -Verbose
Write-Verbose '---' -Verbose
$Global:test = $Event
Write-Verbose ($Event.SourceArgs[0] | Out-String) -Verbose
} -SupportEvent
# Unregister statement as a reference
# Unregister-Event -SourceIdentifier 'Get-Stuff_Return' -Force
The advantage being that then anybody could optionally bind itself to a function call and do anything he likes.
References
about_Splatting
How to implement event handling in PowerShell with classes
about Functions Advanced
about_Automatic_Variables
about_Try_Catch_Finally