XCLogParser

Build Status License

XCLogParser 是一个命令行工具,用于解析 Xcode 和 xcodebuild 使用的 SLF 序列化格式,该格式用于存储其构建和测试日志 (xcactivitylog 文件)。

您可以在这里找到有关日志中使用格式的更多信息。您也可以查看 Erick Camacho 在 AltConf 2019 上的演讲

该工具支持创建不同类型的报告来分析日志的内容。 XCLogParser 可以提供关于项目中每个模块和文件的构建时间警告错误单元测试结果的大量见解。

这是一个从 Kickstarter iOS 开源应用程序的构建日志创建的报告示例。

kickstarter build report

如何和为什么

XCLogParser 是一个使用 SPM 编写的可执行文件,它支持三个命令

  1. Dumpxcactivitylog 的内容转储到 JSON 文档中。
  2. Parsexcactivitylog 的内容解析成不同类型的报告 (jsonflatJsonsummaryJsonchromeTracerissueshtml)。
  3. Dump 将 LogStoreManifest.plist 文件的 Manifest 内容转储到 JSON 文档中。

根据您的需求,有多种使用场景可以使用 XCLogParser 帮助您

安装

您可以使用命令 rake build[debug]rake build[release] 编译可执行文件,或者直接使用 Swift Package Manager 命令。 您还可以运行 rake install 将可执行文件安装到您的 /usr/local/bin 目录中。

Homebrew

$ brew install xclogparser

我们目前正在努力添加更多安装选项。

Xcode 集成

您可以使用 post-scheme 构建操作来自动化 xcactivitylog 文件的解析。 这样,一旦构建完成,就可以立即解析最新的构建日志。 为此,请在项目中打开 scheme 编辑器,然后展开左侧的“Build”面板。 然后,您可以添加一个新的“Post-action”运行脚本,并使用所需的参数调用 xclogparser 可执行文件

xclogparser parse --project MyApp --reporter html

此脚本假定 xclogparser 可执行文件已安装并在您的 PATH 中。

运行脚本由 Xcode 在临时目录中执行,因此您可能会发现立即使用 open MyAppLogs 在脚本末尾打开生成的输出非常有用。 构建完成后,Finder 将自动打开输出文件夹,然后您可以查看生成的 HTML 页面,其中包含您构建过程的精美可视化! ✨

提示和技巧

  1. 在 post-action 运行脚本中抛出的错误会被忽略,因此可能很难注意到简单的错误。
  2. 自 Xcode 11 起,只有在存在选项 -resultBundlePath 时,xcodebuild 才会生成 .xcactivitylog 构建日志。 如果您使用该命令而不是 Xcode 进行编译,请务必将该选项设置为以 .xcresult 结尾的有效路径。
  3. Xcode 喜欢等待所有子进程退出后再完成构建。 因此,如果您的 post-scheme 操作执行时间过长,您可能会注意到延迟的“Build Succeeded”消息。 您可以通过将执行卸载到后台的另一个脚本并立即关闭输入、输出和错误流来解决此问题,以便让 Xcode 和 xcodebuild 干净地完成。 创建以下 launcher 脚本,并从您的 post-scheme 操作中按如下方式调用它:launcher command-that-parses-the-log-here
    #!/bin/sh
    
    # The first argument is the directory of the executable you want to run.
    # The following arguments are directly forwarded to the executable.
    # We execute the command in the background and immediately close the input, output
    # and error streams in order to let Xcode and xcodebuild finish cleanly.
    # This is done to prevent Xcode and xcodebuild being stuck in waiting for all
    # subprocesses to end before exiting.
    executable=$1
    shift;
    $executable "$@" <&- >&- 2>&- &
  4. 如果构建失败,则不会执行 post-scheme 操作。 Xcode 中一个未公开的功能允许您即使在这种情况下也可以执行它。 在 scheme 的 BuildAction 中将属性 runPostActionsOnFailure 设置为 YES,如下所示
    <BuildAction buildImplicitDependencies='YES' parallelizeBuildables='YES' runPostActionsOnFailure='YES'>

日志类型

构建日志

xcactivitylog 文件由 Xcode/xcodebuild 在构建完成后几秒钟创建。 日志位于 DerivedData/YourProjectName-UUID/Logs/Build 目录中。 它是使用 gzip 压缩的 SLF 格式的二进制文件。

在同一目录中,您将找到一个 LogStoreManifest.plist 文件,其中包含为项目生成的所有 xcactivitylog 文件的列表。 可以监视此文件,以便在每次新日志准备就绪时收到通知。

测试日志

测试日志在 DerivedData/YourProjectName-UUID/Logs/Test 目录中创建。 Xcode 和 xcodebuild 创建不同的日志。 您可以在这篇 博客文章中找到关于创建哪些日志的很好的描述。

功能

Dump 命令

xcactivitylog 文件的全部内容转储为 JSON 文档。 如果您想要一个原始但易于解析的日志表示形式,您可以使用此命令。

例子

xclogparser dump --file path/to/log.xcactivitylog --output activity.json
xclogparser dump --project MyProject --output activity.json --redacted

为了简洁起见,已省略示例输出,因为它可能包含有关构建的大量信息。

可用参数
参数名 描述 必需
--file xcactivitylog 的路径。 否 *
--project 如果您不知道日志的路径,则为项目名称。 该工具将尝试在 DerivedData 目录中以该名称开头的文件夹中查找最新的构建日志。 使用 --strictProjectName 进行更严格的名称匹配。 否 *
--workspace 如果您不知道日志的路径,则为 xcworkspace 文件的路径。 它将使用 Xcode 的哈希算法为 DerivedData 文件夹中的项目生成文件夹名称,并将尝试在该目录中找到最新的构建日志。 否 *
--xcodeproj 如果您不知道日志的路径,并且项目没有 xcworkspace 文件,则为 xcodeproj 文件的路径。 它将使用 Xcode 的哈希算法为 DerivedData 文件夹中的项目生成文件夹名称,并将尝试在该目录中找到最新的构建日志。 否 *
--derived_data 如果您使用 xcodebuild 构建项目时使用了 -derivedDataPath 选项,则为 derived data 文件夹的路径。
--output 如果指定,JSON 文件将写入给定的路径。 如果未定义,该命令将输出到标准输出。
--redacted 如果指定,用户名将在日志中包含的文件路径中被替换为单词 redacted。 出于隐私原因很有用,但会稍微降低性能。
--without_build_specific_info 如果指定,则将从日志中删除特定于构建的信息(例如,DerivedData/Product-bolnckhlbzxpxoeyfujluasoupft/Build 中的 bolnckhlbzxpxoeyfujluasoupft 将被删除)。 用于按其内容对日志进行分组非常有用。
--strictProjectName --project 结合使用。 如果指定,则将对项目名称进行更严格的名称匹配。

否 *: 必须提供 --file--project--workspace--xcodeproj 参数之一。

Parse 命令

xcactivitylog 解析构建信息,并将其转换为不同的表示形式,例如 JSON 文件扁平 JSON 文件摘要 JSON 文件问题 JSON 文件Chrome Tracer 文件或静态 HTML 页面

如果将某些标志传递给 Xcode/xcodebuild,此命令支持解析其他数据

  1. swiftc 报告的编译时间。 要使用该功能,您需要使用选项 -Xfrontend -debug-time-expression-type-checking-Xfrontend -debug-time-function-bodies 构建项目。
  2. ld64 的统计信息输出。 可以通过将 -Xlinker -print_statistics 添加到 Xcode 的“Other Linker Flags”来生成统计信息,这对于跟踪链接时间回归非常有用。
  3. Clang 的时间跟踪数据。 指定 -ftime-trace 标志后,clang 将为每个转换单元生成一个 json 跟踪文件,XCLogParser 将收集它们并将数据添加到解析器输出。

例子

xclogparser parse --project MyApp --reporter json --output build.json
xclogparser parse --file /path/to/log.xcactivitylog --reporter chromeTracer
xclogparser parse --workspace /path/to/MyApp.xcworkspace --derived_data /path/to/custom/DerivedData --reporter html --redacted

示例输出可在 reporters 部分中找到。

可用参数
参数名 描述 必需
--reporter 用于转换日志的 reporter。 它可以是 jsonflatJsonsummaryJsonchromeTracerissueshtml。(必需)
--file xcactivitylog 的路径。 否 *
--project 如果您不知道日志的路径,则为项目名称。 该工具将尝试在 DerivedData 目录中以该名称开头的文件夹中查找最新的构建日志。 使用 --strictProjectName 进行更严格的名称匹配。 否 *
--workspace 如果您不知道日志的路径,则为 xcworkspace 文件的路径。 它将使用 Xcode 的哈希算法为 DerivedData 文件夹中的项目生成文件夹名称,并将尝试在该目录中找到最新的构建日志。 否 *
--xcodeproj 如果您不知道日志的路径,并且项目没有 xcworkspace 文件,则为 xcodeproj 文件的路径。 它将使用 Xcode 的哈希算法为 DerivedData 文件夹中的项目生成文件夹名称,并将尝试在该目录中找到最新的构建日志。 否 *
--derived_data 如果您使用 xcodebuild 构建项目时使用了 -derivedDataPath 选项,则为 derived data 文件夹的路径。
--output 如果指定,JSON 文件将写入给定的路径。 如果未定义,该命令将输出到标准输出。
--rootOutput 如果指定,HTML 文件将写入给定的文件夹,如果该文件夹不存在,则它优先于 output,并且将被创建。 它适用于相对主目录路径 ~
--redacted 如果指定,用户名将在日志中包含的文件路径中被替换为单词 redacted。 出于隐私原因很有用,但会稍微降低性能。
--without_build_specific_info 如果指定,则将从日志中删除特定于构建的信息(例如,DerivedData/Product-bolnckhlbzxpxoeyfujluasoupft/Build 中的 bolnckhlbzxpxoeyfujluasoupft 将被删除)。 用于按其内容对日志进行分组非常有用。
--strictProjectName --project 结合使用。 如果指定,则将对项目名称进行更严格的名称匹配。
--machine_name 如果指定,计算机名称将用于创建 buildIdentifier。 如果未指定,将使用主机名。
--omit_warnings 省略最终报告中的警告详细信息。 如果警告太多并且报告的大小太大,这将非常有用。
--omit_notes 省略最终报告中的注释详细信息。 如果注释太多并且报告的大小太大,这将非常有用。
--trunc_large_issues 如果单个任务有超过 100 个问题(警告、注释、错误),则将其截断为 100 个。这对于减少使用的内存量很有用。

否 *: 必须提供 --file--project--workspace--xcodeproj 参数之一。

Manifest 命令

以 JSON 格式输出 LogStoreManifest.plist 的内容,该文件列出了为项目生成的所有 xcactivitylog 文件。

例子

xclogparser manifest --project MyApp

示例输出

{
  "scheme" : "MyApp",
  "timestampEnd" : 1548337458,
  "fileName" : "D6539DED-8AC8-4508-9841-46606D0C794A.xcactivitylog",
  "title" : "Build MyApp",
  "duration" : 46,
  "timestampStart" : 1548337412,
  "uniqueIdentifier" : "D6539DED-8AC8-4508-9841-46606D0C794A",
  "type" : "xcode"
}
可用参数
参数名 描述 必需
--log_manifest 现有 LogStoreManifest.plist 的路径。 否 *
--project 如果您不知道日志的路径,则为项目名称。 该工具将尝试在 DerivedData 目录中以该名称开头的文件夹中查找最新的构建日志。 使用 --strictProjectName 进行更严格的名称匹配。 否 *
--workspace 如果您不知道日志的路径,则为 xcworkspace 文件的路径。 它将使用 Xcode 的哈希算法为 DerivedData 文件夹中的项目生成文件夹名称,并将尝试在该目录中找到最新的构建日志。 否 *
--xcodeproj 如果您不知道日志的路径,并且项目没有 xcworkspace 文件,则为 xcodeproj 文件的路径。 它将使用 Xcode 的哈希算法为 DerivedData 文件夹中的项目生成文件夹名称,并将尝试在该目录中找到最新的构建日志。 否 *
--derived_data 如果您使用 xcodebuild 构建项目时使用了 -derivedDataPath 选项,则为 derived data 文件夹的路径。
--output 如果指定,JSON 文件将写入给定的路径。 如果未定义,该命令将输出到标准输出。
--strictProjectName --project 结合使用。 如果指定,则将对项目名称进行更严格的名称匹配。

否 *: 必须提供 --log-manifest--project--workspace--xcodeproj 参数之一。

Reporters

parse 命令 内置了不同类型的 reporter,可以表示和可视化日志数据

JSON Reporter

此 reporter 解析日志并将其输出为 JSON。 它包含有关构建中每个步骤的持续时间的信息,以及其他元数据和有趣的信息,例如错误和警告。

例子

xclogparser parse --project MyApp --reporter json
示例输出
{
    "detailStepType" : "swiftCompilation",
    "startTimestamp" : 1545143336.649699,
    "endTimestamp" : 1545143336.649699,
    "schema" : "MyApp",
    "domain" : "com.apple.dt.IDE.BuildLogSection",
    "parentIdentifier" : "095709ba230e4eda80ab43be3b68f99c_1545299644.4805899_20",
    "endDate" : "2018-12-18T14:28:56.650000+0000",
    "title" : "Compile \/Users\/<redacted>\/projects\/MyApp\/Libraries\/Utilities\/Sources\/Disposables\/Cancelable.swift",
    "identifier" : "095709ba230e4eda80ab43be3b68f99c_1545299644.4805899_185",
    "signature" : "CompileSwift normal x86_64 \/Users\/<redacted>\/MyApp\/Libraries\/Utilities\/Sources\/Disposables\/Cancelable.swift",
    "type" : "detail",
    "buildStatus" : "succeeded",
    "subSteps" : [

    ],
    "startDate" : "2018-12-18T14:28:56.650000+0000",
    "buildIdentifier" : "095709ba230e4eda80ab43be3b68f99c_1545299644.4805899",
    "machineName" : "095709ba230e4eda80ab43be3b68f99c",
    "duration" : 5.5941859483718872,
    "errors" : "",
    "warnings" : "",
    "errorCount" : 0,
    "warningCount" : 0,
    "errors" : [],
    "warnings" : [],
    "swiftFunctionTimes" : [
      {
        "durationMS" : 0.08,
        "occurrences" : 5,
        "startingColumn" : 36,
        "startingLine" : 48,
        "file" : "file:\/\/\/Users\/<redacted>\/MyApp\/Libraries\/Utilities\/Sources\/Disposables\/Cancelable.swift",
        "signature" : "getter description"
      }
    ],
    "swiftTypeCheckTimes" : [
      {
        "durationMS" : 0.5,
        "occurrences" : 2,
        "startingColumn" : 16,
        "startingLine" : 9,
        "file" : "file:\/\/\/Users\/<redacted>\/MyApp\/Libraries\/Utilities\/Sources\/Disposables\/Cancelable.swift",
      }
    ]
}

有关每个字段的更多信息,请查看 JSON 格式文档

FlatJson Reporter

将日志解析为 JSON 对象数组,没有嵌套步骤(字段 subSteps 始终为空)。 将数据转储到数据库中以便于分析非常有用。

数组中 JSON 对象的格式与 json reporter 中使用的格式相同。

例子

xclogparser parse --file path/to/log.xcactivitylog --reporter flatJson
示例输出
[
  {
    "parentIdentifier" : "",
    "title" : "Build MobiusCore",
    "warningCount" : 0,
    "duration" : 0,
    "startTimestamp" : 1558590748,
    "signature" : "Build MobiusCore",
    "endDate" : "2019-05-23T05:52:28.274000Z",
    "errorCount" : 0,
    "domain" : "Xcode.IDEActivityLogDomainType.BuildLog",
    "type" : "main",
    "identifier" : "68a2bbd0048a454d91b3734b5d5dc45e_1558640253_1",
    "buildStatus" : "succeeded",
    "schema" : "MobiusCore",
    "subSteps" : [

    ],
    "endTimestamp" : 1558590748,
    "architecture" : "",
    "machineName" : "68a2bbd0048a454d91b3734b5d5dc45e",
    "buildIdentifier" : "68a2bbd0048a454d91b3734b5d5dc45e_1558640253",
    "startDate" : "2019-05-23T05:52:28.244000Z",
    "documentURL" : "",
    "detailStepType" : "none"
  },
  {
    "parentIdentifier" : "68a2bbd0048a454d91b3734b5d5dc45e_1558640253_1",
    "title" : "Prepare build",
    "warningCount" : 0,
    "duration" : 0,
    "startTimestamp" : 1558590748,
    "signature" : "Prepare build",
    "endDate" : "2019-05-23T05:52:28.261000Z",
    "errorCount" : 0,
    "domain" : "Xcode.IDEActivityLogDomainType.XCBuild.Preparation",
    "type" : "target",
    "identifier" : "68a2bbd0048a454d91b3734b5d5dc45e_1558640253_2",
    "buildStatus" : "succeeded",
    "schema" : "MobiusCore",
    "subSteps" : [

    ],
    "endTimestamp" : 1558590748,
    "architecture" : "",
    "machineName" : "68a2bbd0048a454d91b3734b5d5dc45e",
    "buildIdentifier" : "68a2bbd0048a454d91b3734b5d5dc45e_1558640253",
    "startDate" : "2019-05-23T05:52:28.254000Z",
    "documentURL" : "",
    "detailStepType" : "none"
  },{
    "parentIdentifier" : "68a2bbd0048a454d91b3734b5d5dc45e_1558640253_1",
    "title" : "Build target MobiusCore",
    "warningCount" : 0,
    "duration" : 4,
    "startTimestamp" : 1558590708,
    "signature" : "MobiusCore-fmrwijcuutzbrmbgantlsfqxegcg",
    "endDate" : "2019-05-23T05:51:51.890000Z",
    "errorCount" : 0,
    "domain" : "Xcode.IDEActivityLogDomainType.target.product-type.framework",
    "type" : "target",
    "identifier" : "68a2bbd0048a454d91b3734b5d5dc45e_1558640253_3",
    "buildStatus" : "succeeded",
    "schema" : "MobiusCore",
    "subSteps" : [

    ],
    "endTimestamp" : 1558590712,
    "architecture" : "",
    "machineName" : "68a2bbd0048a454d91b3734b5d5dc45e",
    "buildIdentifier" : "68a2bbd0048a454d91b3734b5d5dc45e_1558640253",
    "startDate" : "2019-05-23T05:51:48.206000Z",
    "documentURL" : "",
    "detailStepType" : "none"
  },
  ...
]

有关每个字段的更多信息,请查看 JSON 格式文档

SummaryJson Reporter

将日志解析为 JSON 对象,没有嵌套步骤(字段 subSteps 始终为空)。 获取构建的高级摘要非常有用。

例子

xclogparser parse --file path/to/log.xcactivitylog --reporter summaryJson
示例输出
  {
    "parentIdentifier" : "",
    "title" : "Build MobiusCore",
    "warningCount" : 0,
    "duration" : 0,
    "startTimestamp" : 1558590748,
    "signature" : "Build MobiusCore",
    "endDate" : "2019-05-23T05:52:28.274000Z",
    "errorCount" : 0,
    "domain" : "Xcode.IDEActivityLogDomainType.BuildLog",
    "type" : "main",
    "identifier" : "68a2bbd0048a454d91b3734b5d5dc45e_1558640253_1",
    "buildStatus" : "succeeded",
    "schema" : "MobiusCore",
    "subSteps" : [

    ],
    "endTimestamp" : 1558590748,
    "architecture" : "",
    "machineName" : "68a2bbd0048a454d91b3734b5d5dc45e",
    "buildIdentifier" : "68a2bbd0048a454d91b3734b5d5dc45e_1558640253",
    "startDate" : "2019-05-23T05:52:28.244000Z",
    "documentURL" : "",
    "detailStepType" : "none"
  }

有关每个字段的更多信息,请查看 JSON 格式文档

ChromeTracer Reporter

以 Chrome tracer 使用的格式将 xcactivitylog 解析为 JSON 对象数组。 您可以使用此 JSON 在 Chrome 中的 Chrome 跟踪工具中可视化构建时间:chrome://tracing

例子

xclogparser parse --file path/to/log.xcactivitylog --reporter chromeTracer
示例输出

Issues Reporter

将日志中找到的错误和警告列表输出为 JSON 文档。 当您只想检查构建时发现的问题时,这非常有用。

例子

xclogparser parse --file path/to/log.xcactivitylog --reporter issues
示例输出
```json
{
  "errors" : [
    {
      "characterRangeStart" : 0,
      "startingColumnNumber" : 5,
      "endingColumnNumber" : 30,
      "characterRangeEnd" : 18446744073709551615,
      "title" : "use of undeclared type 'AType'",
      "endingLineNumber" : 10,
      "type" : "swiftError",
      "documentURL" : "file:\/\/\/MyProject\/MyFile.swift",
      "startingLineNumber" : 10,
      "severity" : 1
      "detail": "\/MyProject\/MyFile.swift:10:5: error: use of undeclared type 'AType'\r    func doSomething(completion: @escaping () -> AType) -> void) {\r^~~~\r"
    }
  ],
  "warnings" : [
    {
      "characterRangeStart" : 0,
      "startingColumnNumber" : 5,
      "endingColumnNumber" : 30,
      "characterRangeEnd" : 18446744073709551615,
      "title" : "Warning",
      "endingLineNumber" : 10,
      "type" : "swiftWarning",
      "documentURL" : "file:\/\/\/MyProject\/MyFile.swift",
      "startingLineNumber" : 10,
      "severity" : 1
    }
  ]
}
```

HTML Reporter

生成 HTML 报告,以可视化每个模块和文件的构建时间,以及警告和错误消息。

例子

xclogparser parse --file path/to/log.xcactivitylog --reporter html --output build/reports
示例输出

要求和兼容性

环境 版本
🛠 Xcode 13.0
🐦 语言 Swift 5.5

状态

XCLogParser 目前处于 alpha 状态。我们正在内部使用它,并在各种项目上进行了测试,但我们需要社区的帮助,以便在更多的 iOS 和 Mac 应用程序上进行测试和改进。

开发和贡献

MacOS

  1. 使用 git clone git@github.com:MobileNativeFoundation/XCLogParser.git 克隆存储库。
  2. 运行 rake gen_resources 以生成编译应用程序所需的静态资源 Swift 文件。
  3. 运行 swift package generate-xcodeproj 以生成 Xcode 项目(或使用任何文本编辑器)。
  4. 直接在 Xcode 中运行测试 (CMD + U) 或使用 rake test
  5. 创建 issue 并讨论可能的解决方案或改进。
  6. 创建 PR。

Linux

  1. 提供了一个 Dockerfile,您可以使用标签 xlogparser 创建一个镜像: docker build --tag xclogparser .
  2. 要在 Linux 中编译应用程序,只需运行 shell 脚本:./run-in-docker.sh

如果您发现错误或想提出改进建议,欢迎您创建一个 issue

发布

  1. 确保 Version.swift 中设置的版本已更新
  2. 在 GitHub 中创建一个新的发布标签,标签格式如 vx.x.x。提供发布标题和描述。
  3. 构建发布工件
    1. 对于 macOS,使用 DEVELOPER_DIR=<path_to_xcode_version> rake archive 构建一个工件。使用与 要求 部分匹配的 Xcode 版本。
    2. 对于 Linux,运行 ./build_release_in_docker.sh
  4. 将发布工件上传到发布版本摘要
    1. 对于 macOS,上传放置在 releases/XCLogParser-x.x.x.zip 中的 zip 文件
    2. 对于 Linux,更新文件名中的当前版本并上传 releases/linux/XCLogParser-x.x.x-Linux.zip

行为准则

此项目遵循 开放行为准则。 参与者应遵守此准则。