This is one in a series of articles about Time Tortoise, a Universal Windows Platform app for planning and tracking your work schedule. For more on the development of this app and the ideas behind it, see my Time Tortoise category page.
The Log Viewer, which is installed with Visual Studio, is a simple tool for managing a set of logging-related registry keys and the log files in a specified directory. When logging is enabled, the system writes log files to the directory. These log files are in HTML format, but don’t have much in the way of formatting. So though the information they contain is invaluable, it’s not presented in the most understandable way.
Since I found myself spending a lot of time looking through these files to resolve dependency issues, I decided it would be worth writing a simple tool to display the log files in a more useful format.
A typical session with the Assembly Binding Log Viewer goes like this:
- Run the tool (as Administrator).
- Clear any existing logs, to get a fresh start.
- Run something (e.g., a unit test) that generates a binding error.
- For each resulting entry in the Log Viewer, open the corresponding log file and take action based on the results.
- Repeat until the scenario works. Often there will still be binding errors. Many failures seem to be expected as part of normal Windows operation. They don’t necessarily all need to be fixed.
The time-consuming step is #4. Although the log files have a standard format, it still takes time to look through each one and convert the logging information into actionable steps. And since there are often multiple binding failures, this process needs to be repeated multiple times. Can we automate Step #4?
Once the registry entries are set, the Log Viewer tool becomes a simple view into the chosen log file location on disk. That’s easy to replicate in a WinForms app with list views, text boxes, and buttons.
One way to make Step #4 faster is to parse the log file, extract the most useful information, and present it in a format that emphasizes the next step to take in order to fix the problem. Once that information is available, it’s also possible to add buttons to automate some steps.
Log File Line Prefixes
An assembly binding log file contains a list of lines, each of which presents information about a step in the assembly binding process. The parsing process consists of evaluating each line in the file from top to bottom. To determine what information a given line contains, I look at the prefix, the part of the line that doesn’t change based on the assembly being bound. For example, here’s a common line type:
Calling assembly : Microsoft.VisualStudio.TestPlatform.Common, Version=18.104.22.168, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a.
The prefix is
Calling assembly :. The rest of the line contains the fully-qualified name of the assembly. This can be further parsed into the assembly name, version, culture, and public key token.
For some log file lines, the entire line is the prefix. For example:
LOG: All probing URLs attempted and failed.
There’s no other information to extract from this line.
Extracting Log Line Information
For lines that contain more than just a prefix, the next step after identifying the line type is to extract the remaining information from the line. An example for calling assembly is given above. Here’s a different example:
LOG: Using application configuration file: C:\PROGRAM FILES (X86)\ MICROSOFT VISUAL STUDIO\2017\ENTERPRISE\COMMON7\IDE\COMMONEXTENSIONS\ MICROSOFT\TESTWINDOW\vstest.executionengine.x86.exe.Config
The prefix is
LOG: Using application configuration file:, and the rest of the line is a path to the app.config file that was in use when the binding failure occurred.
Once each line has been evaluated and information has been extracted from it, we’re ready to display the results.
One goal of this assembly log parser is to display the log information in a clearer format. Currently, the format consists of a summary section followed by a list of key information:
- Summary: The main problem identified by the log file, and a recommendation on how to fix it.
- Executable: The program that was running when the binding failure occurred.
- Target assembly name: The assembly that the executable tried to load.
- Calling assembly name: The assembly containing the code that tried to load the target assembly.
- App config path: The path to the app.config file that was in use when the binding failure occurred.
- Target assembly name (after app config): The app.config file can direct the system to load a different target assembly version than specified by the calling assembly. This line identifies the full name of the target assembly after the app.config file is processed.
- Found target assembly at path: If some version of the target assembly was found, this is where it was found.
- Found assembly name: The full name of the assembly that was found.
- Name mismatch part: The part of the assembly version that didn’t match the target.
Not all of these results are shown for every log file. The example above is for the case where the wrong version of the assembly was found. If the assembly is missing entirely, then lines like Found assembly name and Name mismatch part are not relevant, so they are omitted.
Although the formatted results make key information more accessible than it is in the original log file, there are still some manual steps to take after reading the results. A few buttons in the tool can make these steps faster or provide additional information:
- Open Original: Open the original log file to take a closer look at the source data.
- Open Config: Open the app.config file identified by the tool, in order to read or edit settings.
- Refresh: Process the log file directory again. This is useful after running a unit test or taking another action that generates new log files.
- Delete All: Delete all files in the log directory, to get a fresh start.
- Generate Binding Redirect to Clipboard: Using the log information, generate a binding redirect config section for the app.config. This can fix problems related to finding the wrong version of an assembly. Another option would be to have the tool update the app.config directly, but this would be more complex.
- Generate Copy Command to Clipboard: Using the log information, generate a command to copy a missing assembly. Another option would be to have the tool copy the assembly directly, but generating the copy command instead (e.g., for pasting into a cmd file) provides an audit trail, which makes it easier to reverse the action if necessary.
- Copy Target Assembly Name to Clipboard: This makes it easier to find the missing assembly (e.g., on nuget.org).
Simplified Dependency Investigation
Investigating dependency issues can be a frustrating process, but it helps to have a tool that automates some of the steps. If you want to try it out, I put it on GitHub.