TripPin 第 8 部分 - 添加诊断

注释

此内容当前引用 Visual Studio 中诊断的旧实现中的内容。 内容将在近期内更新,以涵盖 Visual Studio Code 中的新 Power Query SDK。

此多部分教程介绍如何为 Power Query 创建新的数据源扩展。 本教程旨在按顺序完成 — 每个课程都基于在上一课中创建的连接器上构建,以增量方式向连接器添加新功能。

在本课中,你将:

  • 了解 Diagnostics.Trace 函数
  • 使用诊断帮助程序函数添加跟踪信息以帮助调试连接器

启用诊断

Power Query 用户可以通过选中“选项”下的复选框来启用跟踪日志记录 |诊断

Power Query 选项的屏幕截图,其中选择了“诊断”选项卡并启用了跟踪。

启用后,任何后续查询都会导致 M 引擎向位于固定用户目录中的日志文件发出跟踪信息。

从 Power Query SDK 中运行 M 查询时,会在项目级别启用跟踪。 在项目属性页上,有三个与跟踪相关的设置:

  • 清除日志:设置为 true时,运行查询时将重置/清除日志。 建议将此设置保留为 true

  • 显示引擎跟踪:此设置控制 M 引擎中内置跟踪的输出。 这些跟踪仅适用于 Power Query 团队的成员,因此通常希望将此设置保留为 false

  • 显示用户跟踪:此设置控制连接器输出的跟踪信息。 你想要将此设置设置为 true.

    TripPin 属性页的屏幕截图,其中显示了与跟踪相关的三个设置。

启用后,日志条目将显示在“日志”选项卡下的“M 查询输出”窗口中。

Diagnostics.Trace

Diagnostics.Trace 函数用于将消息写入 M 引擎的跟踪日志。

Diagnostics.Trace = (traceLevel as number, message as text, value as any, optional delayed as nullable logical as any) => ...

重要

M 是一种具有惰性求值的函数式语言。 使用 Diagnostics.Trace时,请记住,仅当表达式是实际计算结果的一部分时,才会调用函数。 本教程稍后可以找到此行为的示例。

参数 traceLevel 可以是下列值之一(降序):

  • TraceLevel.Critical
  • TraceLevel.Error
  • TraceLevel.Warning
  • TraceLevel.Information
  • TraceLevel.Verbose

启用跟踪后,用户可以选择要查看的最大消息级别。 此级别和下面的所有跟踪消息都输出到日志。 例如,如果用户选择“警告”级别,日志中将显示TraceLevel.WarningTraceLevel.ErrorTraceLevel.Critical的跟踪消息。

message 参数是跟踪文件的实际文本输出。 除非在文本中显式包含该参数,否则文本不包含 value 该参数。

参数 value 是函数返回的内容。 当参数 delayed 设置为 true零参数函数时, value 该函数返回要计算的实际值。 如果 delayed 设置为 falsevalue 则为实际值。 在 延迟评估中可以找到此工作原理的示例。

在 TripPin 连接器中使用 Diagnostics.Trace

有关使用 Diagnostics.Trace 以及 delayed 参数影响的实际示例,请更新 TripPin 连接器的 GetSchemaForEntity 函数,以捕获 error 异常:

GetSchemaForEntity = (entity as text) as type =>
    try
        SchemaTable{[Entity=entity]}[Type]
    otherwise
        let
            message = Text.Format("Couldn't find entity: '#{0}'", {entity})
        in
            Diagnostics.Trace(TraceLevel.Error, message, () => error message, true);

通过将无效的实体名称传递给 GetEntity 函数,可以在评估期间(出于测试目的)强制出错。 在此处更改 withData 函数中的 TripPinNavTable 行,替换为 [Name]"DoesNotExist"

TripPinNavTable = (url as text) as table =>
    let
        // Use our schema table as the source of top level items in the navigation tree
        entities = Table.SelectColumns(SchemaTable, {"Entity"}),
        rename = Table.RenameColumns(entities, {{"Entity", "Name"}}),
        // Add Data as a calculated column
        withData = Table.AddColumn(rename, "Data", each GetEntity(url, "DoesNotExist"), type table),
        // Add ItemKind and ItemName as fixed text values
        withItemKind = Table.AddColumn(withData, "ItemKind", each "Table", type text),
        withItemName = Table.AddColumn(withItemKind, "ItemName", each "Table", type text),
        // Indicate that the node should not be expandable
        withIsLeaf = Table.AddColumn(withItemName, "IsLeaf", each true, type logical),
        // Generate the nav table
        navTable = Table.ToNavigationTable(withIsLeaf, {"Name"}, "Name", "Data", "ItemKind", "ItemName", "IsLeaf")
    in
        navTable;

为项目启用跟踪 ,并运行测试查询。 在 Errors 选项卡上,应会看到引发的错误文本:

显示不存在错误的 M 查询输出的屏幕截图。

此外,在 Log 选项卡上,应会看到相同的消息。 如果对 message 参数 value 使用不同的值,则这些值会有所不同。

M 查询输出中日志标记的屏幕截图,其中突出显示了日志消息。

另请注意,Action日志消息的字段包含有关您的扩展程序的名称(数据源类型),在此情况下为Engine/Extension/TripPin。 当涉及多个查询和/或启用系统(混合引擎)跟踪时,此字段能够更便捷地找到与您的扩展相关的信息。

延迟评估

作为参数工作原理 delayed 的示例,需要进行一些修改,然后再次运行查询。

首先,将 delayed 值设置为 false,但保持 value 参数不变。

Diagnostics.Trace(TraceLevel.Error, message, () => error message, false);

运行查询时,您会收到错误“无法将函数类型的值转换为数据类型”,而不是您实际遇到的错误。 这种差异是因为调用现在返回一个 function 值,而不是值本身。

接下来,从 value 参数中删除函数:

Diagnostics.Trace(TraceLevel.Error, message, error message, false);

运行查询时,会收到正确的错误,但如果选中 “日志 ”选项卡,则没有任何消息。 这种差异是因为error在调用期间被触发/评估,因此消息实际上从未输出。

了解参数的影响 delayed 后,请务必在继续作之前将连接器重置回工作状态。

Diagnostics.pqm 中的诊断帮助程序函数

此项目中包含的 Diagnostics.pqm 文件包含许多帮助程序函数,可简化跟踪。 如 上一教程所示,可以在项目中包括此文件(记得将生成作设置为 编译),然后将其加载到连接器文件中。 连接器文件的底部现在应类似于以下代码片段。 请随意浏览本模块提供的各种函数,但在此示例中,你只使用 Diagnostics.LogValueDiagnostics.LogFailure 函数。

// Diagnostics module contains multiple functions. We can take the ones we need.
Diagnostics = Extension.LoadFunction("Diagnostics.pqm");
Diagnostics.LogValue = Diagnostics[LogValue];
Diagnostics.LogFailure = Diagnostics[LogFailure];

Diagnostics.LogValue

函数 Diagnostics.LogValue 非常类似 Diagnostics.Trace,可用于输出要计算的值。

Diagnostics.LogValue = (prefix as text, value as any) as any => ...

参数 prefix 被添加到日志消息的开头。 使用此参数可以找出哪个调用输出消息。 该 value 参数是函数返回的内容,还作为 M 值的文本表示形式写入跟踪。 例如,如果 value 与列 table A 和 B 相等,则日志包含等效 #table 表示形式: #table({"A", "B"}, {{"row1 A", "row1 B"}, {"row2 A", row2 B"}})

注释

将 M 值序列化为文本可能是一项昂贵的作。 请注意要输出到追踪日志的值的潜在大小。

注释

大多数 Power Query 环境会将跟踪消息截断为最大长度。

例如,更新 TripPin.Feed 函数以跟踪传递到该函数中的 urlschema 参数。

TripPin.Feed = (url as text, optional schema as type) as table =>
    let
        _url = Diagnostics.LogValue("Accessing url", url),
        _schema = Diagnostics.LogValue("Schema type", schema),
        //result = GetAllPagesByNextLink(url, schema)
        result = GetAllPagesByNextLink(_url, _schema)
    in
        result;

必须在调用GetAllPagesByNextLink时使用新_url值和_schema值。 如果使用了原始函数参数,那么 Diagnostics.LogValue 的调用实际上不会被评估,因此不会有消息被写入到跟踪中。 函数编程很有趣!

运行查询时,现在应在日志中看到新消息。

访问 URL:

M 查询输出的屏幕截图,显示访问 URL 的信息。

架构类型:

显示架构类型消息的 M 查询输出的屏幕截图。

将显示schema参数的type序列化版本,而不是在类型值上执行简单Text.FromValue时产生的结果(其结果为“type”)。

Diagnostics.LogFailure (诊断日志失败)

Diagnostics.LogFailure 函数可用于封装函数调用,并且仅当函数调用失败(即返回一个 error)时才写入跟踪。

Diagnostics.LogFailure = (text as text, function as function) as any => ...

在内部,Diagnostics.LogFailurefunction 调用中添加一个 try 运算符。 如果调用失败,则 text 该值将写入跟踪,然后再返回原始 error值。 如果function调用成功,结果会直接返回,不会向跟踪中写入任何内容。 由于 M 错误不包含完整堆栈跟踪(也就是说,通常只看到错误的消息),因此当想要确定错误引发的位置时,此函数非常有用。

作为一个不完善的示例,修改withData函数的TripPinNavTable行以再次故意引发错误:

withData = Table.AddColumn(rename, "Data", each Diagnostics.LogFailure("Error in GetEntity", () => GetEntity(url, "DoesNotExist")), type table),

在跟踪中,你可以找到包含text的结果性错误消息以及原始错误信息。

显示日志失败消息的 M 查询输出的屏幕截图。

在继续下一教程之前,请务必将函数重置为工作状态。

结论

本简短(但重要的)课程介绍了如何使用诊断帮助程序函数登录到 Power Query 跟踪文件。 正确使用时,这些函数在调试连接器中的问题非常有用。

注释

作为连接器开发人员,你需要负责确保不会将敏感信息或个人身份信息(PII)记录为诊断日志记录的一部分。 还必须小心,不要输出过多的跟踪信息,因为它可能会对性能产生负面影响。

后续步骤

TripPin 第 9 部分 - TestConnection