PowerShell ErrorRecord.CategoryInfo.Activity (CategoryActivity)

标签 powershell error-handling standards

因此,考虑到有四种 * 报告错误的方式(不包括 $ErrorActionPreferencetry/catchtrap 块的影响,这些块有可能改变报告错误的方式),因此 PowerShell 错误报告似乎是一团糟由另一个来源提供),其中存在细微且缺乏记录的差异,更糟糕的是,没有关于何时使用不同类型的真正说明。 (我意识到它因情况而异,但“标准”是什么 - 其他人会期待我做什么?)
不过,这并不是真正的重点。我决定尽可能使用最简单的选项,因为我希望大多数其他人都会使用它,所以至少我们会保持一致性 - 但我要么没有正确理解某些东西,要么它坏了。
根据PowerShell documentation , Activity ErrorCategoryInfo 的属性(property)类(在 CategoryInfoSystem.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
    }
}
报告的错误在所有方面都与 Invoke-Error1 中的错误相同除了 cmdlet 的名称和关联的 InvocationInfo。特别是,再次注意 $Error[0].CategoryInfo.Activity -eq 'Write-Error'而不是“我的事件”。
然而,有一种编写错误的方法可以正确地将错误事件写入错误......但它似乎是报告错误的推荐方法列表中最低的,因为它通常与 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)
    }
}
此功能在所有方面都与 Invoke-Error1 相同除了我使用 $PSCmdlet.WriteError而不是 Write-Error .根据 certain community standards ,最好使用内置函数而不是 .NET 调用,当然,当行为不同时,使用产生所需结果的过程是有意义的。
这是与任何已编译的 cmdlet 写入错误的方式最相似的方法。因此,cmdlet 写入的任何错误都将使用手动设置的 Activity值,或使用 cmdlet 名称的默认值。我相信后者是 Write-Error 发生的事情。 .
话虽如此,我不觉得我在这里遗漏了什么。 CategoryInfo属性(property)及相关CategoryActivity Write-Error 的参数自 PowerShell 1.0 起就已存在。当然,如果此功能从未起作用,那么现在有人会注意到吗?那么我做错了什么?我将如何正确报告错误?回到 Activity 的目的ErrorCategoryInfo 的属性(property), "Write-Error"不是遇到错误的操作 - 我的函数做了,我想用 Write-Error 报告它.这可能吗?

*如果您不知道,可以从 PowerShell 脚本或脚本函数报告的错误如下:
  • 脚本终止(通过 throw)
  • 语句终止(通过 $PSCmdlet.ThrowTerminatingError)
  • 非终止错误(通过 Write-Error )
  • 非终止错误(通过 $PSCmdlet.WriteError )。这很难与 Write-Error 区分开来。但行为确实不同 - 请注意这个问题,以及 $ErrorActionPreference 的事实不影响 $PSCmdlet.WriteError 直接发出的错误的行为(尽管 $ErrorActionPreference 会影响从 $PSCmdlet.WriteError 间接发出的错误的行为,如果直接来源是脚本)。这可以通过设置 $ErrorActionPreference 来测试在继续调用 $PSCmdlet.WriteError 的函数中对比 Write-Error .
  • 除了这些方法之外,任何非终止错误都可以通过 $ErrorActionPreference 变成语句终止错误。或 -ErrorAction通用参数。 $?变量可用于错误报告,但根据该变量的结果变化程度,这样做似乎是一种糟糕的方法。 try/catch块可用于捕获错误并通过 $PSItem 访问它们。变量,但在该块中,如果您从模块中定义的函数执行,您可能会或可能不会通过 $Error 看到错误取决于报告错误的命令是否在您的模块中定义。编写捕获异常的渗透可能会产生两个错误添加到 $global:Error变量 - 我希望被忽略的捕获异常将放置在 $global:Error 中如果捕获它的命令是在不同于引发错误的模块和我报告的新错误的模块中定义的。
  • 最佳答案

    首先,感谢您的深入分析。
    您已经找到了一种有效的解决方法,该方法也适用于 Windows PowerShell 以实现您的自定义 .Activity荣誉值:使用 $PSCmdlet.WriteError() - 但请注意 $PSCmdlet仅适用于 advanced functions和脚本。
    那个.Activity如果 value 是 System.Management.Automation.ErrorRecord 的一部分,则不兑现。实例完全传递给 Write-Error -ErrorRecord参数,而现在是如果您使用 -CategoryActivity PowerShell (Core)7+ 中的参数(更短的别名:-Activity)可以说是一个错误,我鼓励您在 PowerShell GitHub repo 报告。 .

  • 更新:谢天谢地,您已经在 GitHub issue #15088 中这样做了。 .

  • 注意:正如您所指出的,以下 替代解决方法 仅在 PowerShell (Core) 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 小命令。
    或者,您可以 绕过 [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尝试提供有关何时使用非终止与语句终止错误的指导。
  • 有两种类型的终止错误 - 脚本终止(致命)与语句终止,以及 throw在 PowerShell 代码中只创建前者,而 .ThrowTerminatingError() (通常仅在 C# 代码中使用)只创建后者是一种令人困惑的不对称 - this comment on GitHub issue #14819在 future 的 PowerShell 版本中,放弃这种区别而只提供脚本终止错误,即唯一一种终止错误将是默认情况下致命的错误。这样做会:
  • 防止脚本中的更多命令在默认情况下执行,这似乎是需要的,因为当前的语句终止错误通常是严重的错误条件。
  • 消除 common -ErrorAction parameter 之间令人困惑的不对称性对语句终止错误没有影响,而 preference variable $ErrorActionPreference ,当设置为 'Stop' , 确实(将它们提升为脚本终止错误)。


  • 关于PowerShell ErrorRecord.CategoryInfo.Activity (CategoryActivity),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66751961/

    相关文章:

    standards - UI 设计帮助/建议

    python - Tensorflow:组织大型网络的公认 "clean"方式是什么?

    windows - 用于验证 IIS 设置的 Powershell 脚本

    powershell - 如何使用 Powershell System.Net.WebClient 和自定义用户代理字符串保存 "browser-initiated download"文件?

    c# - Facebook Graph API-Marketing Insights Edge错误500

    c - 我应该使用 char** argv 还是 char* argv[]?

    powershell - 在powershell中将日期转换为unix时间戳

    powershell - 如何获取我执行的PowerShell脚本的目录?

    php - php-在connection_timeout上返回 header

    error-handling - Rust为调试和 Release模式设置了不同的紧急输出