PowerShell ErrorRecord.CategoryInfo.Activity(类别活动)

PowerShell ErrorRecord.CategoryInfo.Activity (CategoryActivity)

因此,考虑到有四种*方式报告错误(不包括 $ErrorActionPreferencetry/catchtrap 块的影响,因此 PowerShell 错误报告看起来像一团糟这有可能改变另一个来源报告错误的方式),差异很小且记录不充分,更糟糕的是没有关于何时使用不同类型的真正说明。 (我知道这会因情况而异,但“标准”是什么——其他人会期望我做什么?)

但这并不是这里的重点。我决定尽可能使用最简单的选项,因为这是我希望大多数其他人会使用的选项,所以至少我们会有一致性 - 但我要么没有正确理解某些内容,要么它已损坏。

根据PowerShell documentationErrorCategoryInfoclass的Activity属性(观察CategoryInfo属性 System.Management.Automation.ErrorRecord) 应该是“遇到错误的操作的文本描述”。考虑到该值是可设置的并且(在编译的 cmdlet 的情况下)指示 cmdlet 的名称这一事实,除非另有说明,我希望基于脚本的错误报告方法具有相同的功能,但我做错了什么或者我在 PowerShell 中发现了一个自 PowerShell 1.0 以来一直存在的错误。

考虑以下示例:

using namespace System.Management.Automation;
function Invoke-Error1 {
    [CmdletBinding()]
    param() 
    process {
        $ex = [InvalidOperationException]::new()
        $er = [ErrorRecord]::new($ex, 'OperationTest', [ErrorCategory]::InvalidOperation, 'MyErrorTarget')
        $er.CategoryInfo.Activity = 'MyActivity'
        Write-Error -ErrorRecord $er
    }
}

调用此函数将导致以下错误。

invoke-error1 : Operation is not valid due to the current state of the object.
At line:1 char:1
+ invoke-error1
+ ~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (MyErrorTarget:String) [Write-Error], InvalidOperationException
    + FullyQualifiedErrorId : OperationTest,Invoke-Error1

进一步调查时,您会注意到 CategoryInfo.Activity 属性 已被 Write-Error 取代。我设置的值(“MyActivity”)被忽略。如果这是预料之中的,我不会期望 Write-Error 方法上的参数来设置错误类别 - 但似乎存在一个。 (请参阅 Get-Help Write-Error -Parameter CategoryActivity。我看不出这可能打算做的任何其他事情。)

这可以通过以下函数确认。

function Invoke-Error2 {
    [CmdletBinding()]
    param() 
    process {
        $Params = @{
            Exception         = [InvalidOperationException]::new()
            ErrorId           = 'OperationTest'
            Category          = [ErrorCategory]::InvalidOperation
            TargetObject      = 'MyErrorTarget'
            CategoryActivity  = 'MyActivity'
        }
        Write-Error @Params
    }
}

除了 cmdlet 的名称和关联的 InvocationInfo 之外,报告的错误在所有方面都与 Invoke-Error1 中的错误相同。具体来说,再次注意 $Error[0].CategoryInfo.Activity -eq 'Write-Error' 而不是“MyActivity”。

然而,有一种写错误的方法确实正确地写了错误activity错误...但它似乎是最低的在报告错误的推荐方法列表中,因为它 通常 与 Write-Error 相同。

function Invoke-Error3 {
    [CmdletBinding()]
    param()
    process {
    $ex = [InvalidOperationException]::new()
        $er = [ErrorRecord]::new($ex, 'OperationTest', [ErrorCategory]::InvalidOperation, 'MyErrorTarget')
        $er.CategoryInfo.Activity = 'MyActivity'
        $PSCmdlet.WriteError($er)
    }
}

除了我使用 $PSCmdlet.WriteError 而不是 Write-Error 之外,此函数在所有方面都与 Invoke-Error1 相同。根据 certain community standards,首选使用内置函数而不是 .NET 调用,当然,当行为不同时,使用产生所需结果的过程是有意义的。

这是与任何已编译的 cmdlet 写入错误的方式最相似的方法。因此,cmdlet 写入的任何错误都将使用手动设置的 Activity 值,或使用 cmdlet 名称的默认值。我相信后者在某种程度上是 Write-Error.

正在发生的事情

话虽这么说,但我不觉得我在这里遗漏了什么。 CategoryInfo 属性 和 Write-Error 的关联 CategoryActivity 参数自 PowerShell 1.0 以来就已存在。当然,如果此功能从未起作用,现在有人会注意到吗?那我做错了什么?我将如何正确报告错误?回到 ErrorCategoryInfo Activity 属性 的目的,“Write-Error”不是遇到错误的操作 - 我的函数确实如此,我想报告它 使用 Write-Error。这可能吗?


*如果您不知道,可以从 PowerShell 脚本或脚本函数报告的错误如下:

首先,感谢您的深入分析。

您已经找到了一种有效的变通方法 ,它也适用于 Windows PowerShell,使您的自定义 .Activity 值得到尊重:使用 $PSCmdlet.WriteError() - though note that $PSCmdlet is only available in advanced functions 和脚本。

如果 .ActivitySystem.Management.Automation.ErrorRecord instance passed in full to Write-Error's -ErrorRecord parameter, whereas it now is if you use the -CategoryActivity parameter (shorter alias: -Activity) in PowerShell (Core)7+, is arguably a bug that I encourage you to report at the PowerShell GitHub repo 的一部分,则 不会受到尊重。

注意:正如您所指出的,以下替代解决方法 仅在 PowerShell(核心)7+ 中有效:

稍微不太晦涩的解决方法不需要$PSCmdlet,因此也可用于-高级函数和脚本:

using namespace System.Management.Automation

$ex = [InvalidOperationException]::new()
$er = [ErrorRecord]::new($ex, 'OperationTest', [ErrorCategory]::InvalidOperation, 'MyErrorTarget')
$er.CategoryInfo.Activity = 'MyActivity'

# Workaround: Specify the activity *also* via -Activity
Write-Error -ErrorRecord $er -Activity $er.CategoryInfo.Activity

注意:要查看生成的错误(记录)的友好表示,请将上面的内容通过管道传输到
2>&1 | Format-List -Force;在 PowerShell (Core) 7+ 中,您也可以使用专用的 Get-Error cmdlet。

或者,您可以完全绕过 [ErrorRecord] 显式 构造 并使用不同参数的组合来实现同样的结果:

Write-Error -Exception InvalidOperationException `
            -ErrorId OperationTest `
            -Activity MyActivity `
            -TargetObject MyErrorTarget

I've found a bug in PowerShell that has pervaded since PowerShell 1.0.

看起来像。

最可能的解释是 直接 构造 [ErrorRecord] 在 PowerShell 代码 中需要高级知识并且很少见 - 事实上, Write-Error 为您构造这样一个实例 是它的主要目的,提供带有 参数 的 cmdlet 熟悉的命令行体验,如上面最后一个解决方法所示。

另一个因素可能是处理错误记录的调用者很少关注.CategoryInfo.Activity等字段;通常,考虑的是 .ErrorId 字段或包装异常的 .NET 类型。
从更哲学的角度来看,有人可能会争辩说 ErrorRecord 类型有点过度设计。


至于你更一般的观察:

there's no real instruction on when to use the different types [of errors]

  • GitHub 文档问题 Our Error Handing, Ourselves - time to fully understand and properly document PowerShell's error handling 旨在概述 PowerShell 异常复杂且部分不一致的错误处理,并要求此类概述成为文档的正式部分。

  • This answer 尝试就何时使用 非终止statement-terminating[=114] 提供指导=]错误。

    • 两种类型的终止错误-脚本-终止(致命) vs. 语句-终止,PowerShell代码中的throw只会创建,而 .ThrowTerminatingError()(通常仅在 C# 代码中使用)只会造成后者是一种令人困惑的不对称性 - this comment on GitHub issue #14819 使得放弃这种区别而支持仅提供 script-未来 PowerShell 版本中的终止错误 - 即唯一一种终止错误将是默认情况下的 fatal。这样做会:
      • 防止脚本中的更多命令默认执行,这似乎是必要的,因为当前的语句终止错误通常是严重错误条件。
      • 消除 common -ErrorAction parameter having no effect on statement-terminating errors, whereas preference variable $ErrorActionPreference 之间令人困惑的不对称性,当设置为 'Stop' 时,确实(将它们提升为 script-终止错误)。