Stop using diagnostic verbosity in MSBuild
On CI, some projects use the msbuild
diagnostic verbosity to get more information about the build. This is useful to troubleshoot build issues. However, this verbosity level has some drawbacks. In this post, I describe how to use the binary log feature of MSBuild to get the same information in a more efficient way.
#Diagnostic verbosity vs binary log
The msbuild
diagnostic verbosity is the most verbose level of verbosity. It outputs a lot of information about the build process, such as the list of all the targets executed, the properties and items used, and the tasks executed. This information is useful when you need to troubleshoot a build issue, but it has 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 in a more efficient way, you can use the binary log feature of MSBuild. The binary log is a file that contains all the information about the build process in a structured format. You can use the MSBuildStructuredLog tool to read the binary log and get the same information as the msbuild
diagnostic verbosity, but in a more readable and efficient way. The binlog has many advantages over the 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 }
Project | Diagnostic verbosity | Binary log |
---|---|---|
dotnet new console | 2.082s | 1.962s |
Meziantou.Analyzer | 37s | 32s |
Meziantou.Framework | 4.17m | 3.16m |
Output Size:
Project | Output size |
---|---|
Meziantou.Framework (default verbosity) | 54kB |
Meziantou.Framework (diagnostic verbosity) | 1.68GB |
Meziantou.Framework (binary log) | 22MB |
While the diagnostic log contains lot of information, I'm not sure I want to search something in a 1.68GB file!
#Generating a binary log
To generate a binary log, you can use the /bl
switch:
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:
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):
<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:
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.
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:
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!