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.

项目属性。

启用后,你将开始在“日志”选项卡下的“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 的实际示例以及该 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 函数传递无效的实体名称,可以在评估过程中强制出错(用于测试目的!)。 此时,可更改 TripPinNavTable 函数中的 withData 行,将 [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 选项卡上,应能看到引发的错误文本:

错误消息。

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

错误日志。

另请注意,日志消息的 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);

运行查询时,会收到正确的错误消息,但如果查看“日志”选项卡,则不会显示任何消息。 这是因为在调用 Diagnostics.Trace过程中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.LogValueDiagnostics.Trace 非常类似,可用于输出要计算的值。

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

参数 prefix 会被添加到日志消息中。 你可以用它来确定哪个调用输出了信息。 参数 value 是函数的返回值,也会以 M 值的文本形式写入跟踪中。 例如,如果 value 与具有列 A 和 B 的 table 相等,则日志将包含等效 #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:

访问 url 消息。

架构类型:

“架构”类型消息。

你会看到 schema 参数 type 的序列化版本,而不是在对类型值执行简单 Text.FromValue 时得到的结果(结果为“类型”)。

Diagnostics.LogFailure

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

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

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

作为(差)示例,修改 TripPinNavTable 函数的 withData 行,可再次强制出错:

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

在跟踪中,您可以找到产生的错误信息,其中包含你的 text,以及原始错误信息。

LogFailure 消息。

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

结束语

本节简短(但非常重要!)的课程介绍了如何使用诊断帮助程序函数记录到 Power Query 跟踪文件。 如果使用得当,则这些函数对调试连接器中的问题很有用。

注意

作为连接器开发人员,有责任确保不会将敏感信息或个人身份信息 (PII) 记录到诊断日志中。 还必须注意不要输出过多的跟踪信息,因为这可能会对性能产生负面影响。

后续步骤

TripPin 第 9 部分 - TestConnection