├── .editorconfig ├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── BinaryCompatReport.txt ├── Directory.Build.props ├── Directory.Build.rsp ├── Directory.Build.targets ├── Directory.Packages.props ├── LICENSE ├── MSBuildStructuredLog.Avalonia.sln ├── MSBuildStructuredLog.sln ├── PRIVACY.md ├── PublishNativeAOT.cmd ├── PublishNativeAOT.sh ├── README.md ├── ThirdPartyNotices ├── appveyor.yml ├── build-macos.cake ├── chocopush.cmd ├── docs ├── BinlogOrGtfo.png ├── BuildTargets.png ├── CompileTargets.png ├── ILoveMSBuild.pdn ├── MSBuildAssemblies.png ├── MSBuildBatSignal.png ├── MSBuildIssues.png ├── Screenshot1.png ├── Screenshot2.png ├── ScreenshotRoslyn.png ├── StructuredLogSchema.txt └── StructuredLogger.png ├── global.json ├── key.snk ├── nuget.config ├── run.sh ├── src ├── BinlogTool │ ├── BinlogTool.csproj │ ├── BinlogToolCommandBase.cs │ ├── CompilerInvocations.cs │ ├── DoubleWrites.cs │ ├── DumpRecords.cs │ ├── ListNuGet.cs │ ├── ListProperties.cs │ ├── ListTools.cs │ ├── Log.cs │ ├── Program.cs │ ├── Redact.cs │ ├── SaveFiles.cs │ ├── SaveStrings.cs │ ├── Search.cs │ ├── binlogtoolexe.nuspec │ └── publish.cmd ├── ResourcesGenerator │ ├── Program.cs │ ├── ResourceCreator.cs │ └── ResourcesGenerator.csproj ├── Samples │ └── TimesAndDurations │ │ ├── Program.cs │ │ └── TimesAndDurations.csproj ├── StructuredLogViewer.Avalonia │ ├── App.xaml │ ├── App.xaml.cs │ ├── AvaloniaExtensions.cs │ ├── BuildParametersScreen.cs │ ├── Controls │ │ ├── BuildControl.xaml │ │ ├── BuildControl.xaml.cs │ │ ├── DocumentWell.xaml │ │ ├── DocumentWell.xaml.cs │ │ ├── ImportLinkHighlighter.cs │ │ ├── NavigationHelper.cs │ │ ├── NodeIsSelectedToColorConverter.cs │ │ ├── NodeLowRelevanceToOpactityConverter.cs │ │ ├── ProjectIconConverter.cs │ │ ├── ProxyNodeIconConverter.cs │ │ ├── SearchAndResultsControl.xaml │ │ ├── SearchAndResultsControl.xaml.cs │ │ ├── SourceFileTab.cs │ │ ├── SplitterPanel.cs │ │ ├── StringEmptinessToVisibilityConverter.cs │ │ ├── TextViewerControl.xaml │ │ └── TextViewerControl.xaml.cs │ ├── Entitlements.plist │ ├── FileTypes.cs │ ├── Info.plist │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── Program.cs │ ├── Resources.xaml │ ├── StructuredLogViewer.Avalonia.csproj │ ├── StructuredLogViewer.icns │ ├── StructuredLogger.ico │ └── Styles.xaml ├── StructuredLogViewer.Core │ ├── BuildProgress.cs │ ├── ButtonNode.cs │ ├── ClipboardService.cs │ ├── Command.cs │ ├── CustomContentNode.cs │ ├── DialogService.cs │ ├── DotnetUtilities.cs │ ├── ExceptionHandler.cs │ ├── HostedBuild.cs │ ├── PreprocessedFileManager.cs │ ├── ProjectGraph │ │ ├── MSAGLProjectGraphConstructor.cs │ │ └── RuntimeGraph.cs │ ├── ProjectImport.cs │ ├── SettingsService.cs │ ├── SingleGlobalInstance.cs │ ├── SourceFiles │ │ ├── ArchiveFileResolver.cs │ │ ├── ISourceFileResolver.cs │ │ ├── LocalSourceFileResolver.cs │ │ ├── SourceFileResolver.cs │ │ └── SourceText.cs │ ├── StructuredLogViewer.Core.csproj │ ├── Timeline │ │ ├── Block.cs │ │ ├── Lane.cs │ │ └── Timeline.cs │ ├── TypingConcurrentOperation.cs │ ├── Utilities.cs │ └── WelcomeScreen.cs ├── StructuredLogViewer │ ├── App.config │ ├── AvaloniaExtensions.cs │ ├── BuildParametersScreen.cs │ ├── Controls │ │ ├── BuildControl.xaml │ │ ├── BuildControl.xaml.cs │ │ ├── Classifier.cs │ │ ├── ContextMenuUtilities.cs │ │ ├── DocumentOutline.cs │ │ ├── DocumentWell.xaml │ │ ├── DocumentWell.xaml.cs │ │ ├── ImportLinkHighlighter.cs │ │ ├── NavigationHelper.cs │ │ ├── NodeHyperlinkControl.cs │ │ ├── ProjectGraphControl.xaml │ │ ├── ProjectGraphControl.xaml.cs │ │ ├── RedactInputControl.xaml │ │ ├── RedactInputControl.xaml.cs │ │ ├── SearchAndResultsControl.xaml │ │ ├── SearchAndResultsControl.xaml.cs │ │ ├── SourceFileTab.cs │ │ ├── SourceFileTabHeader.cs │ │ ├── SplitterPanel.cs │ │ ├── StringEmptinessToVisibilityConverter.cs │ │ ├── TextViewerControl.xaml │ │ ├── TextViewerControl.xaml.cs │ │ ├── TimelineControl.xaml │ │ ├── TimelineControl.xaml.cs │ │ ├── TracingControl.xaml │ │ ├── TracingControl.xaml.cs │ │ ├── TreeViewExtensions.cs │ │ ├── TreeViewItemExtensions.cs │ │ └── VisualTreeUtilities.cs │ ├── Entrypoint.cs │ ├── FileAssociations.cs │ ├── MSBuildLocator.cs │ ├── MSBuildStructuredLogViewer.nuspec │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── Resources │ │ └── SolutionFile.xshd │ ├── ScrollViewerHelper.cs │ ├── StructuredLogViewer.csproj │ ├── StructuredLogger.ico │ ├── TaskExtensions.cs │ ├── WindowPosition.cs │ ├── app.manifest │ ├── icons │ │ └── msbuild-structured-log-viewer.png │ ├── msbuild-structured-log-viewer.nuspec │ ├── themes │ │ ├── Button.xaml │ │ ├── CheckBox.xaml │ │ ├── ComboBox.xaml │ │ ├── ComboBoxItem.xaml │ │ ├── ContextMenu.xaml │ │ ├── Generic.xaml │ │ ├── GroupBox.xaml │ │ ├── MenuItem.xaml │ │ ├── ScrollBar.xaml │ │ ├── TabItem.xaml │ │ ├── ThemeManager.cs │ │ └── TreeViewItem.xaml │ └── tools │ │ ├── .skipAutoUninstall │ │ ├── LICENSE.txt │ │ ├── VERIFICATION.txt │ │ ├── chocolateyinstall.ps1 │ │ └── chocolateyuninstall.ps1 ├── StructuredLogger.Tests │ ├── App.config │ ├── AssertEx.cs │ ├── BinaryLoggerTests.cs │ ├── BinarySerializationTests.cs │ ├── CompilerInvocationTests.cs │ ├── ConditionParserTests.cs │ ├── Differ.cs │ ├── ItemGroupParserTests.cs │ ├── MSBuild.cs │ ├── ParseIntoWordsTests.cs │ ├── ResourceStringTests.cs │ ├── Roundtrip.cs │ ├── StringsTests.cs │ ├── StructuredLogger.Tests.csproj │ ├── TestUtilities.cs │ └── TextUtilitiesTests.cs ├── StructuredLogger.Utils │ ├── BinlogRedactor.cs │ ├── NuGetSearch.cs │ ├── SecretsSearch.cs │ └── StructuredLogger.Utils.csproj ├── StructuredLogger │ ├── Analyzers │ │ ├── BuildAnalyzer.cs │ │ ├── CompilerInvocations.cs │ │ ├── CppAnalyzer.cs │ │ ├── CscTaskAnalyzer.cs │ │ ├── DoubleWritesAnalyzer.cs │ │ ├── FileCopyMap.cs │ │ ├── ImportTreeAnalyzer.cs │ │ ├── MessageTaskAnalyzer.cs │ │ ├── ProjectReferenceGraph.cs │ │ ├── ResolveAssemblyReferenceAnalyzer.cs │ │ ├── Stats.cs │ │ └── TargetGraph.cs │ ├── AssemblyLoadBuildEventArgs2.cs │ ├── BinaryLog.cs │ ├── BinaryLogger │ │ ├── ArrayDictionary.cs │ │ ├── AsyncBufferedReadStream.cs │ │ ├── BinLogReader.cs │ │ ├── BinaryLogRecord.cs │ │ ├── BinaryLogRecordKind.cs │ │ ├── BinaryLogReplayEventSource.cs │ │ ├── BinaryLogger.cs │ │ ├── BuildCanceledEventArgs.cs │ │ ├── BuildCheckEventArgs.cs │ │ ├── BuildEventArgsDispatcher.cs │ │ ├── BuildEventArgsFieldFlags.cs │ │ ├── BuildEventArgsFields.cs │ │ ├── BuildEventArgsReader.Viewer.cs │ │ ├── BuildEventArgsReader.cs │ │ ├── BuildEventArgsWriter.cs │ │ ├── BuildSubmissionStartedEventArgs.cs │ │ ├── EnvironmentVariableReadEventArgs2.cs │ │ ├── EvaluationIdProvider.cs │ │ ├── ExtendedBuildErrorEventArgs.cs │ │ ├── ExtendedBuildMessageEventArgs.cs │ │ ├── ExtendedBuildWarningEventArgs.cs │ │ ├── ExtendedCriticalBuildMessageEventArgs.cs │ │ ├── ExtendedCustomBuildEventArgs.cs │ │ ├── ExtendedDataFields.cs │ │ ├── ExtendedPropertyInitialValueSetEventArgs.cs │ │ ├── ExtendedPropertyReassignmentEventArgs.cs │ │ ├── FileUsedEventArgs.cs │ │ ├── IExtendedBuildEventArgs.cs │ │ ├── IsExternalInit.cs │ │ ├── Postprocessing │ │ │ ├── ArchiveFileEventArgs.cs │ │ │ ├── ArchiveFileEventArgsExtensions.cs │ │ │ ├── BinaryLogReaderErrorEventArgs.cs │ │ │ ├── EmbeddedContentEventArgs.cs │ │ │ ├── IBuildEventArgsReaderNotifications.cs │ │ │ ├── ReaderErrorType.cs │ │ │ ├── StreamChunkOverreadException.cs │ │ │ └── StringReadEventArgs.cs │ │ ├── ProjectImportsCollector.cs │ │ ├── ProjectImportsCollectorExtensions.cs │ │ ├── ResourceUtilities.cs │ │ ├── TaskItemData.cs │ │ ├── TaskParameterEventArgs2.cs │ │ ├── TaskStartedEventArgs2.cs │ │ └── Utilities.cs │ ├── ConditionNode.cs │ ├── Construction │ │ ├── BuildStatistics.cs │ │ ├── Construction.cs │ │ ├── ItemGroupParser.cs │ │ └── MessageProcessor.cs │ ├── CustomAppDomainManager.cs │ ├── ErrorReporting.cs │ ├── ObjectModel │ │ ├── AbstractDiagnostic.cs │ │ ├── AddItem.cs │ │ ├── ArchiveFile.cs │ │ ├── BaseNode.cs │ │ ├── Build.cs │ │ ├── ChildrenList.cs │ │ ├── CriticalBuildMessage.cs │ │ ├── EntryTarget.cs │ │ ├── Error.cs │ │ ├── EvaluationProfileEntry.cs │ │ ├── Folder.cs │ │ ├── IHasExtendedData.cs │ │ ├── IHasLineNumber.cs │ │ ├── IHasRelevance.cs │ │ ├── IHasSourceFile.cs │ │ ├── IPreprocessable.cs │ │ ├── IProjectOrEvaluation.cs │ │ ├── Import.cs │ │ ├── Item.cs │ │ ├── Message.cs │ │ ├── Metadata.cs │ │ ├── NameValueNode.cs │ │ ├── NamedNode.cs │ │ ├── NoImport.cs │ │ ├── NodeFlags.cs │ │ ├── Note.cs │ │ ├── ObservableObject.cs │ │ ├── Package.cs │ │ ├── Parameter.cs │ │ ├── Project.cs │ │ ├── ProjectEvaluation.cs │ │ ├── Property.cs │ │ ├── RemoveItem.cs │ │ ├── SearchableItem.cs │ │ ├── SourceFile.cs │ │ ├── SourceFileLine.cs │ │ ├── Target.cs │ │ ├── Task.cs │ │ ├── TaskItem.cs │ │ ├── Tasks │ │ │ ├── CompilationWrites.cs │ │ │ ├── CopyTask.cs │ │ │ ├── CscTask.cs │ │ │ ├── FileCopyOperation.cs │ │ │ ├── FscTask.cs │ │ │ ├── ManagedCompilerTask.cs │ │ │ ├── ResolveAssemblyReferenceTask.cs │ │ │ ├── RobocopyTask.cs │ │ │ └── VbcTask.cs │ │ ├── TextNode.cs │ │ ├── TimedNode.cs │ │ ├── TreeNode.cs │ │ ├── Warning.cs │ │ └── readme.txt │ ├── PathUtils.cs │ ├── PlatformUtilities.cs │ ├── Progress.cs │ ├── Properties │ │ └── launchSettings.json │ ├── ReaderSettings.cs │ ├── Reflector.cs │ ├── Search │ │ ├── HighlightedText.cs │ │ ├── ISearchExtension.cs │ │ ├── NodeQueryMatcher.cs │ │ ├── PropertiesAndItemsSearch.cs │ │ ├── ProxyNode.cs │ │ ├── ResultTree.cs │ │ ├── Search.cs │ │ ├── SearchIndex.cs │ │ └── SearchResult.cs │ ├── Serialization │ │ ├── AttributeNames.cs │ │ ├── Binary │ │ │ ├── BetterBinaryReader.cs │ │ │ ├── BetterBinaryWriter.cs │ │ │ ├── BuildLogReader.cs │ │ │ ├── BuildLogWriter.cs │ │ │ ├── TreeBinaryReader.cs │ │ │ └── TreeBinaryWriter.cs │ │ ├── Serialization.cs │ │ ├── StringCache.cs │ │ ├── StringWriter.cs │ │ ├── XlinqLogReader.cs │ │ ├── XmlLogReader.cs │ │ └── XmlLogWriter.cs │ ├── Span.cs │ ├── Strings │ │ ├── JsonParser.cs │ │ ├── ResourcesCollection.cs │ │ ├── Strings.cs │ │ ├── Strings.json │ │ └── readme.txt │ ├── StructuredLogger.cs │ ├── StructuredLogger.csproj │ ├── TextUtilities.cs │ ├── UnknownDataBehavior.cs │ ├── Utilities.cs │ └── key.snk ├── TaskRunner │ ├── AppDomainInitializer.cs │ ├── BuildEngine.cs │ ├── Executor.cs │ ├── MissingPropertyException.cs │ ├── Program.cs │ ├── TaskRunner.csproj │ ├── app.config │ └── app.manifest └── Tests │ ├── 1.proj │ └── 2.proj └── version.json /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Don't mess with my line endings. 3 | ############################################################################### 4 | * -text -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [KirillOsenkov] 4 | -------------------------------------------------------------------------------- /BinaryCompatReport.txt: -------------------------------------------------------------------------------- 1 | In assembly 'NuGet.Squirrel, Version=3.0.0.0, PublicKeyToken=null': Failed to resolve assembly reference to 'Microsoft.Data.OData, Version=5.6.1.0, PublicKeyToken=31bf3856ad364e35' 2 | In assembly 'NuGet.Squirrel, Version=3.0.0.0, PublicKeyToken=null': Failed to resolve assembly reference to 'Microsoft.Data.Services.Client, Version=5.6.1.0, PublicKeyToken=31bf3856ad364e35' 3 | In assembly 'NuGet.Squirrel, Version=3.0.0.0, PublicKeyToken=null': Failed to resolve assembly reference to 'Microsoft.Web.XmlTransform, Version=2.1.0.0, PublicKeyToken=b03f5f7f11d50a3a' 4 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | $(MSBuildProjectName) 6 | $([System.Text.RegularExpressions.Regex]::Replace($(MSBuildProjectName), '_[^_]+_wpftmp', '', System.Text.RegularExpressions.RegexOptions.IgnoreCase)) 7 | $(MSBuildThisFileDirectory)obj\$(BasePathProjectName)\ 8 | $(MSBuildThisFileDirectory)bin\$(MSBuildProjectName)\ 9 | true 10 | <_FindDependencies>false 11 | 12 | false 13 | false 14 | 15 | 16 | 17 | false 18 | $(DefineConstants);SIGN; 19 | false 20 | $(MSBuildThisFileDirectory)\key.snk 21 | embedded 22 | latest 23 | $(NoWarn);NU1701;CS8632 24 | False 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Directory.Build.rsp: -------------------------------------------------------------------------------- 1 | /nologo 2 | /r 3 | /m 4 | /bl 5 | /verbosity:minimal 6 | /clp:Summary;ForceNoAlign 7 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kirill Osenkov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PRIVACY.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | ## MSBuild binary logs 4 | 5 | Collecting MSBuild binary logs from MSBuild as described on https://msbuildlog.com may collect your source code, secrets from your source code and environment and store them in the .binlog file, exclusively for the purpose of making .binlog files useful for build analysis. Treat .binlog files like you treat your source code and environment variables. Do not share .binlog files publicly unless you are confident they contain no secrets or private information. See details at https://msbuildlog.com/#security 6 | 7 | ## MSBuild Structured Log Viewer 8 | 9 | The MSBuild Structured Log Viewer app opens .binlog files but it makes no web or cloud requests and all viewing and processing is done on the local machine. It will not send any information anywhere. 10 | 11 | The only network call the binlog viewer makes is to self-update via https://github.com/KirillOsenkov/MSBuildStructuredLog/releases. 12 | -------------------------------------------------------------------------------- /PublishNativeAOT.cmd: -------------------------------------------------------------------------------- 1 | dotnet publish -c Release -r win-x64 -p:PublishNativeAot=True src/StructuredLogViewer.Avalonia/StructuredLogViewer.Avalonia.csproj -------------------------------------------------------------------------------- /PublishNativeAOT.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | unameOut="$(uname -s)" 3 | case "${unameOut}" in 4 | Darwin*) rid=osx-arm64;; 5 | Linux*) rid=linux-x64;; 6 | *) 7 | echo "Unknown OS" 8 | exit 1 9 | ;; 10 | esac 11 | dotnet publish -c Release -r ${rid} -p:PublishNativeAot=True src/StructuredLogViewer.Avalonia/StructuredLogViewer.Avalonia.csproj 12 | -------------------------------------------------------------------------------- /ThirdPartyNotices: -------------------------------------------------------------------------------- 1 | Microsoft Automatic Graph Layout,MSAGL 2 | 3 | Copyright (c) Microsoft Corporation 4 | 5 | All rights reserved. 6 | 7 | MIT License 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining 10 | a copy of this software and associated documentation files (the 11 | ""Software""), to deal in the Software without restriction, including 12 | without limitation the rights to use, copy, modify, merge, publish, 13 | distribute, sublicense, and/or sell copies of the Software, and to 14 | permit persons to whom the Software is furnished to do so, subject to 15 | the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be 18 | included in all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, 21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /chocopush.cmd: -------------------------------------------------------------------------------- 1 | choco push --source https://push.chocolatey.org bin\StructuredLogViewer\Release\net472\msbuild-structured-log-viewer.*.nupkg -------------------------------------------------------------------------------- /docs/BinlogOrGtfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/BinlogOrGtfo.png -------------------------------------------------------------------------------- /docs/BuildTargets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/BuildTargets.png -------------------------------------------------------------------------------- /docs/CompileTargets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/CompileTargets.png -------------------------------------------------------------------------------- /docs/ILoveMSBuild.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/ILoveMSBuild.pdn -------------------------------------------------------------------------------- /docs/MSBuildAssemblies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/MSBuildAssemblies.png -------------------------------------------------------------------------------- /docs/MSBuildBatSignal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/MSBuildBatSignal.png -------------------------------------------------------------------------------- /docs/MSBuildIssues.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/MSBuildIssues.png -------------------------------------------------------------------------------- /docs/Screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/Screenshot1.png -------------------------------------------------------------------------------- /docs/Screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/Screenshot2.png -------------------------------------------------------------------------------- /docs/ScreenshotRoslyn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/ScreenshotRoslyn.png -------------------------------------------------------------------------------- /docs/StructuredLogSchema.txt: -------------------------------------------------------------------------------- 1 | Build 2 | Project 3 | Project 4 | Target 5 | Target 6 | Task 7 | Folder(Parameters) 8 | Property 9 | InputParameter 10 | Folder(Output items) 11 | Property 12 | InputParameter 13 | Folder(Output properties) 14 | Property 15 | Folder(Messages) 16 | Message 17 | Folder(Messages) 18 | Property 19 | InputParameter 20 | Item 21 | Property 22 | Message 23 | Property 24 | Folder(Properties) 25 | Folder(Global) 26 | Property 27 | Property 28 | Folder(Messages) 29 | Folder(Properties) 30 | Property 31 | Folder(Warnings) 32 | Warning 33 | Folder(Errors) 34 | Error 35 | -------------------------------------------------------------------------------- /docs/StructuredLogger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/docs/StructuredLogger.png -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "allowPrerelease": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/key.snk -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 3 | cd "${DIR}/src/StructuredLogViewer.Avalonia" || exit 4 | dotnet run StructuredLogViewer.Avalonia.csproj 5 | -------------------------------------------------------------------------------- /src/BinlogTool/BinlogToolCommandBase.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Microsoft.Build.Logging.StructuredLogger; 3 | 4 | namespace BinlogTool 5 | { 6 | public abstract class BinlogToolCommandBase 7 | { 8 | protected Build ReadBuild(string binLogFilePath, bool throwOnPathNotFound = true) 9 | { 10 | if (string.IsNullOrEmpty(binLogFilePath) || !File.Exists(binLogFilePath)) 11 | { 12 | if(throwOnPathNotFound) 13 | { 14 | throw new FileNotFoundException("Specified binlog was not found.", binLogFilePath); 15 | } 16 | 17 | return null; 18 | } 19 | 20 | return BinaryLog.ReadBuild(binLogFilePath); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/BinlogTool/CompilerInvocations.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.Build.Logging.StructuredLogger; 3 | 4 | namespace BinlogTool 5 | { 6 | public class CompilerInvocations : BinlogToolCommandBase 7 | { 8 | public void Run(string binLogFilePath, string outputFilePath) 9 | { 10 | var invocations = CompilerInvocationsReader.ReadInvocations(binLogFilePath); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/BinlogTool/DoubleWrites.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.Build.Logging.StructuredLogger; 6 | 7 | namespace BinlogTool 8 | { 9 | public class DoubleWrites : BinlogToolCommandBase 10 | { 11 | public void Run(string binLogFilePath, string outputFilePath) 12 | { 13 | var build = this.ReadBuild(binLogFilePath); 14 | BuildAnalyzer.AnalyzeBuild(build); 15 | var doubleWrites = DoubleWritesAnalyzer.GetDoubleWrites(build); 16 | var sb = new StringBuilder(); 17 | foreach (var doubleWrite in doubleWrites.OrderBy(s => s.Key)) 18 | { 19 | sb.AppendLine(doubleWrite.Key); 20 | foreach (var source in doubleWrite.Value.OrderBy(s => s)) 21 | { 22 | sb.AppendLine($" {source}"); 23 | } 24 | } 25 | 26 | if (!string.IsNullOrEmpty(outputFilePath)) 27 | { 28 | File.WriteAllText(outputFilePath, sb.ToString()); 29 | } 30 | else 31 | { 32 | Console.WriteLine(sb.ToString()); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/BinlogTool/ListProperties.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Microsoft.Build.Framework; 6 | using Microsoft.Build.Logging; 7 | using Microsoft.Build.Logging.StructuredLogger; 8 | 9 | namespace BinlogTool 10 | { 11 | public class ListProperties : BinlogToolCommandBase 12 | { 13 | private Dictionary> propertyValues = new(StringComparer.OrdinalIgnoreCase); 14 | 15 | public void Run(string binLogFilePath) 16 | { 17 | var records = BinaryLog.ReadRecords(binLogFilePath); 18 | foreach (var record in records) 19 | { 20 | var args = record.Args; 21 | if (args is ProjectEvaluationFinishedEventArgs evaluationFinished) 22 | { 23 | VisitProperties(record, evaluationFinished.Properties); 24 | } 25 | else if (args is ProjectStartedEventArgs projectStarted) 26 | { 27 | VisitProperties(record, projectStarted.Properties); 28 | } 29 | } 30 | 31 | Print(); 32 | } 33 | 34 | private void Print() 35 | { 36 | foreach (var property in propertyValues.OrderBy(kvp => kvp.Key)) 37 | { 38 | var values = property.Value; 39 | foreach (var value in values.OrderBy(s => s)) 40 | { 41 | Console.WriteLine($"{property.Key}={value}"); 42 | } 43 | } 44 | } 45 | 46 | private void VisitProperties(Record record, IEnumerable properties) 47 | { 48 | if (properties == null) 49 | { 50 | return; 51 | } 52 | 53 | foreach (var kvp in properties) 54 | { 55 | if (kvp is KeyValuePair keyValuePair) 56 | { 57 | VisitProperty(record, keyValuePair.Key, keyValuePair.Value); 58 | } 59 | else if (kvp is DictionaryEntry entry) 60 | { 61 | VisitProperty(record, Convert.ToString(entry.Key), Convert.ToString(entry.Value)); 62 | } 63 | } 64 | } 65 | 66 | private void VisitProperty(Record record, string key, string value) 67 | { 68 | if (!propertyValues.TryGetValue(key, out var bucket)) 69 | { 70 | bucket = []; 71 | propertyValues[key] = bucket; 72 | } 73 | 74 | bucket.Add(value); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/BinlogTool/Log.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BinlogTool 4 | { 5 | public class Log 6 | { 7 | private static readonly object consoleLock = new object(); 8 | 9 | public static bool Quiet { get; internal set; } 10 | 11 | public static void Write(string message, ConsoleColor color = ConsoleColor.Gray) 12 | { 13 | if (Quiet) 14 | { 15 | return; 16 | } 17 | 18 | lock (consoleLock) 19 | { 20 | var oldColor = Console.ForegroundColor; 21 | Console.ForegroundColor = color; 22 | Console.Write(message); 23 | if (color != oldColor) 24 | { 25 | Console.ForegroundColor = oldColor; 26 | } 27 | } 28 | } 29 | 30 | public static void WriteLine(string message = "", ConsoleColor color = ConsoleColor.Gray) 31 | { 32 | if (Quiet) 33 | { 34 | return; 35 | } 36 | 37 | lock (consoleLock) 38 | { 39 | var oldColor = Console.ForegroundColor; 40 | Console.ForegroundColor = color; 41 | Console.WriteLine(message); 42 | if (color != oldColor) 43 | { 44 | Console.ForegroundColor = oldColor; 45 | } 46 | } 47 | } 48 | 49 | public static void WriteError(string message) 50 | { 51 | lock (consoleLock) 52 | { 53 | var oldColor = Console.ForegroundColor; 54 | Console.ForegroundColor = ConsoleColor.Red; 55 | Console.Error.WriteLine(message); 56 | if (oldColor != ConsoleColor.Red) 57 | { 58 | Console.ForegroundColor = oldColor; 59 | } 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/BinlogTool/Redact.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Linq; 5 | using StructuredLogger.Utils; 6 | 7 | namespace BinlogTool 8 | { 9 | internal static class Redact 10 | { 11 | public static void Run(List inputs, List tokens, bool inPlace, bool recurse) 12 | { 13 | if (!inputs.Any()) 14 | { 15 | // Default - current directory 16 | inputs.Add(string.Empty); 17 | } 18 | 19 | var inputBinlogs = inputs.SelectMany(input => Searcher.FindBinlogs(input, recurse)).ToList(); 20 | 21 | if (!inputBinlogs.Any()) 22 | { 23 | Log.WriteError("No binlogs found."); 24 | return; 25 | } 26 | 27 | if (inputBinlogs.Count > 1) 28 | { 29 | Log.WriteLine( 30 | $"Found {inputBinlogs.Count} binlog files. Will redact secrets in all. (found files: {(string.Join(',', inputBinlogs))})"); 31 | } 32 | 33 | BinlogRedactorOptions options = new BinlogRedactorOptions(string.Empty) 34 | { 35 | TokensToRedact = tokens.ToArray(), 36 | IdentifyReplacemenets = true, 37 | AutodetectCommonPatterns = true, 38 | AutodetectUsername = true, 39 | ProcessEmbeddedFiles = true, 40 | }; 41 | 42 | var overallStopwatch = Stopwatch.StartNew(); 43 | 44 | foreach (var inputBinlog in inputBinlogs) 45 | { 46 | options.InputPath = inputBinlog; 47 | options.OutputFileName = GetOutputFileName(inputBinlog); 48 | 49 | Log.WriteLine($"Redacting binlog {inputBinlog} to {options.OutputFileName} ({GetFileSizeInKB(inputBinlog)} KB)"); 50 | 51 | var stopwatch = Stopwatch.StartNew(); 52 | 53 | BinlogRedactor.RedactSecrets(options, progress: null); 54 | 55 | stopwatch.Stop(); 56 | Log.WriteLine($"Redacting done. Duration: {stopwatch.Elapsed}"); 57 | } 58 | 59 | overallStopwatch.Stop(); 60 | if(inputBinlogs.Count > 1) 61 | { 62 | Log.WriteLine($"Redacting all binlogs done. Duration: {overallStopwatch.Elapsed}"); 63 | } 64 | 65 | string GetOutputFileName(string inputFileName) 66 | { 67 | if (inPlace) 68 | { 69 | return inputFileName; 70 | } 71 | 72 | return Path.ChangeExtension(inputFileName, ".redacted.binlog"); 73 | } 74 | 75 | long GetFileSizeInKB(string path) 76 | => new FileInfo(path).Length / 1024; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/BinlogTool/SaveStrings.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.Build.Logging.StructuredLogger; 3 | 4 | namespace BinlogTool 5 | { 6 | public class SaveStrings : BinlogToolCommandBase 7 | { 8 | public void Run(string binLogFilePath, string outputFilePath) 9 | { 10 | var build = this.ReadBuild(binLogFilePath); 11 | var strings = build.StringTable.Instances.OrderBy(s => s).ToArray(); 12 | 13 | Serialization.WriteStringsToFile(outputFilePath, strings); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/BinlogTool/binlogtoolexe.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | binlogtoolexe 5 | 1.0.27 6 | Kirill Osenkov 7 | MIT 8 | https://licenses.nuget.org/MIT 9 | msbuild-structured-log-viewer.png 10 | readme.md 11 | https://github.com/KirillOsenkov/MSBuildStructuredLog 12 | A tool to read, process and analyze MSBuild .binlog files. Read more at https://msbuildlog.com. 13 | MSBuild Log Logger Structure Structured Binlog BinlogTool 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/BinlogTool/publish.cmd: -------------------------------------------------------------------------------- 1 | rem dotnet publish -r win-x64 --self-contained=false /p:Configuration=Release /p:PublishSingleFile=true /bl:publish.binlog 2 | msbuild /p:Configuration=Release /t:PublishBinlogToolExe /p:RuntimeIdentifier=win-x64 -------------------------------------------------------------------------------- /src/ResourcesGenerator/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | 5 | namespace ResourcesGenerator 6 | { 7 | class Program 8 | { 9 | /// 10 | /// To regenerate Strings.json, add the strings you want to consume from MSBuild to 11 | /// and run this program 12 | /// 13 | static void Main() 14 | { 15 | var options = new Microsoft.Build.Locator.VisualStudioInstanceQueryOptions() 16 | { 17 | DiscoveryTypes = Microsoft.Build.Locator.DiscoveryType.VisualStudioSetup | Microsoft.Build.Locator.DiscoveryType.DotNetSdk 18 | }; 19 | var instances = Microsoft.Build.Locator.MSBuildLocator.QueryVisualStudioInstances(options) 20 | .OrderByDescending(i => i.Version).ToArray(); 21 | var instance = 22 | instances.FirstOrDefault(i => !i.MSBuildPath.Contains("Preview")) ?? 23 | instances.FirstOrDefault(); 24 | 25 | var candidates = new[] 26 | { 27 | @"C:\msbuild\artifacts\bin\bootstrap\net472\MSBuild\Current\Bin\amd64", 28 | instance?.MSBuildPath, 29 | Path.Combine( 30 | Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), 31 | "Microsoft Visual Studio", "2022", "Enterprise", "MSBuild", "Current", "Bin"), 32 | }; 33 | 34 | var msbuildPath = candidates.FirstOrDefault(c => c != null && Directory.Exists(c)); 35 | 36 | if (Directory.Exists(msbuildPath)) 37 | { 38 | ResourceCreator.CreateResourceFile(msbuildPath); 39 | } 40 | else 41 | { 42 | Console.Error.WriteLine("Couldn't find MSBuild at " + msbuildPath); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/ResourcesGenerator/ResourcesGenerator.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net472 6 | x86 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Samples/TimesAndDurations/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using Microsoft.Build.Logging.StructuredLogger; 5 | 6 | namespace TimesAndDurations 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | if (args.Length != 1) 13 | { 14 | return; 15 | } 16 | 17 | var logFilePath = args[0]; 18 | if (!File.Exists(logFilePath)) 19 | { 20 | return; 21 | } 22 | 23 | var build = Serialization.Read(logFilePath); 24 | 25 | var targets = build 26 | .FindChildrenRecursive(t => t.StartTime > DateTime.MinValue && t.EndTime < DateTime.MaxValue) 27 | .OrderByDescending(t => t.Duration) 28 | .ToArray(); 29 | 30 | foreach (var target in targets.Take(10)) 31 | { 32 | Console.WriteLine($"Target {target.Name}: {TextUtilities.Display(target.StartTime, displayDate: false)} {TextUtilities.Display(target.EndTime, displayDate: false)} {target.DurationText}"); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Samples/TimesAndDurations/TimesAndDurations.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/StructuredLogViewer.Avalonia/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.ApplicationLifetimes; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace StructuredLogViewer.Avalonia 6 | { 7 | public class App : Application 8 | { 9 | public override void Initialize() 10 | { 11 | AvaloniaXamlLoader.Load(this); 12 | } 13 | 14 | public override void OnFrameworkInitializationCompleted() 15 | { 16 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 17 | desktop.MainWindow = new MainWindow(); 18 | 19 | base.OnFrameworkInitializationCompleted(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/StructuredLogViewer.Avalonia/AvaloniaExtensions.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using System.Collections; 3 | 4 | namespace StructuredLogViewer.Avalonia 5 | { 6 | public static class AvaloniaExtensions 7 | { 8 | public static void AddItem(this ItemsControl itemsControl, object o) 9 | { 10 | (itemsControl.Items as IList)?.Add(o); 11 | } 12 | 13 | public static void RemoveItem(this ItemsControl itemsControl, object o) 14 | { 15 | (itemsControl.Items as IList)?.Remove(o); 16 | } 17 | 18 | public static void ClearItems(this ItemsControl itemsControl) 19 | { 20 | (itemsControl.Items as IList)?.Clear(); 21 | } 22 | 23 | public static void RegisterControl(this Control parent, out TControl control, string name) 24 | where TControl : Control 25 | { 26 | control = parent.FindControl(name); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/StructuredLogViewer.Avalonia/Controls/DocumentWell.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/StructuredLogViewer/Controls/NavigationHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Build.Logging.StructuredLogger; 3 | 4 | namespace StructuredLogViewer.Controls 5 | { 6 | public class NavigationHelper 7 | { 8 | public Build Build { get; } 9 | public SourceFileResolver SourceFileResolver { get; } 10 | 11 | public event Action OpenFileRequested; 12 | 13 | public NavigationHelper(Build build, SourceFileResolver sourceFileResolver) 14 | { 15 | Build = build; 16 | SourceFileResolver = sourceFileResolver; 17 | } 18 | 19 | public void OpenFile(string filePath) 20 | { 21 | OpenFileRequested?.Invoke(filePath); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/StructuredLogViewer/Controls/ProjectGraphControl.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/StructuredLogViewer/Controls/ProjectGraphControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | using System.Windows.Input; 3 | using Microsoft.Msagl.Drawing; 4 | using Microsoft.Msagl.WpfGraphControl; 5 | 6 | namespace StructuredLogViewer.Controls 7 | { 8 | public partial class ProjectGraphControl : UserControl 9 | { 10 | public ProjectGraphControl() 11 | { 12 | InitializeComponent(); 13 | } 14 | 15 | public void Dispose() 16 | { 17 | Panel.Children.Clear(); 18 | BuildControl = null; 19 | } 20 | 21 | public BuildControl BuildControl { get; set; } 22 | 23 | public void SetGraph(Graph graph) 24 | { 25 | graph.IsVisible = true; 26 | 27 | GraphViewer graphViewer = new GraphViewer(); 28 | 29 | graphViewer.BindToPanel(Panel); 30 | 31 | graphViewer.Graph = graph; 32 | } 33 | 34 | private void TextBlock_MouseUp(object sender, MouseButtonEventArgs e) 35 | { 36 | if (sender is TextBlock textBlock && textBlock.Tag is Block block) 37 | { 38 | BuildControl.SelectItem(block.Node); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/StructuredLogViewer/Controls/SourceFileTab.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace StructuredLogViewer.Controls 4 | { 5 | public class SourceFileTab 6 | { 7 | public string FilePath { get; set; } 8 | public string Text { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/StructuredLogViewer/Controls/SourceFileTabHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Microsoft.Build.Logging.StructuredLogger; 4 | 5 | namespace StructuredLogViewer.Controls 6 | { 7 | public class SourceFileTabHeader 8 | { 9 | private readonly SourceFileTab tab; 10 | 11 | public SourceFileTabHeader(SourceFileTab sourceFileTab) 12 | { 13 | this.tab = sourceFileTab; 14 | Close = new Command(InvokeClose); 15 | Header = Path.GetFileName(tab.FilePath); 16 | } 17 | 18 | public string Header { get; private set; } 19 | 20 | public Command Close { get; } 21 | public event Action CloseRequested; 22 | public void InvokeClose() => CloseRequested?.Invoke(tab); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/StructuredLogViewer/Controls/StringEmptinessToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace StructuredLogViewer 7 | { 8 | public class StringEmptinessToVisibilityConverter : IValueConverter 9 | { 10 | public static readonly StringEmptinessToVisibilityConverter Instance = new StringEmptinessToVisibilityConverter(); 11 | 12 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 13 | { 14 | var text = value as string; 15 | bool result = string.IsNullOrEmpty(text); 16 | if (parameter is string s && s == "Invert") 17 | { 18 | result = !result; 19 | } 20 | 21 | return result ? Visibility.Collapsed : Visibility.Visible; 22 | } 23 | 24 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/StructuredLogViewer/Controls/TimelineControl.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | 15 | 16 | 21 | 25 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/StructuredLogViewer/Entrypoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Windows; 4 | using System.Windows.Threading; 5 | using Microsoft.Build.Logging.StructuredLogger; 6 | 7 | namespace StructuredLogViewer 8 | { 9 | public class Entrypoint 10 | { 11 | [STAThread] 12 | public static void Main(string[] args) 13 | { 14 | ExceptionHandler.Initialize(); 15 | DialogService.ShowMessageBoxEvent += message => MessageBox.Show(message); 16 | ClipboardService.Set += Clipboard.SetText; 17 | 18 | AppDomain.MonitoringIsEnabled = true; 19 | 20 | var app = new Application(); 21 | app.DispatcherUnhandledException += OnDispatcherUnhandledException; 22 | var window = new MainWindow(); 23 | app.Run(window); 24 | 25 | // wait for potential background operations to finish before shutting down 26 | window.InProgressTask.Wait(); 27 | } 28 | 29 | private static void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) 30 | { 31 | ErrorReporting.ReportException(e.Exception); 32 | DialogService.ShowMessageBox( 33 | "Unexpected exception. Sorry about that.\r\nPlease Ctrl+C to copy this text and file an issue at https://github.com/KirillOsenkov/MSBuildStructuredLog/issues/new\r\n\r\n" + e.Exception.ToString()); 34 | e.Handled = true; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/StructuredLogViewer/Resources/SolutionFile.xshd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | VisualStudioVersion 9 | MinimumVisualStudioVersion 10 | Project 11 | EndProject 12 | ProjectSection 13 | EndProjectSection 14 | Global 15 | EndGlobal 16 | GlobalSection 17 | EndGlobalSection 18 | HideSolutionNode 19 | SolutionGuid 20 | 21 | 22 | 23 | 24 | \{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\} 25 | 26 | 27 | 28 | 29 | [=()"]+ 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/StructuredLogViewer/StructuredLogger.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/src/StructuredLogViewer/StructuredLogger.ico -------------------------------------------------------------------------------- /src/StructuredLogViewer/TaskExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace StructuredLogViewer 4 | { 5 | internal static class TaskExtensions 6 | { 7 | /// 8 | /// Consumes a task and doesn't do anything with it. Useful for fire-and-forget calls to async methods within sync methods. 9 | /// 10 | /// The task whose result is to be ignored. 11 | public static void Ignore(this Task task) 12 | { /* this is it */ } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/StructuredLogViewer/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | amd64 arm64 15 | 16 | 17 | 18 | 19 | 20 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/StructuredLogViewer/icons/msbuild-structured-log-viewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/src/StructuredLogViewer/icons/msbuild-structured-log-viewer.png -------------------------------------------------------------------------------- /src/StructuredLogViewer/msbuild-structured-log-viewer.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | msbuild-structured-log-viewer 6 | 1.0.0 7 | https://github.com/KirillOsenkov/MSBuildStructuredLog 8 | flcdrg, KirillOsenkov 9 | MSBuild Structured Log Viewer 10 | Kirill Osenkov 11 | https://msbuildlog.com/ 12 | http://cdn.rawgit.com/KirillOsenkov/MSBuildStructuredLog/main/src/StructuredLogViewer/icons/msbuild-structured-log-viewer.png 13 | 2016 Kirill Osenkov 14 | https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/main/LICENSE 15 | false 16 | https://github.com/KirillOsenkov/MSBuildStructuredLog 17 | https://github.com/KirillOsenkov/MSBuildStructuredLog/wiki 18 | https://github.com/KirillOsenkov/MSBuildStructuredLog/issues 19 | msbuild 20 | A rich interactive log viewer app for MSBuild logs 21 | MSBuild 15.3 introduces a new command-line switch: `/bl` to record all build events to a structured/binary log file: 22 | 23 | Double-click the `.binlog` file to open it in MSBuild Structured Log Viewer: 24 | 25 | ![Screenshot](https://msbuildlog.com/Screenshot1.png) 26 | 27 | Set the default back to the log tab. 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/StructuredLogViewer/tools/.skipAutoUninstall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/src/StructuredLogViewer/tools/.skipAutoUninstall -------------------------------------------------------------------------------- /src/StructuredLogViewer/tools/LICENSE.txt: -------------------------------------------------------------------------------- 1 | From: https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/main/LICENSE 2 | 3 | LICENSE 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2016 Kirill Osenkov 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. -------------------------------------------------------------------------------- /src/StructuredLogViewer/tools/VERIFICATION.txt: -------------------------------------------------------------------------------- 1 | VERIFICATION 2 | Verification is intended to assist the Chocolatey moderators and community 3 | in verifying that this package's contents are trustworthy. 4 | 5 | Original downloads are located at https://github.com/KirillOsenkov/MSBuildStructuredLog/releases 6 | 7 | 1. Download the .exe from the same release version that corresponds to this package version. 8 | 2. Calculate the checksum for the downloaded file and compare that to the one listed for this package on Chocolatey.org -------------------------------------------------------------------------------- /src/StructuredLogViewer/tools/chocolateyinstall.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop'; # stop on all errors 2 | $toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" 3 | $fileLocation = Join-Path $toolsDir 'MSBuildStructuredLogSetup.exe' 4 | 5 | $packageArgs = @{ 6 | packageName = $env:ChocolateyPackageName 7 | unzipLocation = $toolsDir 8 | fileType = 'EXE' 9 | file = $fileLocation 10 | 11 | softwareName = 'MSBuild Structured Log Viewer' 12 | 13 | validExitCodes= @(0) 14 | silentArgs = '-s' # Squirrel 15 | } 16 | 17 | Install-ChocolateyInstallPackage @packageArgs -------------------------------------------------------------------------------- /src/StructuredLogViewer/tools/chocolateyuninstall.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop'; # stop on all errors 2 | 3 | $packageArgs = @{ 4 | packageName = $env:ChocolateyPackageName 5 | softwareName = 'MSBuild Structured Log Viewer' #part or all of the Display Name as you see it in Programs and Features. It should be enough to be unique 6 | fileType = 'EXE' #only one of these: MSI or EXE (ignore MSU for now) 7 | silentArgs = '--uninstall -s' # Squirrel 8 | validExitCodes= @(0) #please insert other valid exit codes here 9 | } 10 | 11 | $uninstall = Get-UninstallRegistryKey -softwareName $packageArgs.softwareName 12 | 13 | $packageArgs.file = ($uninstall.InstallLocation + "\update.exe") 14 | 15 | Uninstall-ChocolateyPackage @packageArgs -------------------------------------------------------------------------------- /src/StructuredLogger.Tests/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/StructuredLogger.Tests/AssertEx.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using DiffPlex.DiffBuilder; 3 | using DiffPlex.DiffBuilder.Model; 4 | using Xunit; 5 | 6 | namespace StructuredLogger.Tests 7 | { 8 | internal static class AssertEx 9 | { 10 | /// 11 | /// Asserts that two strings are equal, and prints a diff between the two if they are not. 12 | /// 13 | /// The expected string. This is presented as the "baseline/before" side in the diff. 14 | /// The actual string. This is presented as the changed or "after" side in the diff. 15 | /// The message to precede the diff, if the values are not equal. 16 | public static void EqualOrDiff(string expected, string actual, string message = null) 17 | { 18 | if (expected == actual) 19 | { 20 | return; 21 | } 22 | 23 | var diffBuilder = new InlineDiffBuilder(new DiffPlex.Differ()); 24 | var diff = diffBuilder.BuildDiffModel(expected, actual, ignoreWhitespace: false); 25 | var messageBuilder = new StringBuilder(); 26 | messageBuilder.AppendLine( 27 | string.IsNullOrEmpty(message) 28 | ? "Actual and expected values differ. Expected shown in baseline of diff:" 29 | : message); 30 | 31 | foreach (var line in diff.Lines) 32 | { 33 | switch (line.Type) 34 | { 35 | case ChangeType.Inserted: 36 | messageBuilder.Append("+"); 37 | break; 38 | case ChangeType.Deleted: 39 | messageBuilder.Append("-"); 40 | break; 41 | default: 42 | messageBuilder.Append(" "); 43 | break; 44 | } 45 | 46 | messageBuilder.AppendLine(line.Text); 47 | } 48 | 49 | Assert.Fail(messageBuilder.ToString()); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/StructuredLogger.Tests/CompilerInvocationTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Build.Framework; 2 | using Microsoft.Build.Logging.StructuredLogger; 3 | using Xunit; 4 | 5 | namespace StructuredLogger.Tests 6 | { 7 | public class CompilerInvocationTests 8 | { 9 | [Theory] 10 | [InlineData( 11 | @"C:\Program Files\dotnet\dotnet.exe exec ""C:\Program Files\dotnet\sdk\5.0.100-rc.2.20479.15\Roslyn\bincore\csc.dll"" /noconfig", "/noconfig")] 12 | [InlineData(@"foo\csc.exe a.cs /out:a.dll", "a.cs /out:a.dll")] 13 | public void Parse1(string arg, string expected) 14 | { 15 | var result = CompilerInvocationsReader.TrimCompilerExeFromCommandLine(arg, CompilerInvocation.CSharp); 16 | Assert.Equal(expected, result); 17 | } 18 | 19 | //[Fact] 20 | internal void ReadRecordsTest() 21 | { 22 | var binlog = @"C:\temp\msbuild.binlog"; 23 | var records = BinaryLog.ReadRecords(binlog); 24 | string lastTask = null; 25 | 26 | foreach (var record in records) 27 | { 28 | if (record.Args is TaskStartedEventArgs taskStarted) 29 | { 30 | lastTask = taskStarted.TaskName; 31 | } 32 | else if (record.Args is TaskParameterEventArgs taskParameters) 33 | { 34 | if (lastTask == "Csc" && taskParameters.ItemType == "Compile") 35 | { 36 | foreach (ITaskItem item in taskParameters.Items) 37 | { 38 | var csFilePath = item.ItemSpec; 39 | } 40 | } 41 | } 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/StructuredLogger.Tests/Differ.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.IO; 3 | 4 | namespace StructuredLogger.Tests 5 | { 6 | public class Differ 7 | { 8 | public static bool AreDifferent(string file1, string file2) 9 | { 10 | var source = File.ReadAllText(file1); 11 | var destination = File.ReadAllText(file2); 12 | if (source != destination) 13 | { 14 | // Process.Start("devenv", $"/diff \"{Path.GetTestFile(file1)}\" \"{Path.GetTestFile(file2)}\""); 15 | return true; 16 | } 17 | else 18 | { 19 | return false; 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/StructuredLogger.Tests/ParseIntoWordsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | using Xunit; 4 | 5 | namespace StructuredLogger.Tests 6 | { 7 | public class ParseIntoWordsTests 8 | { 9 | [Fact] 10 | public void TestParseIntoWords() 11 | { 12 | T("a b", "a", "b"); 13 | T("a \"b c\"", "a", "b c"); 14 | T("a \"b\" c\"", "a", "b", "c\""); 15 | T("a (b c)", "a", "(b c)"); 16 | T("a (b\" c)", "a", "(b\" c)"); 17 | T("a (b\"f\" c)", "a", "(b\"f\" c)"); 18 | T("a \"b(\"", "a", "b("); 19 | T("a \"b)\"", "a", "b)"); 20 | T("a \"(b\"", "a", "(b"); 21 | T("a \")b\"", "a", ")b"); 22 | T("a \")b(\"", "a", ")b("); 23 | T("a \"(b)\"", "a", "(b)"); 24 | } 25 | 26 | private static void T(string query, params string[] expectedParts) 27 | { 28 | var actualParts = ParseIntoWords(query); 29 | Assert.Equal(expectedParts, actualParts); 30 | } 31 | 32 | private static List ParseIntoWords(string query) 33 | { 34 | var result = new List(); 35 | 36 | StringBuilder currentWord = new StringBuilder(); 37 | bool isInParentheses = false; 38 | bool isInQuotes = false; 39 | for (int i = 0; i < query.Length; i++) 40 | { 41 | char c = query[i]; 42 | switch (c) 43 | { 44 | case ' ' when !isInParentheses && !isInQuotes: 45 | result.Add(TrimQuotes(currentWord.ToString())); 46 | currentWord.Clear(); 47 | break; 48 | case '(' when !isInParentheses && !isInQuotes: 49 | isInParentheses = true; 50 | currentWord.Append(c); 51 | break; 52 | case ')' when isInParentheses && !isInQuotes: 53 | isInParentheses = false; 54 | currentWord.Append(c); 55 | break; 56 | case '"' when !isInParentheses: 57 | isInQuotes = !isInQuotes; 58 | currentWord.Append(c); 59 | break; 60 | default: 61 | currentWord.Append(c); 62 | break; 63 | } 64 | } 65 | 66 | result.Add(TrimQuotes(currentWord.ToString())); 67 | 68 | return result; 69 | } 70 | 71 | private static string TrimQuotes(string word) 72 | { 73 | if (word.Length > 2 && word[0] == '"' && word[word.Length - 1] == '"') 74 | { 75 | word = word.Substring(1, word.Length - 2); 76 | } 77 | 78 | return word; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/StructuredLogger.Tests/ResourceStringTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Build.Logging.StructuredLogger; 2 | using Xunit; 3 | 4 | namespace StructuredLogger.Tests 5 | { 6 | public class ResourceStringTests 7 | { 8 | [Fact] 9 | public void TestInitialize() 10 | { 11 | lock (typeof(Strings)) 12 | { 13 | var resources = StringsSet.ResourcesCollection; 14 | var cultures = resources.Keys; 15 | 16 | foreach (var culture in cultures) 17 | { 18 | Strings.Initialize(culture); 19 | Assert.Equal(culture, Strings.ResourceSet.Culture); 20 | Assert.NotNull(Strings.OutputItemsMessagePrefix); 21 | } 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/StructuredLogger.Tests/StructuredLogger.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | StructuredLogger.Tests 4 | net472 5 | true 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | True 37 | ..\StructuredLogger\key.snk 38 | False 39 | 40 | -------------------------------------------------------------------------------- /src/StructuredLogger.Tests/TestUtilities.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.IO; 3 | using System.Reflection; 4 | 5 | namespace StructuredLogger.Tests 6 | { 7 | public class TestUtilities 8 | { 9 | public static string GetTestFile(string fileName) 10 | { 11 | return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), fileName); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/StructuredLogger.Utils/StructuredLogger.Utils.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0;net8.0 4 | true 5 | true 6 | true 7 | 8 | 9 | 10 | 11 | 12 | $(DefineConstants);NETCORE 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | True 24 | ..\StructuredLogger\key.snk 25 | False 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/StructuredLogger/Analyzers/MessageTaskAnalyzer.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace Microsoft.Build.Logging.StructuredLogger 4 | { 5 | public class MessageTaskAnalyzer 6 | { 7 | public static void Analyze(Task task) 8 | { 9 | var message = task.Children.OfType().FirstOrDefault(); 10 | if (message?.ShortenedText != null) 11 | { 12 | task.Name = "Message: " + message.ShortenedText; 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/StructuredLogger/AssemblyLoadBuildEventArgs2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using Microsoft.Build.Framework; 6 | using Microsoft.Build.Shared; 7 | 8 | // TODO: remove whole file once AssemblyLoadBuildEventArgs are imported via Microsoft.Build.Framework package 9 | 10 | namespace Microsoft.Build.Framework 11 | { 12 | internal enum AssemblyLoadingContext 13 | { 14 | TaskRun, 15 | Evaluation, 16 | SdkResolution, 17 | LoggerInitialization, 18 | } 19 | 20 | internal sealed class AssemblyLoadBuildEventArgs : BuildMessageEventArgs 21 | { 22 | private const string DefaultAppDomainDescriptor = "[Default]"; 23 | 24 | public AssemblyLoadBuildEventArgs() 25 | { } 26 | 27 | public AssemblyLoadBuildEventArgs( 28 | AssemblyLoadingContext loadingContext, 29 | string? loadingInitiator, 30 | string? assemblyName, 31 | string assemblyPath, 32 | Guid mvid, 33 | string? customAppDomainDescriptor, 34 | MessageImportance importance = MessageImportance.Low) 35 | : base(null, null, null, importance, DateTime.UtcNow, assemblyName, assemblyPath, mvid) 36 | { 37 | LoadingContext = loadingContext; 38 | LoadingInitiator = loadingInitiator; 39 | AssemblyName = assemblyName; 40 | AssemblyPath = assemblyPath; 41 | MVID = mvid; 42 | AppDomainDescriptor = customAppDomainDescriptor; 43 | } 44 | 45 | public AssemblyLoadingContext LoadingContext { get; private set; } 46 | public string? LoadingInitiator { get; private set; } 47 | public string? AssemblyName { get; private set; } 48 | public string? AssemblyPath { get; private set; } 49 | public Guid MVID { get; private set; } 50 | // Null string indicates that load occurred on Default AppDomain (for both Core and Framework). 51 | public string? AppDomainDescriptor { get; private set; } 52 | 53 | public override string Message 54 | { 55 | get 56 | { 57 | if (RawMessage == null) 58 | { 59 | string? loadingInitiator = LoadingInitiator == null ? null : $" ({LoadingInitiator})"; 60 | RawMessage = string.Format("Assembly loaded during {0}{1}: {2} (location: {3}, MVID: {4}, AppDomain: {5})", LoadingContext.ToString(), loadingInitiator, AssemblyName, AssemblyPath, MVID.ToString(), AppDomainDescriptor ?? DefaultAppDomainDescriptor); 61 | } 62 | 63 | return RawMessage; 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/BinaryLogRecord.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Build.Framework; 2 | using Microsoft.Build.Logging.StructuredLogger; 3 | 4 | namespace Microsoft.Build.Logging 5 | { 6 | public class Record 7 | { 8 | public BinaryLogRecordKind Kind; 9 | public byte[] Bytes; 10 | public BuildEventArgs Args; 11 | public long Start; 12 | public long Length; 13 | } 14 | 15 | public readonly record struct RecordInfo(BinaryLogRecordKind Kind, long Start, long Length); 16 | } 17 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/BinaryLogRecordKind.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public enum BinaryLogRecordKind 4 | { 5 | EndOfFile = 0, 6 | BuildStarted, 7 | BuildFinished, 8 | ProjectStarted, 9 | ProjectFinished, 10 | TargetStarted, 11 | TargetFinished, 12 | TaskStarted, 13 | TaskFinished, 14 | Error, 15 | Warning, 16 | Message, 17 | TaskCommandLine, 18 | CriticalBuildMessage, 19 | ProjectEvaluationStarted, 20 | ProjectEvaluationFinished, 21 | ProjectImported, 22 | ProjectImportArchive, 23 | TargetSkipped, 24 | PropertyReassignment, 25 | UninitializedPropertyRead, 26 | EnvironmentVariableRead, 27 | PropertyInitialValueSet, 28 | NameValueList, 29 | String, 30 | TaskParameter, 31 | FileUsed, 32 | AssemblyLoad, 33 | BuildCheckMessage, 34 | BuildCheckWarning, 35 | BuildCheckError, 36 | BuildCheckTracing, 37 | BuildCheckAcquisition, 38 | BuildSubmissionStarted, 39 | BuildCanceled, 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/BuildCanceledEventArgs.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.IO; 6 | 7 | namespace StructuredLogger.BinaryLogger 8 | { 9 | /// 10 | /// This class represents the event arguments for build canceled events. 11 | /// 12 | internal sealed class BuildCanceledEventArgs : Microsoft.Build.Framework.BuildStatusEventArgs 13 | { 14 | 15 | /// 16 | /// Constructor which allows the timestamp to be set. 17 | /// 18 | /// text message 19 | /// Timestamp when the event was created 20 | public BuildCanceledEventArgs( 21 | string message, 22 | DateTime eventTimestamp) 23 | : this(message, eventTimestamp, null) 24 | { 25 | } 26 | 27 | /// 28 | /// Constructor which allows the timestamp to be set. 29 | /// 30 | /// text message 31 | /// Timestamp when the event was created 32 | /// message arguments 33 | public BuildCanceledEventArgs( 34 | string message, 35 | DateTime eventTimestamp, 36 | params object[]? messageArgs) 37 | : base(message, null, "MSBuild", eventTimestamp, messageArgs) 38 | { 39 | if (string.IsNullOrWhiteSpace(message)) 40 | { 41 | throw new ArgumentException("Message cannot be null or consist only white-space characters."); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/BuildCheckEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.Build.Framework; 4 | 5 | namespace StructuredLogger.BinaryLogger 6 | { 7 | /// 8 | /// Base class for all build check event args. 9 | /// Not intended to be extended by external code. 10 | /// 11 | internal abstract class BuildCheckEventArgs : BuildEventArgs 12 | { } 13 | 14 | /// 15 | /// Transport mean for the BuildCheck tracing data from additional nodes. 16 | /// 17 | /// 18 | internal sealed class BuildCheckTracingEventArgs(Dictionary tracingData) : BuildCheckEventArgs 19 | { 20 | public Dictionary TracingData { get; private set; } = tracingData; 21 | } 22 | 23 | internal sealed class BuildCheckAcquisitionEventArgs(string acquisitionPath, string projectPath) : BuildCheckEventArgs 24 | { 25 | public string AcquisitionPath { get; private set; } = acquisitionPath; 26 | 27 | public string ProjectPath { get; private set; } = projectPath; 28 | } 29 | 30 | internal sealed class BuildCheckResultMessage : BuildMessageEventArgs 31 | { 32 | public BuildCheckResultMessage(string message) => RawMessage = message; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/BuildEventArgsFieldFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Microsoft.Build.Logging 4 | { 5 | /// 6 | /// A bitmask to specify which fields on a BuildEventArgs object are present; used in serialization 7 | /// 8 | [Flags] 9 | internal enum BuildEventArgsFieldFlags 10 | { 11 | None = 0, 12 | BuildEventContext = 1 << 0, 13 | HelpKeyword = 1 << 1, 14 | Message = 1 << 2, 15 | SenderName = 1 << 3, 16 | ThreadId = 1 << 4, 17 | Timestamp = 1 << 5, 18 | Subcategory = 1 << 6, 19 | Code = 1 << 7, 20 | File = 1 << 8, 21 | ProjectFile = 1 << 9, 22 | LineNumber = 1 << 10, 23 | ColumnNumber = 1 << 11, 24 | EndLineNumber = 1 << 12, 25 | EndColumnNumber = 1 << 13, 26 | Arguments = 1 << 14, 27 | Importance = 1 << 15, 28 | Extended = 1 << 16, 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/BuildEventArgsFields.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Build.Framework; 3 | 4 | namespace Microsoft.Build.Logging 5 | { 6 | /// 7 | /// Represents a collective set of common properties on BuildEventArgs. Used for deserialization. 8 | /// 9 | internal class BuildEventArgsFields 10 | { 11 | public BuildEventArgsFieldFlags Flags { get; set; } 12 | 13 | public string Message { get; set; } 14 | public object[] Arguments { get; set; } 15 | public BuildEventContext BuildEventContext { get; set; } 16 | public int ThreadId { get; set; } 17 | public string HelpKeyword { get; set; } 18 | public string SenderName { get; set; } 19 | public DateTime Timestamp { get; set; } 20 | public MessageImportance Importance { get; set; } = MessageImportance.Low; 21 | 22 | public string Subcategory { get; set; } 23 | public string Code { get; set; } 24 | public string File { get; set; } 25 | public string ProjectFile { get; set; } 26 | public int LineNumber { get; set; } 27 | public int ColumnNumber { get; set; } 28 | public int EndLineNumber { get; set; } 29 | public int EndColumnNumber { get; set; } 30 | public ExtendedDataFields Extended { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/EnvironmentVariableReadEventArgs2.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Build.Framework; 2 | 3 | namespace StructuredLogger.BinaryLogger 4 | { 5 | public class EnvironmentVariableReadEventArgs2 : EnvironmentVariableReadEventArgs 6 | { 7 | /// 8 | /// Initializes an instance of the EnvironmentVariableReadEventArgs class. 9 | /// 10 | /// The name of the environment variable that was read. 11 | /// The value of the environment variable that was read. 12 | /// file associated with the event 13 | /// line number (0 if not applicable) 14 | /// column number (0 if not applicable) 15 | public EnvironmentVariableReadEventArgs2( 16 | string environmentVarName, 17 | string environmentVarValue, 18 | string file, 19 | int line, 20 | int column) 21 | : base(environmentVarName, environmentVarValue) 22 | { 23 | LineNumber = line; 24 | ColumnNumber = column; 25 | File = file; 26 | } 27 | 28 | // the properties in the base class have an internal setter 29 | public new int LineNumber { get; set; } 30 | public new int ColumnNumber { get; set; } 31 | public new string File { get; set; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/EvaluationIdProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | //----------------------------------------------------------------------- 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System; 8 | using System.Diagnostics; 9 | using System.Threading; 10 | 11 | namespace Microsoft.Build.Logging.StructuredLogger 12 | { 13 | /// 14 | /// Assigns unique evaluation ids. Thread safe. 15 | /// 16 | internal static class EvaluationIdProvider 17 | { 18 | private static long _sAssignedId = -1; 19 | private static readonly long ProcessId = Process.GetCurrentProcess().Id; 20 | 21 | /// 22 | /// Returns a unique evaluation id 23 | /// 24 | /// 25 | /// The id is guaranteed to be unique across all running processes. 26 | /// Additionally, it is monotonically increasing for callers on the same process id 27 | /// 28 | public static long GetNextId() 29 | { 30 | checked 31 | { 32 | var nextId = Interlocked.Increment(ref _sAssignedId); 33 | // Returns a unique number based on nextId (a unique number for this process) and the current process Id 34 | // Uses the Cantor pairing function (https://en.wikipedia.org/wiki/Pairing_function) to guarantee uniqueness 35 | return (((nextId + ProcessId) * (nextId + ProcessId + 1)) / 2) + ProcessId; 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/ExtendedDataFields.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Microsoft.Build.Logging; 4 | 5 | internal class ExtendedDataFields 6 | { 7 | public string ExtendedType { get; set; } 8 | public IDictionary ExtendedMetadata { get; set; } 9 | public string ExtendedData { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/ExtendedPropertyInitialValueSetEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Build.Framework; 2 | 3 | namespace StructuredLogger.BinaryLogger 4 | { 5 | internal class ExtendedPropertyInitialValueSetEventArgs : BuildMessageEventArgs 6 | { 7 | /// 8 | /// Creates an instance of the class. 9 | /// 10 | /// The name of the property. 11 | /// The value of the property. 12 | /// The source of the property. 13 | /// The file associated with the event. 14 | /// The line number (0 if not applicable). 15 | /// The column number (0 if not applicable). 16 | /// The message of the property. 17 | /// The help keyword. 18 | /// The sender name of the event. 19 | /// The importance of the message. 20 | public ExtendedPropertyInitialValueSetEventArgs( 21 | string propertyName, 22 | string propertyValue, 23 | string propertySource, 24 | string file, 25 | int line, 26 | int column, 27 | string message, 28 | string helpKeyword = null, 29 | string senderName = null, 30 | MessageImportance importance = MessageImportance.Low) 31 | : base(subcategory: null, code: null, file: file, lineNumber: line, columnNumber: column, 0, 0, message, helpKeyword, senderName, importance) 32 | { 33 | PropertyName = propertyName; 34 | PropertyValue = propertyValue; 35 | PropertySource = propertySource; 36 | } 37 | 38 | /// 39 | /// The name of the property. 40 | /// 41 | public string PropertyName { get; set; } 42 | 43 | /// 44 | /// The value of the property. 45 | /// 46 | public string PropertyValue { get; set; } 47 | 48 | /// 49 | /// The source of the property. 50 | /// 51 | public string PropertySource { get; set; } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/ExtendedPropertyReassignmentEventArgs.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Build.Framework; 2 | 3 | namespace StructuredLogger.BinaryLogger 4 | { 5 | internal class ExtendedPropertyReassignmentEventArgs : BuildMessageEventArgs 6 | { 7 | /// 8 | /// Creates an instance of the class. 9 | /// 10 | /// The name of the property whose value was reassigned. 11 | /// The previous value of the reassigned property. 12 | /// The new value of the reassigned property. 13 | /// The file associated with the event. 14 | /// The line number (0 if not applicable). 15 | /// The column number (0 if not applicable). 16 | /// The message of the property. 17 | /// The help keyword. 18 | /// The sender name of the event. 19 | /// The importance of the message. 20 | public ExtendedPropertyReassignmentEventArgs( 21 | string propertyName, 22 | string previousValue, 23 | string newValue, 24 | string file, 25 | int line, 26 | int column, 27 | string message, 28 | string helpKeyword = null, 29 | string senderName = null, 30 | MessageImportance importance = MessageImportance.Low) 31 | : base(subcategory: null, code: null, file: file, lineNumber: line, columnNumber: column, 0, 0, message, helpKeyword, senderName, importance) 32 | { 33 | PropertyName = propertyName; 34 | PreviousValue = previousValue; 35 | NewValue = newValue; 36 | } 37 | 38 | /// 39 | /// The name of the property whose value was reassigned. 40 | /// 41 | public string PropertyName { get; set; } 42 | 43 | /// 44 | /// The previous value of the reassigned property. 45 | /// 46 | public string PreviousValue { get; set; } 47 | 48 | /// 49 | /// The new value of the reassigned property. 50 | /// 51 | public string NewValue { get; set; } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/FileUsedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microsoft.Build.Framework 6 | { 7 | /// 8 | /// Arguments for the response file used event 9 | /// 10 | [Serializable] 11 | public class FileUsedEventArgs : BuildMessageEventArgs 12 | { 13 | public FileUsedEventArgs() 14 | { 15 | } 16 | 17 | /// 18 | /// Initialize a new instance of the ResponseFileUsedEventArgs class. 19 | /// 20 | public FileUsedEventArgs(string responseFilePath) : base() 21 | { 22 | FilePath = responseFilePath; 23 | } 24 | 25 | public string? FilePath { set; get; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/IExtendedBuildEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Microsoft.Build.Framework; 4 | 5 | /// 6 | /// Interface for Extended EventArgs to allow enriching particular events with extended data. 7 | /// Deriving from EventArgs will be deprecated soon and using Extended EventArgs is recommended for custom Event Args. 8 | /// 9 | public interface IExtendedBuildEventArgs 10 | { 11 | /// 12 | /// Unique string identifying type of extended data so receiver side knows how to interpret, deserialize and handle . 13 | /// 14 | string ExtendedType { get; set; } 15 | 16 | /// 17 | /// Metadata of . 18 | /// Example usage: 19 | /// - data which needed in custom code to properly routing this message without interpreting/deserializing . 20 | /// - simple extended data can be transferred in form of dictionary key-value per one extended property. 21 | /// 22 | IDictionary? ExtendedMetadata { get; set; } 23 | 24 | /// 25 | /// Transparent data as string. 26 | /// Custom code is responsible to serialize and deserialize this string to structured data - if needed. 27 | /// Custom code can use any serialization they deem safe - e.g. json for textual data, base64 for binary data... 28 | /// 29 | string? ExtendedData { get; set; } 30 | } 31 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/IsExternalInit.cs: -------------------------------------------------------------------------------- 1 | namespace System.Runtime.CompilerServices 2 | { 3 | // This is a dummy class to make C# 9.0 init-only properties work in Full FW. 4 | // See https://developercommunity.visualstudio.com/t/error-cs0518-predefined-type-systemruntimecompiler/1244809 5 | internal static class IsExternalInit { } 6 | } 7 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/Postprocessing/ArchiveFileEventArgs.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | 6 | namespace Microsoft.Build.Logging.StructuredLogger; 7 | 8 | /// 9 | /// Event arguments for event. 10 | /// 11 | public sealed class ArchiveFileEventArgs : EventArgs 12 | { 13 | public ArchiveFileEventArgs(ArchiveFile archiveFile) => 14 | ArchiveFile = archiveFile; 15 | 16 | public ArchiveFile ArchiveFile { get; set; } 17 | } 18 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/Postprocessing/ArchiveFileEventArgsExtensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | 6 | namespace Microsoft.Build.Logging.StructuredLogger; 7 | 8 | public static class ArchiveFileEventArgsExtensions 9 | { 10 | public static Action ToArchiveFileHandler(this Action stringHandler) 11 | { 12 | return args => 13 | { 14 | var pathArgs = new StringReadEventArgs(args.ArchiveFile.FullPath); 15 | stringHandler(pathArgs); 16 | var contentArgs = new StringReadEventArgs(args.ArchiveFile.Text); 17 | stringHandler(contentArgs); 18 | 19 | args.ArchiveFile = new ArchiveFile(pathArgs.StringToBeUsed, contentArgs.StringToBeUsed); 20 | }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/Postprocessing/BinaryLogReaderErrorEventArgs.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | 6 | namespace Microsoft.Build.Logging.StructuredLogger 7 | { 8 | /// 9 | /// Materializes the error message. 10 | /// Until it's called the error message is not materialized and no string allocations are made. 11 | /// 12 | /// The error message. 13 | internal delegate string FormatErrorMessage(); 14 | 15 | /// 16 | /// An event args for event. 17 | /// 18 | public sealed class BinaryLogReaderErrorEventArgs : EventArgs 19 | { 20 | private readonly FormatErrorMessage _formatErrorMessage; 21 | 22 | internal BinaryLogReaderErrorEventArgs( 23 | ReaderErrorType errorType, 24 | BinaryLogRecordKind recordKind, 25 | FormatErrorMessage formatErrorMessage) 26 | { 27 | ErrorType = errorType; 28 | RecordKind = recordKind; 29 | _formatErrorMessage = formatErrorMessage; 30 | } 31 | 32 | /// 33 | /// Type of the error that occurred during reading. 34 | /// 35 | public ReaderErrorType ErrorType { get; } 36 | 37 | /// 38 | /// Kind of the record that encountered the error. 39 | /// 40 | public BinaryLogRecordKind RecordKind { get; } 41 | 42 | /// 43 | /// Materializes the error message. 44 | /// Until it's called the error message is not materialized and no string allocations are made. 45 | /// 46 | /// The error message. 47 | public string GetFormattedMessage() => _formatErrorMessage(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/Postprocessing/EmbeddedContentEventArgs.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.IO; 6 | 7 | namespace Microsoft.Build.Logging.StructuredLogger 8 | { 9 | internal sealed class EmbeddedContentEventArgs : EventArgs 10 | { 11 | public EmbeddedContentEventArgs(BinaryLogRecordKind contentKind, Stream contentStream) 12 | { 13 | ContentKind = contentKind; 14 | ContentStream = contentStream; 15 | } 16 | 17 | public BinaryLogRecordKind ContentKind { get; } 18 | public Stream ContentStream { get; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/Postprocessing/IBuildEventArgsReaderNotifications.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | 6 | namespace Microsoft.Build.Logging.StructuredLogger 7 | { 8 | /// 9 | /// An interface for notifications from BuildEventArgsReader 10 | /// 11 | public interface IBuildEventArgsReaderNotifications 12 | { 13 | /// 14 | /// An event that allows the subscriber to be notified when a string is read from the binary log. 15 | /// Subscriber may adjust the string by setting property. 16 | /// The passed event arg can be reused and should not be stored. 17 | /// 18 | public event Action? StringReadDone; 19 | 20 | /// 21 | /// An event that allows the caller to be notified when an embedded file is encountered in the binary log. 22 | /// When subscriber is OK with greedy reading entire content of the file and is interested only in the individual strings (e.g. for sensitive data redaction purposes), 23 | /// it can simplify subscribing to this event, by using handler with same signature as handler for and wrapping it via 24 | /// extension. 25 | /// 26 | /// 27 | /// 28 | /// private void OnStringReadDone(StringReadEventArgs e) 29 | /// { 30 | /// e.StringToBeUsed = e.StringToBeUsed.Replace("foo", "bar"); 31 | /// } 32 | /// 33 | /// private void SubscribeToEvents() 34 | /// { 35 | /// reader.StringReadDone += OnStringReadDone; 36 | /// reader.ArchiveFileEncountered += ((Action<StringReadEventArgs>)OnStringReadDone).ToArchiveFileHandler(); 37 | /// } 38 | /// 39 | /// 40 | public event Action? ArchiveFileEncountered; 41 | 42 | /// 43 | /// Receives recoverable errors during reading. 44 | /// Communicates type of the error, kind of the record that encountered the error and the message detailing the error. 45 | /// In case of this is raised before returning the structured representation of a build event 46 | /// that has some extra unknown data in the binlog. In case of other error types this event is raised and the offending build event is skipped and not returned. 47 | /// 48 | event Action? RecoverableReadError; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/Postprocessing/ReaderErrorType.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace Microsoft.Build.Logging.StructuredLogger; 5 | 6 | /// 7 | /// Type of the error that occurred during reading. 8 | /// 9 | public enum ReaderErrorType 10 | { 11 | /// 12 | /// The encountered event is completely unknown to the reader. It cannot interpret any part of it. 13 | /// 14 | UnknownEventType, 15 | 16 | /// 17 | /// The encountered event is known to the reader and reader is able to read the event as it knows it. 18 | /// However there are some extra data (append only extension to the event in future version), that reader cannot interpret, 19 | /// it can only skip it. 20 | /// 21 | UnknownEventData, 22 | 23 | /// 24 | /// The encountered event type is known to the reader, but the reader cannot interpret the data of the event. 25 | /// This is probably caused by an event definition changing more than just adding fields. 26 | /// The reader can only skip the event in full. 27 | /// 28 | UnknownFormatOfEventData, 29 | } 30 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/Postprocessing/StreamChunkOverreadException.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | 6 | namespace Microsoft.Build.Logging.StructuredLogger 7 | { 8 | public class StreamChunkOverReadException : Exception 9 | { 10 | public StreamChunkOverReadException() 11 | { 12 | } 13 | 14 | public StreamChunkOverReadException(string message) : base(message) 15 | { 16 | } 17 | 18 | public StreamChunkOverReadException(string message, Exception inner) : base(message, inner) 19 | { 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/Postprocessing/StringReadEventArgs.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | 6 | namespace Microsoft.Build.Logging.StructuredLogger 7 | { 8 | /// 9 | /// An event args for callback. 10 | /// 11 | public sealed class StringReadEventArgs : EventArgs 12 | { 13 | /// 14 | /// The original string that was read from the binary log. 15 | /// 16 | public string OriginalString { get; private set; } 17 | 18 | /// 19 | /// The adjusted string (or the original string of none subscriber replaced it) that will be used by the reader. 20 | /// 21 | public string StringToBeUsed { get; set; } 22 | 23 | public StringReadEventArgs(string str) 24 | { 25 | OriginalString = str; 26 | StringToBeUsed = str; 27 | } 28 | 29 | internal void Reuse(string newValue) 30 | { 31 | OriginalString = newValue; 32 | StringToBeUsed = newValue; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/ProjectImportsCollectorExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Build.Framework; 2 | 3 | namespace Microsoft.Build.Logging 4 | { 5 | // This functionality is separate from SourceFileCollector to avoid having SourceFileCollector 6 | // depend on MSBuild. It seems like a more standalone and useful utility and it feels better to 7 | // have the population from BuildEventArgs in a separate file. 8 | internal static class ProjectImportsCollectorExtensions 9 | { 10 | public static void IncludeSourceFiles(this ProjectImportsCollector projectImportsCollector, BuildEventArgs e) 11 | { 12 | if (e is TaskStartedEventArgs) 13 | { 14 | var taskArgs = (TaskStartedEventArgs)e; 15 | projectImportsCollector.AddFile(taskArgs.TaskFile); 16 | } 17 | else if (e is TargetStartedEventArgs) 18 | { 19 | var targetArgs = (TargetStartedEventArgs)e; 20 | projectImportsCollector.AddFile(targetArgs.TargetFile); 21 | } 22 | else if (e is ProjectStartedEventArgs) 23 | { 24 | var projectStarted = (ProjectStartedEventArgs)e; 25 | projectImportsCollector.AddFile(projectStarted.ProjectFile); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/ResourceUtilities.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Shared 2 | { 3 | internal class ResourceUtilities 4 | { 5 | internal static string FormatResourceString(out string errorCode, out string helpKeyword, string text, string filePath, string message) 6 | { 7 | errorCode = "MSB0001"; 8 | helpKeyword = ""; 9 | return message; 10 | } 11 | 12 | internal static string FormatResourceStringStripCodeAndKeyword(out string errorCode, out string helpKeyword, string text, string filePath, string message) 13 | { 14 | errorCode = "MSB0001"; 15 | helpKeyword = ""; 16 | return message; 17 | } 18 | 19 | internal static string FormatResourceString(string v1, string v2) 20 | { 21 | return v1; 22 | } 23 | 24 | internal static string FormatResourceStringStripCodeAndKeyword(string v1, string v2) 25 | { 26 | return v1; 27 | } 28 | 29 | internal static string GetResourceString(string s) => s; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/TaskParameterEventArgs2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | 4 | namespace Microsoft.Build.Framework 5 | { 6 | public class TaskParameterEventArgs2 : TaskParameterEventArgs 7 | { 8 | public TaskParameterEventArgs2( 9 | TaskParameterMessageKind kind, 10 | string parameterName, 11 | string propertyName, 12 | string itemType, 13 | IList items, 14 | bool logItemMetadata, 15 | DateTime eventTimestamp) 16 | : base(kind, itemType, items, logItemMetadata, eventTimestamp) 17 | { 18 | ParameterName = parameterName; 19 | PropertyName = propertyName; 20 | } 21 | 22 | // Added in MSBuild 17.11 23 | public string ParameterName { get; set; } 24 | public string PropertyName { get; set; } 25 | 26 | // the properties in the base class have an internal setter 27 | public new int LineNumber { get; set; } 28 | public new int ColumnNumber { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/StructuredLogger/BinaryLogger/TaskStartedEventArgs2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Microsoft.Build.Framework 4 | { 5 | public class TaskStartedEventArgs2 : TaskStartedEventArgs 6 | { 7 | public TaskStartedEventArgs2( 8 | string message, 9 | string helpKeyword, 10 | string projectFile, 11 | string taskFile, 12 | string taskName, 13 | DateTime eventTimestamp) 14 | : base( 15 | message, 16 | helpKeyword, 17 | projectFile, 18 | taskFile, 19 | taskName, 20 | eventTimestamp) 21 | { 22 | } 23 | 24 | // the properties in the base class have an internal setter 25 | public new int LineNumber { get; set; } 26 | public new int ColumnNumber { get; set; } 27 | public string TaskAssemblyLocation { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/StructuredLogger/Construction/BuildStatistics.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Microsoft.Build.Logging.StructuredLogger 5 | { 6 | public class BuildStatistics 7 | { 8 | public int Tasks; 9 | 10 | public Dictionary> TaskParameterMessagesByTask = new Dictionary>(); 11 | public Dictionary> OutputItemMessagesByTask = new Dictionary>(); 12 | 13 | public int TimedNodeCount { get; set; } 14 | 15 | public void ReportTaskParameterMessage(Task task, string message) 16 | { 17 | Add(task.Name, message, TaskParameterMessagesByTask); 18 | } 19 | 20 | public void ReportOutputItemMessage(Task task, string message) 21 | { 22 | Add(task.Name, message, OutputItemMessagesByTask); 23 | } 24 | 25 | public void Add(string key, string value, Dictionary> dictionary) 26 | { 27 | if (!dictionary.TryGetValue(key, out var bucket)) 28 | { 29 | bucket = new List(); 30 | dictionary[key] = bucket; 31 | } 32 | 33 | bucket.Add(value); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/StructuredLogger/CustomAppDomainManager.cs: -------------------------------------------------------------------------------- 1 | #if !NETCORE 2 | 3 | using System; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Security.Policy; 7 | 8 | namespace Microsoft.Build.Logging.StructuredLogger 9 | { 10 | /// 11 | /// We want a callback when an appdomain is created, to install the MSBuildLocator 12 | /// into the XAML markup compilation appdomain. 13 | /// See https://github.com/KirillOsenkov/AppDomainManagerTest 14 | /// 15 | /// 16 | /// This class can't be in the entrypoint assembly because of a CLR bug. 17 | /// We need a way to callback the entrypoint assembly when a new appdomain 18 | /// is created. Let's just instantiate a well-known type from the 19 | /// entrypoint assembly. 20 | /// 21 | public class CustomAppDomainManager : AppDomainManager 22 | { 23 | /// 24 | /// This runs in the old appdomain when a new appdomain is created 25 | /// 26 | public override AppDomain CreateDomain(string friendlyName, Evidence securityInfo, AppDomainSetup appDomainInfo) 27 | { 28 | var result = base.CreateDomain(friendlyName, securityInfo, appDomainInfo); 29 | return result; 30 | } 31 | 32 | /// 33 | /// This runs in the new appdomain very early when it is being initialized 34 | /// 35 | public override void InitializeNewDomain(AppDomainSetup appDomainInfo) 36 | { 37 | base.InitializeNewDomain(appDomainInfo); 38 | NotifyEntrypointAssembly(); 39 | } 40 | 41 | private void NotifyEntrypointAssembly() 42 | { 43 | if (AppDomain.CurrentDomain.IsDefaultAppDomain()) 44 | { 45 | return; 46 | } 47 | 48 | var assembly = Assembly.Load("TaskRunner"); 49 | if (assembly == null) 50 | { 51 | return; 52 | } 53 | 54 | var type = assembly.GetType("TaskRunner.AppDomainInitializer"); 55 | if (type == null) 56 | { 57 | return; 58 | } 59 | 60 | Activator.CreateInstance(type); 61 | } 62 | } 63 | } 64 | 65 | #endif -------------------------------------------------------------------------------- /src/StructuredLogger/ErrorReporting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Microsoft.Build.Logging.StructuredLogger 5 | { 6 | public class ErrorReporting 7 | { 8 | private static readonly string logFilePath = Path.Combine(PathUtils.RootPath, "LoggerExceptions.txt"); 9 | 10 | public static string LogFilePath => logFilePath; 11 | 12 | public static void ReportException(Exception ex) 13 | { 14 | if (ex == null) 15 | { 16 | return; 17 | } 18 | 19 | try 20 | { 21 | Directory.CreateDirectory(Path.GetDirectoryName(logFilePath)); 22 | 23 | // if the log has gotten too big, delete it 24 | if (File.Exists(logFilePath) && new FileInfo(logFilePath).Length > 10000000) 25 | { 26 | File.Delete(logFilePath); 27 | } 28 | 29 | File.AppendAllText(logFilePath, ex.ToString() + Environment.NewLine); 30 | } 31 | catch (Exception) 32 | { 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/AbstractDiagnostic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Microsoft.Build.Logging.StructuredLogger 4 | { 5 | public class AbstractDiagnostic : TextNode, IHasSourceFile 6 | { 7 | public DateTime Timestamp { get; set; } 8 | public string Code { get; set; } 9 | public int ColumnNumber { get; set; } 10 | public int EndColumnNumber { get; set; } 11 | public int EndLineNumber { get; set; } 12 | public string File { get; set; } 13 | public int LineNumber { get; set; } 14 | public string ProjectFile { get; set; } 15 | public string Subcategory { get; set; } 16 | 17 | string IHasSourceFile.SourceFilePath => File; 18 | 19 | public override string TypeName => nameof(AbstractDiagnostic); 20 | 21 | public override string Title => ToString(); 22 | 23 | public override string ToString() 24 | { 25 | File ??= ""; 26 | 27 | string position = ""; 28 | if (LineNumber != 0 || ColumnNumber != 0) 29 | { 30 | string column = ""; 31 | if (ColumnNumber > 0) 32 | { 33 | column = "," + ColumnNumber.ToString(); 34 | } 35 | 36 | position = $"({LineNumber}{column}):"; 37 | } 38 | 39 | string code = ""; 40 | if (!string.IsNullOrWhiteSpace(Code)) 41 | { 42 | code = $" {this.GetType().Name.ToLowerInvariant()} {Code}:"; 43 | } 44 | 45 | string text = Text; 46 | if (File.Length + position.Length + code.Length > 0) 47 | { 48 | text = " " + text; 49 | } 50 | 51 | string projectFile = ""; 52 | if (!string.IsNullOrWhiteSpace(ProjectFile)) 53 | { 54 | projectFile = $" [{ProjectFile}]"; 55 | } 56 | 57 | return $"{File}{position}{code}{text}{projectFile}"; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/AddItem.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class AddItem : NamedNode, IHasLineNumber 4 | { 5 | public AddItem() 6 | { 7 | DisableChildrenCache = true; 8 | } 9 | 10 | public override string TypeName => nameof(AddItem); 11 | 12 | public int? LineNumber { get; set; } 13 | } 14 | 15 | public class TaskParameterItem : AddItem 16 | { 17 | public string ParameterName { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/ArchiveFile.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.IO.Compression; 3 | 4 | namespace Microsoft.Build.Logging.StructuredLogger 5 | { 6 | public class ArchiveFile 7 | { 8 | public ArchiveFile(string fullPath, string text) 9 | { 10 | FullPath = fullPath; 11 | Text = text; 12 | } 13 | 14 | public string FullPath { get; } 15 | public string Text { get; } 16 | 17 | public static ArchiveFile From(ZipArchiveEntry entry) 18 | => From(entry, adjustPath: true); 19 | 20 | public static ArchiveFile From(ZipArchiveEntry entry, bool adjustPath) 21 | { 22 | var filePath = adjustPath ? CalculateArchivePath(entry.FullName) : entry.FullName; 23 | var text = GetText(entry); 24 | var file = new ArchiveFile(filePath, text); 25 | return file; 26 | } 27 | 28 | public static string GetText(ZipArchiveEntry entry) 29 | { 30 | using (var contentStream = entry.Open()) 31 | using (var reader = new StreamReader(contentStream)) 32 | { 33 | var text = reader.ReadToEnd(); 34 | return text; 35 | } 36 | } 37 | 38 | public static string CalculateArchivePath(string filePath) 39 | { 40 | string archivePath = filePath; 41 | 42 | if (archivePath.Length > 1 && archivePath[1] == '\\' && archivePath[0] != '\\' && archivePath[0] != '/') 43 | { 44 | archivePath = archivePath[0] + ":" + archivePath.Substring(1); 45 | } 46 | 47 | archivePath = TextUtilities.NormalizeFilePath(archivePath); 48 | 49 | return archivePath; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/CriticalBuildMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class CriticalBuildMessage : AbstractDiagnostic 4 | { 5 | public override string TypeName => nameof(CriticalBuildMessage); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/EntryTarget.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | namespace Microsoft.Build.Logging.StructuredLogger 4 | { 5 | public class EntryTarget : NamedNode, IHasRelevance 6 | { 7 | private Project? project; 8 | private Target? target; 9 | public override string TypeName => nameof(EntryTarget); 10 | private Project? Project => project ??= GetNearestParent(); 11 | public Target? Target => target ??= Project?.FindFirstChild(t => t.Name == Name); 12 | public bool IsLowRelevance => Target?.IsLowRelevance ?? true; 13 | public string? DurationText => Target?.DurationText; 14 | } 15 | } -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Error.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class Error : AbstractDiagnostic 4 | { 5 | public override string TypeName => nameof(Error); 6 | } 7 | 8 | public class BuildError : Error 9 | { 10 | public override string TypeName => "Build Error"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/EvaluationProfileEntry.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Microsoft.Build.Framework.Profiler; 3 | 4 | namespace Microsoft.Build.Logging.StructuredLogger 5 | { 6 | public class EvaluationProfileEntry : TreeNode, IHasSourceFile, IHasLineNumber 7 | { 8 | public string ElementName { get; set; } 9 | public string ElementDescription { get; set; } 10 | public string EvaluationPassDescription { get; set; } 11 | public EvaluationLocationKind Kind { get; set; } 12 | public string SourceFilePath { get; set; } 13 | public int? LineNumber { get; set; } 14 | public string NumberOfHits => ProfiledLocation.NumberOfHits > 0 ? ProfiledLocation.NumberOfHits.ToString() : ""; 15 | 16 | public override string TypeName => nameof(EvaluationProfileEntry); 17 | 18 | public ProfiledLocation ProfiledLocation { get; private set; } 19 | 20 | public void AddEntry(ProfiledLocation result) 21 | { 22 | ProfiledLocation = result; 23 | } 24 | 25 | public string DurationText 26 | { 27 | get 28 | { 29 | if (ProfiledLocation.InclusiveTime.TotalMilliseconds == 0) 30 | { 31 | return ""; 32 | } 33 | 34 | var result = TextUtilities.DisplayDuration(ProfiledLocation.InclusiveTime); 35 | 36 | var hits = ProfiledLocation.NumberOfHits; 37 | if (hits == 1) 38 | { 39 | result += " (1 hit)"; 40 | } 41 | else if (hits > 1) 42 | { 43 | result += " (" + hits + " hits)"; 44 | } 45 | 46 | return result; 47 | } 48 | } 49 | 50 | public string FileName => Path.GetFileName(SourceFilePath); 51 | 52 | public override string Title 53 | { 54 | get 55 | { 56 | if (SourceFilePath == null) 57 | { 58 | return EvaluationPassDescription; 59 | } 60 | 61 | if (Kind == EvaluationLocationKind.Element) 62 | { 63 | return $"{FileName}:{LineNumber}"; 64 | } 65 | 66 | return $"{FileName}:{LineNumber} {Kind}"; 67 | } 68 | } 69 | 70 | public string ShortenedElementDescription => TextUtilities.ShortenValue(ElementDescription, "...", maxChars: 80); 71 | 72 | public double Value { get; set; } 73 | 74 | public override string ToString() 75 | { 76 | return $"{Title}"; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Folder.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class Folder : NamedNode, IHasRelevance 4 | { 5 | public bool IsLowRelevance 6 | { 7 | get => HasFlag(NodeFlags.LowRelevance) && !IsSelected; 8 | set => SetFlag(NodeFlags.LowRelevance, value); 9 | } 10 | 11 | public override string TypeName => nameof(Folder); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/IHasExtendedData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Microsoft.Build.Logging.StructuredLogger; 4 | 5 | public interface IHasExtendedData 6 | { 7 | string ExtendedType { get; } 8 | IDictionary? ExtendedMetadata { get; } 9 | string? ExtendedData { get; } 10 | } 11 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/IHasLineNumber.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public interface IHasLineNumber 4 | { 5 | int? LineNumber { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/IHasRelevance.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public interface IHasRelevance 4 | { 5 | bool IsLowRelevance { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/IHasSourceFile.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public interface IHasSourceFile 4 | { 5 | string SourceFilePath { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/IPreprocessable.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public interface IPreprocessable 4 | { 5 | string RootFilePath { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Import.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class Import : TextNode, IHasRelevance, IPreprocessable, IHasSourceFile, IHasLineNumber 4 | { 5 | public string ProjectFilePath { get; set; } 6 | public string ImportedProjectFilePath { get; set; } 7 | public int Line { get; set; } 8 | public int Column { get; set; } 9 | 10 | public Import() 11 | { 12 | } 13 | 14 | public Import( 15 | string projectFilePath, 16 | string importedProjectFilePath, 17 | int line, 18 | int column) 19 | { 20 | ProjectFilePath = projectFilePath; 21 | ImportedProjectFilePath = importedProjectFilePath; 22 | Line = line; 23 | Column = column; 24 | 25 | Text = importedProjectFilePath; 26 | } 27 | 28 | public string Location => $" at ({Line};{Column})"; 29 | 30 | public override string TypeName => nameof(Import); 31 | 32 | public bool IsLowRelevance 33 | { 34 | get => HasFlag(NodeFlags.LowRelevance) && !IsSelected; 35 | set => SetFlag(NodeFlags.LowRelevance, value); 36 | } 37 | 38 | string IPreprocessable.RootFilePath => ImportedProjectFilePath; 39 | 40 | string IHasSourceFile.SourceFilePath => ProjectFilePath; 41 | int? IHasLineNumber.LineNumber => Line; 42 | 43 | public override string ToString() => $"Import: {Text}{Location}"; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Item.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | /// 4 | /// Class representation of an item/property with associated metadata (if any). 5 | /// 6 | public class Item : NamedNode 7 | { 8 | public Item() 9 | { 10 | DisableChildrenCache = true; 11 | } 12 | 13 | public override string TypeName => nameof(Item); 14 | 15 | public string Text 16 | { 17 | get => Name; 18 | set => Name = value; 19 | } 20 | } 21 | 22 | public class FileCopy : Item 23 | { 24 | public string Kind { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Metadata.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class Metadata : NameValueNode 4 | { 5 | public override string TypeName => nameof(Metadata); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/NameValueNode.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class NameValueNode : BaseNode 4 | { 5 | public string Name { get; set; } 6 | public string Value { get; set; } 7 | public string NameAndEquals => Name + " = "; 8 | public string ShortenedValue => TextUtilities.ShortenValue(Value); 9 | public bool IsValueShortened => Value != ShortenedValue; 10 | 11 | public override string TypeName => nameof(NameValueNode); 12 | public override string Title => Name; 13 | 14 | public override string ToString() => Name + " = " + Value; 15 | public override string GetFullText() => ToString(); 16 | public bool IsVisible { get => true; set { } } 17 | public bool IsExpanded { get => true; set { } } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/NamedNode.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class NamedNode : TreeNode 4 | { 5 | public virtual string Name { get; set; } 6 | public string ShortenedName => TextUtilities.ShortenValue(Name); 7 | public bool IsNameShortened => Name != ShortenedName; 8 | 9 | public override string TypeName => nameof(NamedNode); 10 | public override string Title => Name; 11 | 12 | public override string ToString() => Name; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/NoImport.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class NoImport : TextNode, IHasRelevance, IHasSourceFile, IHasLineNumber 4 | { 5 | public string ProjectFilePath { get; set; } 6 | public string ImportedFileSpec { get; set; } 7 | public string Reason { get; set; } 8 | public int Line { get; set; } 9 | public int Column { get; set; } 10 | 11 | public NoImport() 12 | { 13 | } 14 | 15 | public override string TypeName => nameof(NoImport); 16 | 17 | public NoImport( 18 | string projectFilePath, 19 | string importedFileSpec, 20 | int line, 21 | int column, 22 | string reason) 23 | : this() 24 | { 25 | ProjectFilePath = projectFilePath; 26 | Text = importedFileSpec; 27 | Line = line; 28 | Column = column; 29 | Reason = reason; 30 | } 31 | 32 | public string Location => $" at ({Line};{Column})"; 33 | 34 | public bool IsLowRelevance 35 | { 36 | get => HasFlag(NodeFlags.LowRelevance) && !IsSelected; 37 | set => SetFlag(NodeFlags.LowRelevance, value); 38 | } 39 | 40 | string IHasSourceFile.SourceFilePath => ProjectFilePath; 41 | int? IHasLineNumber.LineNumber => Line; 42 | 43 | public override string ToString() => $"NoImport: {Text}{Location} {Reason}"; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/NodeFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Microsoft.Build.Logging.StructuredLogger 4 | { 5 | [Flags] 6 | internal enum NodeFlags : byte 7 | { 8 | None = 0, 9 | Hidden = 1 << 0, 10 | Expanded = 1 << 1, 11 | SearchResult = 1 << 2, 12 | ContainsSearchResult = 1 << 3, 13 | LowRelevance = 1 << 4, 14 | DisableChildrenCache = 1 << 5 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Note.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class Note : TextNode 4 | { 5 | protected override bool IsSelectable => false; 6 | 7 | public override string TypeName => nameof(Note); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/ObservableObject.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace Microsoft.Build.Logging.StructuredLogger 6 | { 7 | public abstract class ObservableObject : INotifyPropertyChanged 8 | { 9 | public event PropertyChangedEventHandler PropertyChanged; 10 | 11 | protected bool SetField(ref TField field, TField newValue, [CallerMemberName] string propertyName = null) 12 | { 13 | if (EqualityComparer.Default.Equals(field, newValue)) 14 | { 15 | return false; 16 | } 17 | 18 | field = newValue; 19 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 20 | 21 | return true; 22 | } 23 | 24 | protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) 25 | => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Package.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class Package : NamedNode 4 | { 5 | public override string TypeName => nameof(Package); 6 | 7 | public string Version { get; set; } 8 | 9 | public string VersionSpec { get; set; } 10 | 11 | public override string ToString() 12 | { 13 | string result = Name; 14 | if (!string.IsNullOrEmpty(Version)) 15 | { 16 | result = $"{result} {Version}"; 17 | } 18 | 19 | if (!string.IsNullOrEmpty(VersionSpec)) 20 | { 21 | result = $"{result} {VersionSpec}"; 22 | } 23 | 24 | return result; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Parameter.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class Parameter : NamedNode 4 | { 5 | public override string TypeName => nameof(Parameter); 6 | 7 | public string ParameterName { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Property.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class Property : NameValueNode 4 | { 5 | public override string TypeName => nameof(Property); 6 | } 7 | 8 | public class TaskParameterProperty : Property 9 | { 10 | public string ParameterName { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/RemoveItem.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class RemoveItem : NamedNode, IHasLineNumber 4 | { 5 | public RemoveItem() 6 | { 7 | DisableChildrenCache = true; 8 | } 9 | 10 | public override string TypeName => nameof(RemoveItem); 11 | 12 | public int? LineNumber { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/SearchableItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microsoft.Build.Logging.StructuredLogger 6 | { 7 | public class SearchableItem : Item 8 | { 9 | public SearchableItem() : base() { } 10 | 11 | public string SearchText 12 | { 13 | get { return _searchText ?? this.Text; } 14 | set { _searchText = value; } 15 | } 16 | 17 | private string _searchText; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/SourceFile.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class SourceFile : NamedNode, IHasSourceFile 4 | { 5 | public string SourceFilePath { get; set; } 6 | public override string TypeName => nameof(SourceFile); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/SourceFileLine.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class SourceFileLine : TreeNode 4 | { 5 | public int LineNumber { get; set; } 6 | public string LineText { get; set; } 7 | 8 | public override string TypeName => nameof(SourceFileLine); 9 | 10 | public override string ToString() 11 | { 12 | return LineNumber.ToString().PadRight(5, ' ') + LineText; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Task.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Microsoft.Build.Logging.StructuredLogger 5 | { 6 | public class Task : TimedNode, IHasSourceFile, IHasLineNumber 7 | { 8 | public string FromAssembly { get; set; } 9 | public string CommandLineArguments { get; set; } 10 | public string SourceFilePath { get; set; } 11 | 12 | public override string TypeName => nameof(Task); 13 | 14 | public virtual bool IsDerivedTask => this.GetType() != typeof(Task); 15 | 16 | public int? LineNumber { get; set; } 17 | 18 | public IReadOnlyList GetMessages() 19 | { 20 | TreeNode node = this; 21 | if (this.FindChild(Strings.Messages) is Folder messagesFolder) 22 | { 23 | node = messagesFolder; 24 | } 25 | 26 | return node.Children.OfType().ToArray(); 27 | } 28 | } 29 | 30 | public class MSBuildTask : Task 31 | { 32 | //public override string TypeName => nameof(MSBuildTask); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/TaskItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using Microsoft.Build.Framework; 5 | 6 | namespace Microsoft.Build.Logging 7 | { 8 | public class TaskItem : ITaskItem2 9 | { 10 | public string ItemSpec { get; set; } 11 | public Dictionary Metadata { get; } = new Dictionary(); 12 | public string EvaluatedIncludeEscaped { get; set; } 13 | 14 | public int MetadataCount => Metadata.Count; 15 | public ICollection MetadataNames => Metadata.Keys; 16 | 17 | public TaskItem() 18 | { 19 | } 20 | 21 | public TaskItem(string itemSpec) 22 | { 23 | ItemSpec = itemSpec; 24 | } 25 | 26 | public IDictionary CloneCustomMetadata() 27 | { 28 | return Metadata; 29 | } 30 | 31 | public IDictionary CloneCustomMetadataEscaped() 32 | { 33 | throw new NotImplementedException(); 34 | } 35 | 36 | public void CopyMetadataTo(ITaskItem destinationItem) 37 | { 38 | foreach (var kvp in Metadata) 39 | { 40 | destinationItem.SetMetadata(kvp.Key, kvp.Value); 41 | } 42 | } 43 | 44 | public string GetMetadata(string metadataName) 45 | { 46 | if (!Metadata.TryGetValue(metadataName, out var result)) 47 | { 48 | if (metadataName == "FullPath") 49 | { 50 | return ItemSpec; 51 | } 52 | } 53 | 54 | return result ?? ""; 55 | } 56 | 57 | public string GetMetadataValueEscaped(string metadataName) 58 | { 59 | throw new NotImplementedException(); 60 | } 61 | 62 | public void RemoveMetadata(string metadataName) 63 | { 64 | throw new NotImplementedException(); 65 | } 66 | 67 | public void SetMetadata(string metadataName, string metadataValue) 68 | { 69 | Metadata[metadataName] = metadataValue; 70 | } 71 | 72 | public void SetMetadataValueLiteral(string metadataName, string metadataValue) 73 | { 74 | SetMetadata(metadataName, metadataValue); 75 | } 76 | 77 | public override string ToString() => ItemSpec; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Tasks/CopyTask.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace Microsoft.Build.Logging.StructuredLogger 6 | { 7 | public class CopyTask : Task 8 | { 9 | //public override string TypeName => nameof(CopyTask); 10 | 11 | private IEnumerable fileCopyOperations; 12 | public IEnumerable FileCopyOperations => fileCopyOperations ??= GetFileCopyOperations(); 13 | 14 | protected virtual IEnumerable GetFileCopyOperations() 15 | { 16 | if (!HasChildren) 17 | { 18 | return Enumerable.Empty(); 19 | } 20 | 21 | List list = new List(); 22 | 23 | Match match; 24 | foreach (var message in this.GetMessages()) 25 | { 26 | var text = message.Text; 27 | 28 | match = Strings.CopyingFileFromRegex.Match(text); 29 | if (match.Success && match.Groups.Count > 2) 30 | { 31 | var operation = ParseCopyingFileFrom(match); 32 | operation.Node = message; 33 | list.Add(operation); 34 | continue; 35 | } 36 | 37 | match = Strings.DidNotCopyRegex.Match(text); 38 | if (match.Success && match.Groups.Count > 2) 39 | { 40 | var operation = ParseCopyingFileFrom(match, copied: false); 41 | operation.Node = message; 42 | list.Add(operation); 43 | continue; 44 | } 45 | 46 | match = Strings.CreatingHardLinkRegex.Match(text); 47 | if (match.Success && match.Groups.Count > 2) 48 | { 49 | var operation = ParseCopyingFileFrom(match); 50 | operation.Node = message; 51 | list.Add(operation); 52 | continue; 53 | } 54 | } 55 | 56 | return list; 57 | } 58 | 59 | protected static FileCopyOperation ParseCopyingFileFrom(Match match, bool copied = true) => new FileCopyOperation 60 | { 61 | Source = match.Groups["From"].Value, 62 | Destination = match.Groups["To"].Value, 63 | Copied = copied 64 | }; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Tasks/CscTask.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class CscTask : ManagedCompilerTask 4 | { 5 | //public override string TypeName => nameof(CscTask); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Tasks/FileCopyOperation.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class FileCopyOperation 4 | { 5 | public string Source { get; set; } 6 | public string Destination { get; set; } 7 | 8 | /// 9 | /// We need to represent both "Copied" and "Did not copy" cases 10 | /// 11 | public bool Copied { get; set; } 12 | 13 | /// 14 | /// Node in the tree where the copy operation happened 15 | /// (usually the Message under a Copy task) 16 | /// 17 | public TreeNode Node { get; set; } 18 | 19 | public override string ToString() 20 | { 21 | return $"{Source} ➔ {Destination}"; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Tasks/FscTask.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class FscTask : ManagedCompilerTask 4 | { 5 | //public override string TypeName => nameof(FscTask); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Tasks/ManagedCompilerTask.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class ManagedCompilerTask : Task 4 | { 5 | //public override string TypeName => nameof(ManagedCompilerTask); 6 | 7 | private CompilationWrites? compilationWrites; 8 | public CompilationWrites? CompilationWrites 9 | { 10 | get 11 | { 12 | if (!HasChildren) 13 | { 14 | return null; 15 | } 16 | 17 | if (!compilationWrites.HasValue) 18 | { 19 | compilationWrites = Logging.StructuredLogger.CompilationWrites.TryParse(this); 20 | if (compilationWrites == null) 21 | { 22 | return null; 23 | } 24 | } 25 | 26 | return compilationWrites.Value; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Tasks/ResolveAssemblyReferenceTask.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class ResolveAssemblyReferenceTask : Task 4 | { 5 | //public override string TypeName => nameof(ResolveAssemblyReferenceTask); 6 | 7 | public Folder Inputs { get; set; } 8 | public Folder Results { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Tasks/RobocopyTask.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace Microsoft.Build.Logging.StructuredLogger 6 | { 7 | public class RobocopyTask : CopyTask 8 | { 9 | //public override string TypeName => nameof(RobocopyTask); 10 | 11 | protected override IEnumerable GetFileCopyOperations() 12 | { 13 | if (!HasChildren) 14 | { 15 | return Enumerable.Empty(); 16 | } 17 | 18 | List list = new List(); 19 | 20 | Match match; 21 | foreach (var message in GetMessages()) 22 | { 23 | var text = message.Text; 24 | 25 | match = Strings.RobocopyFileCopiedRegex.Match(text); 26 | if (match.Success && match.Groups.Count > 2) 27 | { 28 | var operation = ParseCopyingFileFrom(match); 29 | operation.Node = message; 30 | list.Add(operation); 31 | continue; 32 | } 33 | 34 | match = Strings.RobocopyCreatedCopyOnWriteLinkRegex.Match(text); 35 | if (match.Success && match.Groups.Count > 2) 36 | { 37 | var operation = ParseCopyingFileFrom(match); 38 | operation.Node = message; 39 | list.Add(operation); 40 | continue; 41 | } 42 | 43 | match = Strings.RobocopyFileSkippedRegex.Match(text); 44 | if (match.Success && match.Groups.Count > 2) 45 | { 46 | var operation = ParseCopyingFileFrom(match, copied: false); 47 | operation.Node = message; 48 | list.Add(operation); 49 | continue; 50 | } 51 | 52 | match = Strings.RobocopyFileSkippedAsDuplicateRegex.Match(text); 53 | if (match.Success && match.Groups.Count > 2) 54 | { 55 | var operation = ParseCopyingFileFrom(match, copied: false); 56 | operation.Node = message; 57 | list.Add(operation); 58 | continue; 59 | } 60 | 61 | match = Strings.RobocopyFileFailedRegex.Match(text); 62 | if (match.Success && match.Groups.Count > 2) 63 | { 64 | var operation = ParseCopyingFileFrom(match, copied: false); 65 | operation.Node = message; 66 | list.Add(operation); 67 | continue; 68 | } 69 | } 70 | 71 | return list; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Tasks/VbcTask.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class VbcTask : ManagedCompilerTask 4 | { 5 | //public override string TypeName => nameof(VbcTask); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/TextNode.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class TextNode : TreeNode 4 | { 5 | public string Text { get; set; } 6 | public string ShortenedText => TextUtilities.ShortenValue(Text); 7 | public bool IsTextShortened => Text != null && Text.Length != TextUtilities.GetShortenLength(Text); 8 | 9 | public override string TypeName => nameof(TextNode); 10 | public override string Title => Text ?? TypeName; 11 | 12 | public override string ToString() 13 | { 14 | return Title; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/TimedNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Microsoft.Build.Logging.StructuredLogger 4 | { 5 | public class TimedNode : NamedNode 6 | { 7 | /// 8 | /// The Id of a Project, ProjectEvaluation, Target and Task. 9 | /// Corresponds to ProjectStartedEventsArgs.ProjectId, TargetStartedEventArgs.TargetId, etc. 10 | /// 11 | public int Id { get; set; } 12 | 13 | /// 14 | /// Corresponds to BuildEventArgs.BuildEventContext.NodeId, 15 | /// which is the id of the MSBuild.exe node process that built the current project or 16 | /// executed the given target or task. 17 | /// 18 | public int NodeId { get; set; } 19 | 20 | /// 21 | /// Unique index of the node in the build tree, can be used as a 22 | /// "URL" to node 23 | /// 24 | public int Index { get; set; } 25 | 26 | public DateTime StartTime { get; set; } 27 | public DateTime EndTime { get; set; } 28 | public TimeSpan Duration 29 | { 30 | get 31 | { 32 | if (EndTime >= StartTime) 33 | { 34 | return EndTime - StartTime; 35 | } 36 | 37 | return TimeSpan.Zero; 38 | } 39 | } 40 | 41 | public string DurationText => TextUtilities.DisplayDuration(Duration); 42 | 43 | public override string TypeName => nameof(TimedNode); 44 | 45 | public string GetTimeAndDurationText(bool fullPrecision = false) 46 | { 47 | var duration = DurationText; 48 | if (string.IsNullOrEmpty(duration)) 49 | { 50 | duration = "0"; 51 | } 52 | 53 | return $@"Start: {TextUtilities.Display(StartTime, displayDate: true, fullPrecision)} 54 | End: {TextUtilities.Display(EndTime, displayDate: true, fullPrecision)} 55 | Duration: {duration}"; 56 | } 57 | 58 | public override string ToolTip => GetTimeAndDurationText(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/StructuredLogger/ObjectModel/Warning.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class Warning : AbstractDiagnostic 4 | { 5 | public override string TypeName => nameof(Warning); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/StructuredLogger/PlatformUtilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Microsoft.Build.Logging.StructuredLogger; 5 | 6 | public class PlatformUtilities 7 | { 8 | public static bool HasThreads => !_isBrowser && !_isWasi; 9 | 10 | public static bool HasTempStorage => !_isWasi; 11 | public static bool HasColor => !_isWasi; 12 | 13 | private static readonly bool _isBrowser = RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")); 14 | private static readonly bool _isWasi = RuntimeInformation.IsOSPlatform(OSPlatform.Create("WASI")); 15 | } 16 | -------------------------------------------------------------------------------- /src/StructuredLogger/Progress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace Microsoft.Build.Logging.StructuredLogger 5 | { 6 | public class Progress : IProgress 7 | { 8 | public virtual CancellationToken CancellationToken { get; set; } = CancellationToken.None; 9 | 10 | public event Action Updated; 11 | 12 | public virtual void Report(double ratio) 13 | { 14 | Report(new ProgressUpdate { Ratio = ratio }); 15 | } 16 | 17 | public virtual void Report(ProgressUpdate progressUpdate) 18 | { 19 | Updated?.Invoke(progressUpdate); 20 | } 21 | } 22 | 23 | public struct ProgressUpdate 24 | { 25 | public double Ratio; 26 | public int BufferLength; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/StructuredLogger/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "StructuredLogger": { 4 | "commandName": "Executable", 5 | "executablePath": "$(MSBuildToolsPath)\\MSBuild.exe", 6 | "commandLineArgs": "$(SolutionDir)src\\StructuredLogger.Tests\\StructuredLogger.Tests.csproj /t:Rebuild /v:diag /noconlog /logger:StructuredLogger,$(TargetPath);test.buildlog", 7 | "workingDirectory": "$(SolutionDir)" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/StructuredLogger/ReaderSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | /// 4 | /// Settings for the . 5 | /// 6 | public class ReaderSettings 7 | { 8 | public static ReaderSettings Default { get; } = 9 | new() { UnknownDataBehavior = UnknownDataBehavior.Warning }; 10 | 11 | /// 12 | /// Indication of how the unknown data in future versions of binlogs should be handled. 13 | /// 14 | public UnknownDataBehavior UnknownDataBehavior { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/StructuredLogger/Search/HighlightedText.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public class HighlightedText 4 | { 5 | public string Text { get; set; } 6 | public string Style { get; set; } 7 | 8 | public override string ToString() => Text; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/StructuredLogger/Search/ISearchExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using StructuredLogViewer; 3 | 4 | namespace Microsoft.Build.Logging.StructuredLogger 5 | { 6 | public interface ISearchExtension 7 | { 8 | bool AugmentOtherResults { get; } 9 | bool TryGetResults(NodeQueryMatcher nodeQueryMatcher, IList resultCollector, int maxResults); 10 | } 11 | } -------------------------------------------------------------------------------- /src/StructuredLogger/Search/SearchResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.Build.Logging.StructuredLogger; 4 | 5 | namespace StructuredLogViewer 6 | { 7 | public class SearchResult 8 | { 9 | public BaseNode Node { get; } 10 | public List<(string field, string match)> WordsInFields = new List<(string, string)>(); 11 | 12 | public IList FieldsToDisplay { get; set; } 13 | 14 | public bool MatchedByType { get; private set; } 15 | public TimeSpan Duration { get; set; } 16 | public DateTime StartTime { get; set; } 17 | public DateTime EndTime { get; set; } 18 | 19 | public string RootFolder { get; set; } 20 | 21 | public FileCopyInfo AssociatedFileCopy { get; set; } 22 | 23 | public static SearchResult EmptyQueryMatch { get; } = new SearchResult(); 24 | 25 | public SearchResult() 26 | { 27 | } 28 | 29 | public SearchResult(BaseNode node, bool includeDuration = false, bool includeStart = false, bool includeEnd = false) 30 | { 31 | Node = node; 32 | 33 | if (node is TimedNode timedNode) 34 | { 35 | if (includeDuration) 36 | { 37 | Duration = timedNode.Duration; 38 | } 39 | 40 | if (includeStart) 41 | { 42 | StartTime = timedNode.StartTime; 43 | } 44 | 45 | if (includeEnd) 46 | { 47 | EndTime = timedNode.EndTime; 48 | } 49 | } 50 | } 51 | 52 | public void AddMatch(string field, string word, bool addAtBeginning = false) 53 | { 54 | if (addAtBeginning) 55 | { 56 | WordsInFields.Insert(0, (field, word)); 57 | } 58 | else 59 | { 60 | WordsInFields.Add((field, word)); 61 | } 62 | } 63 | 64 | public void AddMatchByNodeType() 65 | { 66 | MatchedByType = true; 67 | } 68 | 69 | public override string ToString() 70 | { 71 | return Node?.ToString(); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/StructuredLogger/Serialization/AttributeNames.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | public enum AttributeNames 4 | { 5 | Name, 6 | IsLowRelevance, 7 | Text, 8 | ProjectFile, 9 | DependsOnTargets, 10 | FromAssembly, 11 | CommandLineArguments, 12 | Code, 13 | File, 14 | LineNumber, 15 | ColumnNumber, 16 | EndLineNumber, 17 | EndColumnNumber, 18 | StartTime, 19 | EndTime, 20 | Succeeded, 21 | Timestamp, 22 | IsAnalyzed, 23 | NodeId 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/StructuredLogger/Serialization/Binary/BetterBinaryReader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Microsoft.Build.Logging.StructuredLogger 4 | { 5 | public class BetterBinaryReader : BinaryReader 6 | { 7 | public BetterBinaryReader(Stream input) : base(input) 8 | { 9 | } 10 | 11 | public override int ReadInt32() 12 | { 13 | return Read7BitEncodedInt(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/StructuredLogger/Serialization/Binary/BetterBinaryWriter.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Microsoft.Build.Logging.StructuredLogger 4 | { 5 | public class BetterBinaryWriter : BinaryWriter 6 | { 7 | public BetterBinaryWriter(Stream output) : base(output) 8 | { 9 | } 10 | 11 | public override void Write(int value) 12 | { 13 | Write7BitEncodedInt(value); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/StructuredLogger/Serialization/StringWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Microsoft.Build.Logging.StructuredLogger 4 | { 5 | public class StringWriter 6 | { 7 | public static int MaxStringLength = 100_000_000; 8 | 9 | public static string GetString(BaseNode rootNode) 10 | { 11 | var sb = new StringBuilder(); 12 | 13 | WriteNode(rootNode, sb, 0); 14 | 15 | return sb.ToString(); 16 | } 17 | 18 | private static void WriteNode(BaseNode node, StringBuilder sb, int indent = 0) 19 | { 20 | if (node == null) 21 | { 22 | return; 23 | } 24 | 25 | if (sb.Length > MaxStringLength) 26 | { 27 | return; 28 | } 29 | 30 | Indent(sb, indent); 31 | 32 | var text = node.GetFullText(); 33 | 34 | sb.AppendLine(text); 35 | 36 | var treeNode = node as TreeNode; 37 | if (treeNode != null && treeNode.HasChildren) 38 | { 39 | foreach (var child in treeNode.Children) 40 | { 41 | WriteNode(child, sb, indent + 1); 42 | } 43 | } 44 | } 45 | 46 | private static void Indent(StringBuilder sb, int indent) 47 | { 48 | for (int i = 0; i < indent * 4; i++) 49 | { 50 | sb.Append(' '); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/StructuredLogger/Span.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Microsoft.Build.Logging.StructuredLogger 4 | { 5 | public struct Span 6 | { 7 | public int Start; 8 | public int Length; 9 | public int End => Start + Length; 10 | 11 | public static readonly Span Empty = new Span(); 12 | 13 | public Span(int start, int length) : this() 14 | { 15 | Start = start; 16 | Length = length; 17 | } 18 | 19 | public override string ToString() 20 | { 21 | return $"({Start}, {Length})"; 22 | } 23 | 24 | public Span Skip(int length) 25 | { 26 | if (length > Length) 27 | { 28 | return new Span(); 29 | } 30 | 31 | return new Span(Start + length, Length - length); 32 | } 33 | 34 | public bool ContainsEndInclusive(int position) 35 | { 36 | return position >= Start && position <= End; 37 | } 38 | 39 | public bool Contains(int position) 40 | { 41 | return position >= Start && position < End; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/StructuredLogger/Strings/ResourcesCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using ResourcesDictionary = System.Collections.Generic.Dictionary>; 4 | #if NET8_0_OR_GREATER 5 | using System.Text.Json; 6 | using System.Text.Json.Serialization; 7 | #endif 8 | 9 | namespace Microsoft.Build.Logging.StructuredLogger 10 | { 11 | public partial class StringsSet 12 | { 13 | private Dictionary currentSet; 14 | public string Culture { get; set; } 15 | 16 | public StringsSet(string culture) 17 | { 18 | Culture = culture; 19 | currentSet = ResourcesCollection[culture]; 20 | } 21 | 22 | private static readonly object lockObject = new object(); 23 | 24 | private static ResourcesDictionary resourcesCollection; 25 | public static ResourcesDictionary ResourcesCollection 26 | { 27 | get 28 | { 29 | if (resourcesCollection == null) 30 | { 31 | lock (lockObject) 32 | { 33 | if (resourcesCollection == null) 34 | { 35 | var assembly = typeof(StructuredLogger).Assembly; 36 | using var stream = assembly.GetManifestResourceStream(@"Strings.json"); 37 | #if NET8_0_OR_GREATER 38 | resourcesCollection = JsonSerializer.Deserialize(stream, JsonSourceGenerationContext.Default.ResourcesDictionary); 39 | #else 40 | using var reader = new StreamReader(stream); 41 | var text = reader.ReadToEnd(); 42 | resourcesCollection = TinyJson.JSONParser.FromJson(text); 43 | #endif 44 | } 45 | } 46 | } 47 | 48 | return resourcesCollection; 49 | } 50 | } 51 | 52 | public string GetString(string key) 53 | { 54 | if (currentSet.TryGetValue(key, out var value)) 55 | { 56 | return value; 57 | } 58 | 59 | return string.Empty; 60 | } 61 | 62 | #if NET8_0_OR_GREATER 63 | [JsonSerializable(typeof(ResourcesDictionary), TypeInfoPropertyName = "ResourcesDictionary")] 64 | partial class JsonSourceGenerationContext : JsonSerializerContext 65 | { 66 | } 67 | #endif 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/StructuredLogger/Strings/readme.txt: -------------------------------------------------------------------------------- 1 | Strings.json is a generated file containing all localized resource strings from MSBuild that we want to use. 2 | 3 | To regenerate Strings.json, add the resource string ids you need to ResourcesGenerator project -> ResourceCreator.ResourceNames, 4 | then run the ResourcesGenerator tool. -------------------------------------------------------------------------------- /src/StructuredLogger/UnknownDataBehavior.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Build.Logging.StructuredLogger 2 | { 3 | /// 4 | /// Indication of how the unknown data in future versions of binlogs should be handled. 5 | /// 6 | public enum UnknownDataBehavior 7 | { 8 | /// 9 | /// When unknown data encountered - emit a single synthetic error message for the entire build. 10 | /// 11 | Error, 12 | 13 | /// 14 | /// When unknown data encountered - emit a single synthetic warning message for the entire build. 15 | /// 16 | Warning, 17 | 18 | /// 19 | /// When unknown data encountered - emit a single synthetic message for the entire build. 20 | /// 21 | Message, 22 | 23 | /// 24 | /// Ignore the unknown data and continue reading the rest of the build. 25 | /// 26 | Ignore, 27 | 28 | /// 29 | /// Throw an exception when unknown data is encountered. 30 | /// 31 | ThrowException 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/StructuredLogger/key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KirillOsenkov/MSBuildStructuredLog/ab49d1866d242a750de3ce1250c62f3c57be858e/src/StructuredLogger/key.snk -------------------------------------------------------------------------------- /src/TaskRunner/AppDomainInitializer.cs: -------------------------------------------------------------------------------- 1 | namespace TaskRunner 2 | { 3 | public class AppDomainInitializer 4 | { 5 | /// 6 | /// This is called everytime a new appdomain is created in the current process. 7 | /// 8 | public AppDomainInitializer() 9 | { 10 | Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/TaskRunner/MissingPropertyException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TaskRunner 4 | { 5 | public class MissingPropertyException : MissingMemberException 6 | { 7 | public MissingPropertyException(string className, string propertyName) : base(className, propertyName) 8 | { 9 | } 10 | 11 | public string MSBuildVersion { get; set; } 12 | 13 | public override string Message 14 | => $"Property {MemberName} was not found on type {ClassName}. " + 15 | $"This probably means that the task being run was recorded with a newer version of MSBuild ({MSBuildVersion ?? "N/A"}) " + 16 | $"than the version used by MSBuild Structured Log Viewer ({Microsoft.Build.Evaluation.ProjectCollection.Version})."; 17 | } 18 | } -------------------------------------------------------------------------------- /src/TaskRunner/TaskRunner.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net472;net8.0 6 | app.manifest 7 | 8 | 9 | 10 | true 11 | 12 | 13 | 14 | false 15 | LatestMajor 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/TaskRunner/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | amd64 arm64 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Tests/1.proj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Prop1Value 7 | Prop2Value 8 | Prop1ValueReassigned 9 | 10 | 11 | 12 | 13 | Metadata1Value 14 | 15 | 16 | 17 | 18 | 19 | Metadata2Value 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | PropInTargetValue 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Metadata3Value 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/Tests/2.proj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "version": "2.2", 4 | "publicReleaseRefSpec": [ 5 | "^refs/heads/main$" 6 | ], 7 | "cloudBuild": { 8 | "buildNumber": { 9 | "enabled": true 10 | } 11 | } 12 | } --------------------------------------------------------------------------------