Stop using diagnostic verbosity in MSBuild

 
 
  • Gérald Barré

On CI, some projects use MSBuild's diagnostic verbosity to gather more information about the build. While useful for troubleshooting build issues, this verbosity level has some drawbacks. This post explains how to use MSBuild's binary log feature to get the same information more efficiently.

#Diagnostic verbosity vs binary log

The msbuild diagnostic verbosity is the most detailed verbosity level. It outputs a lot of information about the build process, such as the list of all targets executed, the properties and items used, and the tasks invoked. This information is useful when troubleshooting a build issue, but it comes with some drawbacks:

  • Output is hard to read because of the amount of information.
  • The build time is slower because of the amount of information written to the console. The more projects the solution has, the slower the build will be.

To get the same information more efficiently, you can use MSBuild's binary log feature. The binary log is a structured file that captures all information about the build process. You can use the MSBuildStructuredLog tool to read it and access the same information as diagnostic verbosity, in a more readable and efficient way. The binlog has many advantages over diagnostic verbosity:

  • The binlog is a structured file that can be easily read by tools, making it easier to search for information
  • The binlog is much smaller than the diagnostic verbosity output
  • The impact on the build time is much smaller than the diagnostic verbosity
  • The binlog can embed additional files, making it easier to investigate build issues
  • The binlog can be replayed to get the same output as the diagnostic verbosity
  • The binlog format is documented and a NuGet library is available to read binlog files (example)

Here's a small benchmark to compare the build time and the output size of the diagnostic verbosity and the binary log:

Build time (on a modest machine such as the GitHub Hosted runner):

  • Mesaure-Command { dotnet build --no-incremental --disable-build-servers --verbosity diagnostic }
  • Mesaure-Command { dotnet build --no-incremental --disable-build-servers /bl }
ProjectDiagnostic verbosityBinary log
dotnet new console2.082s1.962s
Meziantou.Analyzer37s32s
Meziantou.Framework4.17m3.16m

Output Size:

ProjectOutput size
Meziantou.Framework (default verbosity)54kB
Meziantou.Framework (diagnostic verbosity)1.68GB
Meziantou.Framework (binary log)22MB

While the diagnostic log contains a lot of information, searching through a 1.68GB file is hardly practical!

#Generating a binary log

To generate a binary log, you can use the /bl switch:

Shell
msbuild.exe MySolution.sln /bl
msbuild.exe MySolution.sln /bl:out.binlog

If you really want to read text, you can regenerate the diagnostic log from the binary log:

Shell
msbuild.exe msbuild.binlog /noconlog /flp:v=diag;logfile=diag.log
dotnet msbuild output.binlog /noconlog "/flp:v=diag;logfile=diag.log"

Note that you can also embed custom files in the binary log, so you can easily investigate the build process. By default all MSBuild files (csproj, props, targets) are embedded in the binary log. You can also embed additional files using the Embed metadata (more info):

XML
<Project Sdk="Microsoft.NET.Sdk">
  <ItemGroup>
    <EmbedInBinlog Include="**/*.txt" />
  </ItemGroup>
</Project>

#Exploring the binary log

To read the binary log, you can use the MSBuildStructuredLog tool. You can install it using the following command:

Shell
winget install KirillOsenkov.MSBuildStructuredLogViewer

MSBuildStructuredLog provides a search syntax to find information in the log. For example, you can search for a specific target, task, or message. You can also filter the log to display only the information you are interested in.

MSBuildStructuredLogViewerMSBuildStructuredLogViewer

The interface provides some useful data such as:

  • Search panel to find targets, tasks, messages, items, etc. (search syntax documentation)
  • A tree view to navigate the log
  • A timeline to see the duration of each task
  • An analyzer and source generator summary (execution time per analyzer or source generator)

#Generating the binary log in GitHub Actions

You can easily integrate the binary log in your CI pipeline. For example, with GitHub Actions:

YAML
name: publish
on:
  push:
    branches:
      - 'main'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET Core
        uses: actions/setup-dotnet@v4

      - run: dotnet build /bl:build.binlog

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: binlog
          if-no-files-found: error
          retention-days: 3
          path: '**/*.binlog'

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?