internal/functions/Write-AzOpsMessage.ps1
function Write-AzOpsMessage { <# .SYNOPSIS Wrapper function to emit logs with Write-PSFMessage and ApplicationInsights. .PARAMETER ApplicationInsights Boolean to indicate if function should emit logs to ApplicationInsights. .PARAMETER Data Additional data points. .PARAMETER ErrorRecord Additional exception information from catch. .PARAMETER LogLevel Set level of message severity. .PARAMETER LogString String used to construct message. .PARAMETER LogStringValues String array used to enrich to message. .PARAMETER Metric Used to output metric. .PARAMETER MetricName Override FunctionName as MetricName. .PARAMETER FunctionName Static set FunctionName in log, otherwise Write-AzOpsMessage collects this from callStack. .PARAMETER ModuleName Static set ModuleName in log, otherwise Write-AzOpsMessage collects this from callStack. .PARAMETER Target The object that was processed when invoked. .EXAMPLE Write-AzOpsMessage -LogLevel Verbose #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [bool] $ApplicationInsights = (Get-PSFConfigValue -FullName 'AzOps.Core.ApplicationInsights'), [Parameter(Mandatory = $false)] [hashtable] $Data, [Parameter(Mandatory = $false)] [System.Management.Automation.ErrorRecord] $ErrorRecord, [Parameter(Mandatory = $true)] [ValidateSet("Critical", "Debug", "Error", "Host", "Important", "InternalComment", "Output", "Significant", "SomewhatVerbose", "System", "Verbose", "VeryVerbose", "Warning")] [string] $LogLevel, [Parameter(Mandatory = $true)] [string] $LogString, [Parameter(Mandatory = $false)] [string[]] $LogStringValues, [Parameter(Mandatory = $false)] [int] $Metric, [Parameter(Mandatory = $false)] [string] $MetricName, [Parameter(Mandatory = $false)] [string] $FunctionName, [Parameter(Mandatory = $false)] [string] $ModuleName, [Parameter(Mandatory = $false)] [string] $Target ) begin { # Collect callStack information to enrich logs with accurate caller information $callStack = (Get-PSCallStack)[1] if (-not $FunctionName) { $FunctionName = $callStack.Command } if (-not $ModuleName) { $ModuleName = $callstack.InvocationInfo.MyCommand.ModuleName } if (-not $ModuleName) { $ModuleName = "<Unknown>" } $File = $callStack.Position.File $Line = $callStack.Position.StartLineNumber } process { # Evaluate message verbosity $logLevels = @{ "Critical" = 1 "Debug" = 8 "Error" = 667 "Host" = 2 "Important" = 2 "InternalComment" = 9 "Output" = 2 "Significant" = 3 "SomewhatVerbose" = 6 "System" = 7 "Verbose" = 5 "VeryVerbose" = 4 "Warning" = 666 } $intLevel = $logLevels[$LogLevel] [int]$messageInfo = Get-PSFConfigValue -FullName 'PSFramework.Message.Info.Maximum' if (($messageInfo -lt $intLevel) -or ([PSFramework.Message.MessageHost]::MinimumInformation -gt $intLevel) -and ($intLevel -notin 666..667)) { # Message is below desired log verbosity, skip return } # Generate unique logTag information, used to identify each log entry $logTag = (New-Guid).Guid + '-' + (Get-Date).TimeOfDay.TotalMilliseconds # Pass information to Write-PSFMessage, to emit local log $params = @{ Level = $LogLevel String = $LogString StringValues = $LogStringValues Tag = $logTag ModuleName = $ModuleName FunctionName = $FunctionName File = $File Line = $Line Target = $Target ErrorRecord = $ErrorRecord Data = $Data } Write-PSFMessage @params if ($env:APPLICATIONINSIGHTS_CONNECTION_STRING -ne '' -and $ApplicationInsights -eq $true) { # Initiate export of log to ApplicationInsights try { # Gather log generated by Write-PSFMessage with retry/backoff logic $logMessage = Invoke-AzOpsScriptBlock -ArgumentList $FunctionName, $LogTag -ScriptBlock { Get-PSFMessage -FunctionName $FunctionName | Where-Object { $_.Tags -eq $LogTag } -ErrorAction Stop } -RetryCount 5 -RetryWait 1 -RetryType Exponential -ErrorAction Stop } catch { Write-PSFMessage -Level Warning -Message 'Get-PSFMessage failing: {0}' -StringValues $_ } if (-not $logMessage) { # No log message, return return } if ($logMessage.Count -gt 1) { # Log message has duplicate, return Write-PSFMessage -Level Warning -Message 'Get-PSFMessage has duplicate entires for {0} with tag {1}' -StringValues $LogString, $logTag return } # Construct ApplicationInsights object $azOpsMessage = [Microsoft.ApplicationInsights.TelemetryClient]::new() # Set ApplicationInsights connectionstring $azOpsMessage.TelemetryConfiguration.ConnectionString = $env:APPLICATIONINSIGHTS_CONNECTIONSTRING $azOpsMessage.Context.Session.Id = $PID # Adjust logMessage.Level to align with ApplicationInsights switch ($logMessage.Level) { { ($_ -eq "SomewhatVerbose") -or ($_ -eq "System") -or ($_ -eq "Debug") -or ($_ -eq "InternalComment") } { $level = "Verbose" break } { ($_ -eq "Important") -or ($_ -eq "Output") -or ($_ -eq "Host") -or ($_ -eq "Significant") -or ($_ -eq "VeryVerbose") } { $level = "Information" break } default { $level = ($logMessage.Level).ToString() break } } # Create ApplicationInsights customDimensions $logProperties = [System.Collections.Generic.Dictionary[string, string]]::new() $logProperties.Add("Timestamp", $logMessage.Timestamp) $logProperties.Add("ModuleName", $logMessage.ModuleName) $logProperties.Add("FunctionName", $logMessage.FunctionName) $logProperties.Add("TargetObject", $logMessage.TargetObject) $logProperties.Add("Data", $logmessage.Data) $logProperties.Add("Runspace", $logMessage.Runspace) $logProperties.Add("ComputerName", $logMessage.ComputerName) $logProperties.Add("File", $logMessage.File) $logProperties.Add("Line", $logMessage.Line) $logProperties.Add("CallStack", $logMessage.CallStack) $logProperties.Add("ErrorRecord", $logMessage.ErrorRecord) $logProperties.Add("String", $logMessage.String) # Create TrackTrace $azOpsMessage.TrackTrace($logMessage.Message, $level, $logProperties) # Create TrackEvent $azOpsMessage.TrackEvent($logMessage.FunctionName) if ($Metric) { # Create TrackMetric if (-not $MetricName) { $azOpsMessage.TrackMetric($logMessage.FunctionName, $Metric) } else { $azOpsMessage.TrackMetric($MetricName, $Metric) } } if ($level -eq "Critical" -or $level -eq "Error" -or $level -eq "Warning") { # Create TrackException $azOpsMessage.TrackException($logMessage.Message, $logProperties) } # Immediately emit log to ApplicationInsights $azOpsMessage.Flush() } } } |