From 340ad5011fcf0a6ef5529b2c95725b3e2203de8e Mon Sep 17 00:00:00 2001 From: William Li Date: Thu, 9 May 2019 15:37:15 -0700 Subject: [PATCH] Collect crash exception --- src/Tasks/Common/TaskBase.cs | 67 +++++++++++++++++++ ...enThatWeWantToCollectExceptionTelemetry.cs | 38 +++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCollectExceptionTelemetry.cs diff --git a/src/Tasks/Common/TaskBase.cs b/src/Tasks/Common/TaskBase.cs index 88ae5d180428..8a6f37e2455f 100644 --- a/src/Tasks/Common/TaskBase.cs +++ b/src/Tasks/Common/TaskBase.cs @@ -3,6 +3,9 @@ using System; using Microsoft.Build.Utilities; +using Microsoft.Build.Framework; +using System.Collections.Generic; +using System.Globalization; namespace Microsoft.NET.Build.Tasks { @@ -38,10 +41,74 @@ public override bool Execute() { Log.LogError(e.Message); } + catch (Exception e) + { + LogErrorTelemetry("taskBaseCatchException", e); + throw; + } return !Log.HasLoggedErrors; } + private void LogErrorTelemetry(string eventName, Exception e) + { + (BuildEngine as IBuildEngine5)?.LogTelemetry(eventName, new Dictionary { + {"exceptionType", e.GetType().ToString() }, + {"detail", ExceptionToStringWithoutMessage(e) }}); + } + + private static string ExceptionToStringWithoutMessage(Exception e) + { + const string AggregateException_ToString = "{0}{1}---> (Inner Exception #{2}) {3}{4}{5}"; + if (e is AggregateException aggregate) + { + string text = NonAggregateExceptionToStringWithoutMessage(aggregate); + + for (int i = 0; i < aggregate.InnerExceptions.Count; i++) + { + text = string.Format(CultureInfo.InvariantCulture, + AggregateException_ToString, + text, + Environment.NewLine, + i, + ExceptionToStringWithoutMessage(aggregate.InnerExceptions[i]), + "<---", + Environment.NewLine); + } + + return text; + } + else + { + return NonAggregateExceptionToStringWithoutMessage(e); + } + } + + private static string NonAggregateExceptionToStringWithoutMessage(Exception e) + { + string s; + const string Exception_EndOfInnerExceptionStack = "--- End of inner exception stack trace ---"; + + + s = e.GetType().ToString(); + + if (e.InnerException != null) + { + s = s + " ---> " + ExceptionToStringWithoutMessage(e.InnerException) + Environment.NewLine + + " " + Exception_EndOfInnerExceptionStack; + + } + + var stackTrace = e.StackTrace; + + if (stackTrace != null) + { + s += Environment.NewLine + stackTrace; + } + + return s; + } + protected abstract void ExecuteCore(); } } diff --git a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCollectExceptionTelemetry.cs b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCollectExceptionTelemetry.cs new file mode 100644 index 000000000000..eca693403bb5 --- /dev/null +++ b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCollectExceptionTelemetry.cs @@ -0,0 +1,38 @@ +using FluentAssertions; +using Microsoft.NET.TestFramework; +using Microsoft.NET.TestFramework.Assertions; +using Microsoft.NET.TestFramework.Commands; +using System; +using System.IO; +using Xunit.Abstractions; +using System.Reflection; + +namespace Microsoft.NET.Build.Tests +{ + public class GivenThatWeWantToCollectExceptionTelemetry : SdkTest + { + public GivenThatWeWantToCollectExceptionTelemetry(ITestOutputHelper log) : base(log) + { + } + + [CoreMSBuildAndWindowsOnlyFact] + public void It_collects_Exception() + { + Type loggerType = typeof(LogTelemetryToStdOutForTest); + string telemetryTestLogger = $"/Logger:{loggerType.FullName},{loggerType.GetTypeInfo().Assembly.Location}"; + + var testAsset = _testAssetsManager.CopyTestAsset("HelloWorld").WithSource().Restore(Log); + + var mSBuildCommand = new MSBuildCommand(Log, "GenerateToolsSettingsFileFromBuildProperty", Path.Combine(testAsset.TestRoot)); + + string invalidPath = @"\\.\COM56"; + string causeTaskToFail = $"/p:_ToolsSettingsFilePath={invalidPath}"; + + mSBuildCommand + .Execute(telemetryTestLogger, causeTaskToFail) + .StdOut.Should() + .Contain("\"EventName\":\"taskBaseCatchException\",\"Properties\":{\"exceptionType\":\"System.IO.FileNotFoundException\"") + .And.Contain("detail"); + } + } +}