如何以编程方式在我的日志中报告 TFS/AzureDevOps 变更集编号

How do I programmatically report the TFS/AzureDevOps changeset number in my logs

我有时会发现自己通过变更集进行二进制搜索并 运行 宁测试以确定引入缺陷的时间。这给我留下了很多非常相似的日志来筛选,有时我很难记住哪个诊断文件来自哪个变更集运行。

我想以某种方式在诊断文件中包含变更集编号。有什么方法可以用 Visual Studio/C#/AzureDevOps?

Microsoft 曾经支持一个名为 BuildInfo.config 的文件,该文件对这类事情很有用,但我认为在 Visual Studio 2015 年之后不再支持它。

基本上,这是一个由 MSBuild 创建的简单 XML 文件。当与应用程序二进制文件一起部署时,日志记录框架可以读取文件并将详细信息包含在日志输出中。

您可以通过多种方式自己创建类似的功能。这就是我要做的...

  1. 创建一个空的构建信息文件并将其与您的源代码一起检入。该文件只是一个参考示例——使用占位符数据。它可以是 XML、JSON 或任何其他格式(但可能应该是基于文本的)。将文件的 build action 设置为 "Content,",以便它将包含在构建输出中。
  2. 使用您的日志记录框架的模板或遥测初始化功能来读取构建信息文件并将其内容包含在日志输出中。确切的实现将取决于您的日志记录框架。
  3. 将 Powershell 脚本任务添加到 Azure DevOps 管道。该脚本应该更新或覆盖构建信息文件。您可以从 build variables 获得一些有用的信息。该任务应 运行 在 MSBuild 编译步骤之前。

创建并解析 BuildInfo 文件

对于这个例子,我将实现我的构建信息文件很简单 key/value 文本文件中的对。我选择了简单的文本,因为它提供了一个清晰的示例 不需要任何第三方库或复杂的解析。在你的应用程序中,你 可能想使用 JSON、XML 或其他更标准化的东西。

首先,创建一个占位符构建信息文件。该文件将用于您的 本地开发环境,并将作为构建信息模式的参考。 我叫我的BuildInfo.txt,放在我项目的根目录下。

COMMIT=commit not set
BUILD=build not set

接下来,编写一个帮助程序来解析构建信息文件。我的很简陋。它 添加一些防御措施以防止丢失或格式错误的构建信息是明智的,但是 我选择省略任何防御性代码以保持示例的重点。

class BuildInfo
{
    // Singleton instance backing field
    static readonly Lazy<BuildInfo> instance = new Lazy<BuildInfo>(() => new BuildInfo());

    // Singleton instance public accessor
    public static BuildInfo Instance => instance.Value;

    public string Commit { get; }
    public string Build { get; }

    private BuildInfo()
    {
        // This is a very rudimentary example of parsing the info file. It
        // will fail loudly on malformed input. Consider:
        //   1) Using a standard file format (JSON, XML). I rolled my own
        //      here to avoid adding a dependency on a parsing library.
        //   2) If you want your app to fail when no build info is
        //      available, add a more descriptive exception. If you don't
        //      want it to fail, add some devensive code or fallback logic.
        var info = File.ReadAllLines("BuildInfo.txt")
            .Select(l => l.Split('=', 2))
            .ToDictionary(key => key[0], val => val[1]);

        Commit = info["COMMIT"];
        Build = info["BUILD"];
    }
}

将构建信息添加到您的日志输出

我这里使用的是log4net,但是任何日志框架都应该有一些类似的 自定义日志输出的机制。查阅框架的文档 更多信息。

首先,将您的构建信息添加到日志记录上下文中。这应该去某个地方 您应用的启动代码——越早越好。

log4net.GlobalContext.Properties["Build"] = BuildInfo.Instance.Build;
log4net.GlobalContext.Properties["Commit"] = BuildInfo.Instance.Commit;

然后,更新您的日志附加程序的配置以包含自定义字段。 这是控制台附加程序的示例配置。重要的一点 是 %property{BuildId}%property{Commit}.

<?xml version="1.0" encoding="utf-8" ?>
<log4net>
    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
        <target value="Console.Error" />
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%-5level [%property{BuildId}] [%property{Commit}] - %message%newline" />
        </layout>
    </appender>
  <root>
    <level value="ALL"/>
    <appender-ref ref="ConsoleAppender" />
  </root>
</log4net>

现在,当您调用 log.Warn("Some log mesage") 时,您会看到以下内容 控制台输出:

WARN  [build not set] [commit not set] - Some log mesage

从 CI

获取构建详细信息

最后,您需要从 CI 环境中获取真实的构建详细信息。我做了 这是一个非常简单的 PowerShell 脚本任务。确保任务 运行s 之前 您的构建步骤!

@(
    "COMMIT=$($Env:BUILD_SOURCEVERSION)",
    "BUILD=$($Env:BUILD_BUILDID)"
) | Out-File BuildInfo.txt

(提示:您可以在 PowerShell 任务中看到 运行ning Get-ChildItem Env: | Sort Name 可用的所有环境变量)

(另一个提示:如果您想使用 JSON 而不是文本,请查看 ConvertTo-Json cmdlet)

现在,如果所有部分都已到位,CI 服务器应该覆盖 签入构建信息文件。新文件应该与您的 可部署的工件,并复制到您的服务器。在启动时,您的应用应该 阅读构建信息,然后该信息应包含在每条日志消息中。

在您的构建和部署之间有很多小事情需要安排 过程,因此请准备好进行一些试验和错误。 CI/CD 设置往往是 乏味,以我的经验。