diff --git a/.gitignore b/.gitignore
index 91f96a8e1e..6919f1ca9c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@
*.user
*.sln.docstates
.vs
+*.sln.dotsettings
# Build results
[Dd]ebug/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b4e3ed027f..7f92000cce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,13 @@
This changelog will be used to generate documentation on [release notes page](http://azure.microsoft.com/en-us/documentation/articles/app-insights-release-notes-dotnet/).
+
+## Version 2.2.0-beta1
+
+- Add ExceptionTelemetry.Message property. If it is provided it is used instead of Exception.Message property for the ounter-most exception.
+- Telemetry types can be exluded from sampling by specifing ExcludedTypes property.
+- ServerTelemetryChannel: changed backoff logic to be less aggressive, added diagnostics event when backoff logic kicks in and added more tracing. (Done to address issues when data stopps flowing till application gets restarted)
+
## Version 2.1.0-beta4
- [Bug fix](https://github.com/Microsoft/ApplicationInsights-dotnet-server/issues/76)
diff --git a/GlobalStaticVersion.props b/GlobalStaticVersion.props
index 7012ed18d4..35a83bfc6b 100644
--- a/GlobalStaticVersion.props
+++ b/GlobalStaticVersion.props
@@ -6,14 +6,14 @@
Update for every public release.
-->
2
- 1
+ 2
0
-
+ beta1
- 2016-03-08
+ 2016-06-02
.PreReleaseVersion
$(MSBuildThisFileDirectory)$(PreReleaseVersionFileName)
diff --git a/README.md b/README.md
index 07d7fb72e4..cb57685a32 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,8 @@ Read about [how to use the API and see the results in the portal][api-overview].
We strongly welcome and encourage contributions to this project. Please read the [contributor's guide][ContribGuide] located in the ApplicationInsights-Home repository. If making a large change we request that you open an [issue][GitHubIssue] first. We follow the [Git Flow][GitFlow] approach to branching.
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
+
[AILandingPage]: http://azure.microsoft.com/services/application-insights/
[api-overview]: https://azure.microsoft.com/documentation/articles/app-insights-api-custom-events-metrics/
[ContribGuide]: https://github.com/Microsoft/ApplicationInsights-Home/blob/master/CONTRIBUTING.md
diff --git a/Test/CoreSDK.Test/Net40/Core.Net40.Tests.csproj b/Test/CoreSDK.Test/Net40/Core.Net40.Tests.csproj
index f07f1d6971..8c23bc8f1e 100644
--- a/Test/CoreSDK.Test/Net40/Core.Net40.Tests.csproj
+++ b/Test/CoreSDK.Test/Net40/Core.Net40.Tests.csproj
@@ -63,6 +63,16 @@
+
+
+ ..\..\..\..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll
+ True
+
+
+ ..\..\..\..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll
+ True
+
+
diff --git a/Test/CoreSDK.Test/Net40/packages.config b/Test/CoreSDK.Test/Net40/packages.config
index 39bccfd597..338f4b0737 100644
--- a/Test/CoreSDK.Test/Net40/packages.config
+++ b/Test/CoreSDK.Test/Net40/packages.config
@@ -6,6 +6,7 @@
+
diff --git a/Test/CoreSDK.Test/Shared/Channel/TransmissionTest.cs b/Test/CoreSDK.Test/Shared/Channel/TransmissionTest.cs
index a2ef471e7c..95be2d3789 100644
--- a/Test/CoreSDK.Test/Shared/Channel/TransmissionTest.cs
+++ b/Test/CoreSDK.Test/Shared/Channel/TransmissionTest.cs
@@ -3,6 +3,7 @@
using System;
using System.IO;
using System.Net;
+ using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -158,13 +159,9 @@ public void ThrowsInvalidOperationExceptionWhenTransmissionIsAlreadySending()
{
AsyncTest.Run(async () =>
{
- var request = new StubWebRequest();
- request.OnBeginGetRequestStream = (callback, state) => TaskEx.Delay(TimeSpan.FromMilliseconds(10)).AsAsyncResult(callback, request);
-
var transmission = new TestableTransmission();
- transmission.OnCreateRequest = uri => request;
-
- Task dontWait = transmission.SendAsync();
+ FieldInfo isSendingField = typeof(Transmission).GetField("isSending", BindingFlags.NonPublic | BindingFlags.Instance);
+ isSendingField.SetValue(transmission, 1, BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Instance, null, null);
await AssertEx.ThrowsAsync(() => transmission.SendAsync());
});
}
diff --git a/Test/CoreSDK.Test/Shared/DataContracts/ExceptionTelemetryTest.cs b/Test/CoreSDK.Test/Shared/DataContracts/ExceptionTelemetryTest.cs
index aeacd6da34..375b177bf6 100644
--- a/Test/CoreSDK.Test/Shared/DataContracts/ExceptionTelemetryTest.cs
+++ b/Test/CoreSDK.Test/Shared/DataContracts/ExceptionTelemetryTest.cs
@@ -110,6 +110,56 @@ public void SerializeWritesItemVersionAsExpectedByEndpoint()
Assert.Equal(2, item.Data.BaseData.Ver);
}
+ [TestMethod]
+ public void SerializeUsesExceptionMessageIfTelemetryMessageNotProvided()
+ {
+ ExceptionTelemetry original = CreateExceptionTelemetry(new ArgumentException("Test"));
+ var item = TelemetryItemTestHelper.SerializeDeserializeTelemetryItem(original);
+
+ Assert.Equal("Test", item.Data.BaseData.Exceptions[0].Message);
+ }
+
+ [TestMethod]
+ public void SerializeTelemetryMessageAsOuterExceptionMessage()
+ {
+ ExceptionTelemetry original = CreateExceptionTelemetry(new ArgumentException("Test"));
+ original.Message = "Custom";
+ var item = TelemetryItemTestHelper.SerializeDeserializeTelemetryItem(original);
+
+ Assert.Equal("Custom", item.Data.BaseData.Exceptions[0].Message);
+ }
+
+ [TestMethod]
+ public void SerializeUsesExceptionMessageForInnerExceptions()
+ {
+ Exception outerException = new ArgumentException("Outer", new Exception("Inner"));
+ ExceptionTelemetry original = CreateExceptionTelemetry(outerException);
+
+ original.Message = "Custom";
+ var item = TelemetryItemTestHelper.SerializeDeserializeTelemetryItem(original);
+
+ Assert.Equal("Custom", item.Data.BaseData.Exceptions[0].Message);
+ Assert.Equal("Inner", item.Data.BaseData.Exceptions[1].Message);
+ }
+
+ [TestMethod]
+ public void SerializeUsesExceptionMessageForInnerAggregateExceptions()
+ {
+ Exception innerException1 = new ArgumentException("Inner1");
+ Exception innerException2 = new ArgumentException("Inner2");
+
+ AggregateException aggregateException = new AggregateException("AggregateException", new [] {innerException1, innerException2});
+
+ ExceptionTelemetry original = CreateExceptionTelemetry(aggregateException);
+
+ original.Message = "Custom";
+ var item = TelemetryItemTestHelper.SerializeDeserializeTelemetryItem(original);
+
+ Assert.Equal("Custom", item.Data.BaseData.Exceptions[0].Message);
+ Assert.Equal("Inner1", item.Data.BaseData.Exceptions[1].Message);
+ Assert.Equal("Inner2", item.Data.BaseData.Exceptions[2].Message);
+ }
+
[TestMethod]
public void SerializeWritesItemHandledAtAsExpectedByEndpoint()
{
diff --git a/Test/CoreSDK.Test/Shared/TelemetryClientTest.cs b/Test/CoreSDK.Test/Shared/TelemetryClientTest.cs
index fb3388b6fc..f405fdb01b 100644
--- a/Test/CoreSDK.Test/Shared/TelemetryClientTest.cs
+++ b/Test/CoreSDK.Test/Shared/TelemetryClientTest.cs
@@ -3,14 +3,19 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+ using System.Diagnostics;
#if CORE_PCL || NET45 || NET46
using System.Diagnostics.Tracing;
#endif
using System.Linq;
+ using System.Net;
+ using System.Net.Http;
using System.Reflection;
+ using System.Text;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
+ using Microsoft.ApplicationInsights.Extensibility.Implementation;
using Microsoft.ApplicationInsights.Extensibility.Implementation.Platform;
using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing;
using Microsoft.ApplicationInsights.TestFramework;
@@ -733,6 +738,13 @@ public void TrackWhenChannelIsNullWillThrowInvalidOperationException()
[TestMethod]
public void TrackAddsSdkVerionByDefault()
{
+ // split version by 4 numbers manually so we do not do the same as in the product code and actually test it
+ string versonStr = Assembly.GetAssembly(typeof(TelemetryConfiguration)).GetCustomAttributes(false)
+ .OfType()
+ .First()
+ .Version;
+ string[] versionParts = new Version(versonStr).ToString().Split('.');
+
var configuration = new TelemetryConfiguration { TelemetryChannel = new StubTelemetryChannel(), InstrumentationKey = Guid.NewGuid().ToString() };
var client = new TelemetryClient(configuration);
@@ -740,7 +752,7 @@ public void TrackAddsSdkVerionByDefault()
EventTelemetry eventTelemetry = new EventTelemetry("test");
client.Track(eventTelemetry);
- Assert.StartsWith("dotnet: ", eventTelemetry.Context.Internal.SdkVersion);
+ Assert.Equal("dotnet:"+ string.Join(".", versionParts[0], versionParts[1], versionParts[2]) + "-" + versionParts[3], eventTelemetry.Context.Internal.SdkVersion);
}
[TestMethod]
@@ -779,7 +791,70 @@ public void AllTelemetryIsSentWithDefaultSamplingRate()
Assert.Equal(ItemsToGenerate, sentTelemetry.Count);
}
-
+
+ #endregion
+
+ #region ValidateEndpoint
+
+ [TestMethod]
+ public void SendEventToValidateEndpoint()
+ {
+ EventTelemetry telemetry1 = new EventTelemetry();
+ MetricTelemetry telemetry2 = new MetricTelemetry();
+ DependencyTelemetry telemetry3 = new DependencyTelemetry();
+ ExceptionTelemetry telemetry4 = new ExceptionTelemetry();
+ MetricTelemetry telemetry5 = new MetricTelemetry();
+ PageViewTelemetry telemetry6 = new PageViewTelemetry();
+ PerformanceCounterTelemetry telemetry7 = new PerformanceCounterTelemetry();
+ RequestTelemetry telemetry8 = new RequestTelemetry();
+ SessionStateTelemetry telemetry9 = new SessionStateTelemetry();
+ TraceTelemetry telemetry10 = new TraceTelemetry();
+
+ var telemetryItems = new List
+ {
+ telemetry1,
+ telemetry2,
+ telemetry3,
+ telemetry4,
+ telemetry5,
+ telemetry6,
+ telemetry7,
+ telemetry8,
+ telemetry9,
+ telemetry10
+ };
+
+ // ChuckNorrisTeamUnitTests resource in Prototypes5
+ var config = new TelemetryConfiguration { InstrumentationKey = "fafa4b10-03d3-4bb0-98f4-364f0bdf5df8" };
+ var telemetryClient = new TelemetryClient(config);
+
+ telemetryClient.Initialize(telemetry1);
+ telemetryClient.Initialize(telemetry2);
+ telemetryClient.Initialize(telemetry3);
+ telemetryClient.Initialize(telemetry4);
+ telemetryClient.Initialize(telemetry5);
+ telemetryClient.Initialize(telemetry6);
+ telemetryClient.Initialize(telemetry7);
+ telemetryClient.Initialize(telemetry8);
+ telemetryClient.Initialize(telemetry9);
+ telemetryClient.Initialize(telemetry10);
+
+ string json = JsonSerializer.SerializeAsString(telemetryItems);
+
+ HttpClient client = new HttpClient();
+ var result = client.PostAsync(
+ "https://dc.services.visualstudio.com/v2/validate",
+ new ByteArrayContent(Encoding.UTF8.GetBytes(json))).GetAwaiter().GetResult();
+
+ if (result.StatusCode != HttpStatusCode.OK)
+ {
+ var response = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
+ Trace.WriteLine(response);
+ }
+
+ Assert.Equal(HttpStatusCode.OK, result.StatusCode);
+ }
+
#endregion
private TelemetryClient InitializeTelemetryClient(ICollection sentTelemetry)
diff --git a/Test/CoreSDK.Test/TestFramework/Shared/TestEventListener.cs b/Test/CoreSDK.Test/TestFramework/Shared/TestEventListener.cs
index 5b3b5b3ca5..7bf36b14d3 100644
--- a/Test/CoreSDK.Test/TestFramework/Shared/TestEventListener.cs
+++ b/Test/CoreSDK.Test/TestFramework/Shared/TestEventListener.cs
@@ -1,6 +1,7 @@
namespace Microsoft.ApplicationInsights.TestFramework
{
using System;
+ using System.Collections.Concurrent;
using System.Collections.Generic;
#if CORE_PCL || NET45 || NET46
using System.Diagnostics.Tracing;
@@ -12,12 +13,12 @@
internal class TestEventListener : EventListener
{
- private readonly Queue events;
+ private readonly ConcurrentQueue events;
private readonly AutoResetEvent eventWritten;
public TestEventListener()
{
- this.events = new Queue();
+ this.events = new ConcurrentQueue();
this.eventWritten = new AutoResetEvent(false);
this.OnOnEventWritten = e =>
{
@@ -40,8 +41,16 @@ public IEnumerable Messages
}
while (this.events.Count != 0)
- {
- yield return this.events.Dequeue();
+ {
+ EventWrittenEventArgs nextEvent;
+ if (this.events.TryDequeue(out nextEvent))
+ {
+ yield return nextEvent;
+ }
+ else
+ {
+ throw new InvalidOperationException();
+ }
}
}
}
diff --git a/Test/ServerTelemetryChannel.Test/Shared.Tests/AdaptiveSamplingTelemetryProcessorTest.cs b/Test/ServerTelemetryChannel.Test/Shared.Tests/AdaptiveSamplingTelemetryProcessorTest.cs
index 083309a67c..28fce4f990 100644
--- a/Test/ServerTelemetryChannel.Test/Shared.Tests/AdaptiveSamplingTelemetryProcessorTest.cs
+++ b/Test/ServerTelemetryChannel.Test/Shared.Tests/AdaptiveSamplingTelemetryProcessorTest.cs
@@ -3,7 +3,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+ using System.Reflection;
using System.Threading;
+
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
@@ -194,6 +196,20 @@ public void SamplingPercentageAdjustsForSpikyProductionRate()
Assert.True(sentTelemetry.Count < targetItemCount + tolerance);
}
+ [TestMethod]
+ public void AdaptiveSamplingSetsExcludedTypesOnInternalSamplingProcessor()
+ {
+ var tc = new TelemetryConfiguration { TelemetryChannel = new StubTelemetryChannel() };
+ var channelBuilder = new TelemetryProcessorChainBuilder(tc);
+ channelBuilder.UseAdaptiveSampling(5, "request;");
+ channelBuilder.Build();
+
+ var fieldInfo = typeof(AdaptiveSamplingTelemetryProcessor).GetField("samplingProcessor", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic);
+ SamplingTelemetryProcessor internalProcessor = (SamplingTelemetryProcessor) fieldInfo.GetValue(tc.TelemetryProcessorChain.FirstTelemetryProcessor);
+
+ Assert.Equal("request;", internalProcessor.ExcludedTypes);
+ }
+
private void TraceSamplingPercentageEvaluation(
double afterSamplingTelemetryItemRatePerSecond,
double currentSamplingPercentage,
diff --git a/Test/ServerTelemetryChannel.Test/Shared.Tests/Helpers/BackendResponseHelper.cs b/Test/ServerTelemetryChannel.Test/Shared.Tests/Helpers/BackendResponseHelper.cs
new file mode 100644
index 0000000000..0ce886bee1
--- /dev/null
+++ b/Test/ServerTelemetryChannel.Test/Shared.Tests/Helpers/BackendResponseHelper.cs
@@ -0,0 +1,36 @@
+namespace Microsoft.ApplicationInsights.WindowsServer.Channel.Helpers
+{
+ using System.Globalization;
+
+ public static class BackendResponseHelper
+ {
+ public static string CreateBackendResponse(int itemsReceived, int itemsAccepted, string[] errorCodes,
+ int indexStartWith = 0)
+ {
+ string singleItem = "{{" +
+ "\"index\": {0}," +
+ "\"statusCode\": {1}," +
+ "\"message\": \"Explanation\"" +
+ "}}";
+
+ string errorList = string.Empty;
+ for (int i = 0; i < errorCodes.Length; ++i)
+ {
+ string errorCode = errorCodes[i];
+ if (!string.IsNullOrEmpty(errorList))
+ {
+ errorList += ",";
+ }
+
+ errorList += string.Format(CultureInfo.InvariantCulture, singleItem, indexStartWith + i, errorCode);
+ }
+
+ return
+ "{" +
+ "\"itemsReceived\": " + itemsReceived + "," +
+ "\"itemsAccepted\": " + itemsAccepted + "," +
+ "\"errors\": [" + errorList + "]" +
+ "}";
+ }
+ }
+}
diff --git a/Test/ServerTelemetryChannel.Test/Shared.Tests/Helpers/StubTransmitter.cs b/Test/ServerTelemetryChannel.Test/Shared.Tests/Helpers/StubTransmitter.cs
index bb1c8fe2e6..b4b18cc9b3 100644
--- a/Test/ServerTelemetryChannel.Test/Shared.Tests/Helpers/StubTransmitter.cs
+++ b/Test/ServerTelemetryChannel.Test/Shared.Tests/Helpers/StubTransmitter.cs
@@ -3,10 +3,8 @@
using System;
using System.Linq;
using Microsoft.ApplicationInsights.Channel;
+ using Microsoft.ApplicationInsights.Channel.Implementation;
using Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.Implementation;
-#if NET45
- using TaskEx = System.Threading.Tasks.Task;
-#endif
internal class StubTransmitter : Transmitter
{
@@ -14,10 +12,19 @@ internal class StubTransmitter : Transmitter
public Action OnEnqueue = transmission => { };
public Action OnInitialize = () => { };
+ public Func OnGetBackOffTime = timeInMs => TimeSpan.FromMilliseconds(timeInMs);
+
public StubTransmitter()
- : base(new StubTransmissionSender(), new StubTransmissionBuffer(), new StubTransmissionStorage(), Enumerable.Empty())
+ : base(new StubTransmissionSender(), new StubTransmissionBuffer(), new StubTransmissionStorage(), Enumerable.Empty(), new BackoffLogicManager(TimeSpan.FromMinutes(30)))
{
+
+ }
+
+ public StubTransmitter(BackoffLogicManager backoffLogicManager)
+ : base(new StubTransmissionSender(), new StubTransmissionBuffer(), new StubTransmissionStorage(), Enumerable.Empty(), backoffLogicManager)
+ {
+
}
internal override void Initialize()
diff --git a/Test/ServerTelemetryChannel.Test/Shared.Tests/Helpers/TestableBackoffLogicManager.cs b/Test/ServerTelemetryChannel.Test/Shared.Tests/Helpers/TestableBackoffLogicManager.cs
new file mode 100644
index 0000000000..d9f1ecc746
--- /dev/null
+++ b/Test/ServerTelemetryChannel.Test/Shared.Tests/Helpers/TestableBackoffLogicManager.cs
@@ -0,0 +1,20 @@
+namespace Microsoft.ApplicationInsights.WindowsServer.Channel.Helpers
+{
+ using System;
+ using Microsoft.ApplicationInsights.Channel.Implementation;
+
+ internal class TestableBackoffLogicManager : BackoffLogicManager
+ {
+ private readonly TimeSpan backoffInterval;
+
+ public TestableBackoffLogicManager(TimeSpan backoffInterval, int defaultBackoffEnabledIntervalInMin = 30) : base(TimeSpan.FromMinutes(defaultBackoffEnabledIntervalInMin))
+ {
+ this.backoffInterval = backoffInterval;
+ }
+
+ protected override TimeSpan GetBackOffTime(string headerValue)
+ {
+ return this.backoffInterval;
+ }
+ }
+}
diff --git a/Test/ServerTelemetryChannel.Test/Shared.Tests/Implementation/BackoffLogicManagerTest.cs b/Test/ServerTelemetryChannel.Test/Shared.Tests/Implementation/BackoffLogicManagerTest.cs
new file mode 100644
index 0000000000..578d5ada81
--- /dev/null
+++ b/Test/ServerTelemetryChannel.Test/Shared.Tests/Implementation/BackoffLogicManagerTest.cs
@@ -0,0 +1,314 @@
+namespace Microsoft.ApplicationInsights.WindowsServer.Channel.Implementation
+{
+ using System;
+#if !NET40
+ using System.Diagnostics.Tracing;
+#endif
+
+ using System.Globalization;
+ using System.Net;
+ using System.Linq;
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ using Microsoft.ApplicationInsights.WindowsServer.Channel.Helpers;
+
+#if NET40
+ using Microsoft.Diagnostics.Tracing;
+#endif
+
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ using Microsoft.ApplicationInsights.Channel.Implementation;
+ using Microsoft.ApplicationInsights.Web.TestFramework;
+ using Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.Implementation;
+
+ using Assert = Xunit.Assert;
+
+#if NET45
+ using TaskEx = System.Threading.Tasks.Task;
+#endif
+
+ public class BackoffLogicManagerTest
+ {
+ [TestClass]
+ public class DefaultBackoffEnabledReportingInterval
+ {
+ [TestMethod]
+ public void DefaultReportingIntervalInMinIs30Min()
+ {
+ Assert.Equal(30, new BackoffLogicManager().DefaultBackoffEnabledReportingInterval.TotalMinutes);
+ }
+ }
+
+ [TestClass]
+ public class GetBackendResponse
+ {
+ [TestMethod]
+ public void ReturnNullIfArgumentIsNull()
+ {
+ var manager = new BackoffLogicManager(TimeSpan.Zero);
+ Assert.Null(manager.GetBackendResponse(null));
+ }
+
+ [TestMethod]
+ public void ReturnNullIfArgumentEmpty()
+ {
+ var manager = new BackoffLogicManager(TimeSpan.Zero);
+ Assert.Null(manager.GetBackendResponse(string.Empty));
+ }
+
+ [TestMethod]
+ public void IfContentCannotBeParsedNullIsReturned()
+ {
+ var manager = new BackoffLogicManager(TimeSpan.Zero);
+ Assert.Null(manager.GetBackendResponse("ab}{"));
+ }
+
+ [TestMethod]
+ public void IfContentIsUnexpectedJsonNullIsReturned()
+ {
+ var manager = new BackoffLogicManager(TimeSpan.Zero);
+ Assert.Null(manager.GetBackendResponse("[1,2]"));
+ }
+
+ [TestMethod]
+ public void BackendResponseIsReturnedForCorrectContent()
+ {
+ string content = BackendResponseHelper.CreateBackendResponse(itemsReceived: 100, itemsAccepted: 1, errorCodes: new[] {"206"}, indexStartWith: 84);
+
+ var manager = new BackoffLogicManager(TimeSpan.Zero);
+ var backendResponse = manager.GetBackendResponse(content);
+
+ Assert.Equal(1, backendResponse.ItemsAccepted);
+ Assert.Equal(100, backendResponse.ItemsReceived);
+ Assert.Equal(1, backendResponse.Errors.Length); // Even though accepted number of items is 1 out of 99 we get only 1 error back. We do not expect same in production but SDK should handle it correctly.
+ Assert.Equal(84, backendResponse.Errors[0].Index);
+ Assert.Equal(206, backendResponse.Errors[0].StatusCode);
+ Assert.Equal("Explanation", backendResponse.Errors[0].Message);
+ }
+ }
+
+ [TestClass]
+ public class ScheduleRestore
+ {
+ [TestMethod]
+ public void NoErrorDelayIsSameAsSlotDelay()
+ {
+ var manager = new BackoffLogicManager(TimeSpan.Zero);
+ manager.ScheduleRestore(string.Empty, () => null);
+ Assert.Equal(TimeSpan.FromSeconds(10), manager.CurrentDelay);
+ }
+
+ [TestMethod]
+ public void FirstErrorDelayIsSameAsSlotDelay()
+ {
+ var manager = new BackoffLogicManager(TimeSpan.Zero);
+ manager.ReportBackoffEnabled(500);
+ manager.ScheduleRestore(string.Empty, () => null);
+ Assert.Equal(TimeSpan.FromSeconds(10), manager.CurrentDelay);
+ }
+
+ [TestMethod]
+ public void UpperBoundOfDelayIsMaxDelay()
+ {
+ var manager = new BackoffLogicManager(TimeSpan.Zero, TimeSpan.Zero);
+
+ PrivateObject wrapper = new PrivateObject(manager);
+ wrapper.SetField("consecutiveErrors", int.MaxValue);
+
+ manager.ScheduleRestore(string.Empty, () => null);
+
+ Assert.InRange(manager.CurrentDelay, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(3600));
+ }
+
+ [TestMethod]
+ public void RetryAfterFromHeadersHasMorePriorityThanExponentialRetry()
+ {
+ WebHeaderCollection headers = new WebHeaderCollection();
+ headers.Add("Retry-After", DateTimeOffset.UtcNow.AddSeconds(30).ToString("O"));
+
+ var manager = new BackoffLogicManager(TimeSpan.Zero);
+ manager.ScheduleRestore(headers, () => null);
+
+ Xunit.Assert.InRange(manager.CurrentDelay, TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(30));
+ }
+
+ [TestMethod]
+ public void AssertIfDateParseErrorCausesDefaultDelay()
+ {
+ WebHeaderCollection headers = new WebHeaderCollection();
+ headers.Add("Retry-After", "no one can parse me");
+
+ var manager = new BackoffLogicManager(TimeSpan.Zero);
+ manager.ScheduleRestore(headers, () => null);
+ Assert.Equal(TimeSpan.FromSeconds(10), manager.CurrentDelay);
+ }
+
+ [TestMethod]
+ public void RetryAfterOlderThanNowCausesDefaultDelay()
+ {
+ // An old date
+ string retryAfterDateString = DateTime.Now.AddMinutes(-1).ToString("R", CultureInfo.InvariantCulture);
+
+ WebHeaderCollection headers = new WebHeaderCollection();
+ headers.Add("Retry-After", retryAfterDateString);
+
+ var manager = new BackoffLogicManager(TimeSpan.Zero);
+ manager.ScheduleRestore(headers, () => null);
+ Assert.Equal(TimeSpan.FromSeconds(10), manager.CurrentDelay);
+ }
+ }
+
+ [TestClass]
+ public class ReportDiagnosticMessage
+ {
+ [TestMethod]
+ public void ReportBackoffWriteMessageOnce()
+ {
+ using (var listener = new TestEventListener())
+ {
+ const long AllKeywords = -1;
+ listener.EnableEvents(TelemetryChannelEventSource.Log, EventLevel.Error, (EventKeywords)AllKeywords);
+
+ var manager = new BackoffLogicManager(TimeSpan.Zero);
+ manager.ReportBackoffEnabled(200);
+ manager.ReportBackoffEnabled(200);
+
+ var traces = listener.Messages.ToList();
+
+ Assert.Equal(1, traces.Count);
+ Assert.Equal(2, traces[0].EventId);
+ }
+ }
+
+ [TestMethod]
+ public void ReportBackoffWriteDoesNotLogMessagesBeforeIntervalPasses()
+ {
+ using (var listener = new TestEventListener())
+ {
+ const long AllKeywords = -1;
+ listener.EnableEvents(TelemetryChannelEventSource.Log, EventLevel.Error, (EventKeywords)AllKeywords);
+
+ var manager = new BackoffLogicManager(TimeSpan.FromSeconds(20));
+
+ manager.ReportBackoffEnabled(200);
+ manager.ReportBackoffEnabled(200);
+
+ var traces = listener.Messages.ToList();
+
+ Assert.Equal(0, traces.Count);
+ }
+ }
+
+ [TestMethod]
+ public void ReportBackoffWritesLogMessagesAfterIntervalPasses()
+ {
+ using (var listener = new TestEventListener())
+ {
+ const long AllKeywords = -1;
+ listener.EnableEvents(TelemetryChannelEventSource.Log, EventLevel.Error, (EventKeywords)AllKeywords);
+
+ var manager = new BackoffLogicManager(TimeSpan.FromMilliseconds(10));
+
+ System.Threading.Thread.Sleep(10);
+
+ manager.ReportBackoffEnabled(200);
+ manager.ReportBackoffEnabled(200);
+
+ var traces = listener.Messages.ToList();
+
+ Assert.Equal(1, traces.Count);
+ }
+ }
+
+ [TestMethod]
+ public void ReportBackoffWriteIsLoggedAgainAfterReportDisabledWasCalled()
+ {
+ using (var listener = new TestEventListener())
+ {
+ const long AllKeywords = -1;
+ listener.EnableEvents(TelemetryChannelEventSource.Log, EventLevel.Error, (EventKeywords)AllKeywords);
+
+ var manager = new BackoffLogicManager(TimeSpan.Zero);
+
+ manager.ReportBackoffEnabled(200);
+ manager.ReportBackoffEnabled(200);
+
+ manager.ReportBackoffDisabled();
+ manager.ReportBackoffDisabled();
+
+ manager.ReportBackoffEnabled(200);
+ manager.ReportBackoffEnabled(200);
+
+ var traces = listener.Messages.ToList();
+ Assert.Equal(3, traces.Count);
+ Assert.Equal(2, traces[0].EventId);
+ Assert.Equal(1, traces[1].EventId);
+ Assert.Equal(2, traces[2].EventId);
+ }
+ }
+
+ [TestMethod]
+ public void DisableDoesNotLogMessageIfEnabledWasNotCalled()
+ {
+ using (var listener = new TestEventListener())
+ {
+ const long AllKeywords = -1;
+ listener.EnableEvents(TelemetryChannelEventSource.Log, EventLevel.Error, (EventKeywords)AllKeywords);
+
+ var manager = new BackoffLogicManager(TimeSpan.Zero);
+
+ manager.ReportBackoffDisabled();
+ manager.ReportBackoffDisabled();
+
+ var traces = listener.Messages.ToList();
+ Assert.Equal(0, traces.Count);
+ }
+ }
+ }
+
+ [TestClass]
+ public class ConsecutiveErrors
+ {
+ [TestMethod]
+ public void DoNotIncrementConsecutiveErrorsMoreOftenThanOnceInminIntervalToUpdateConsecutiveErrors()
+ {
+ BackoffLogicManager manager = new BackoffLogicManager(TimeSpan.Zero, TimeSpan.FromDays(1));
+
+ Task[] tasks = new Task[10];
+ for (int i = 0; i < 10; ++i)
+ {
+ tasks[i] = TaskEx.Run(() => manager.ReportBackoffEnabled(500));
+ }
+
+ Task.WaitAll(tasks);
+
+ Assert.Equal(1, manager.ConsecutiveErrors);
+ }
+
+ [TestMethod]
+ public void IncrementConsecutiveErrorsAfterMinIntervalToUpdateConsecutiveErrorsPassed()
+ {
+ BackoffLogicManager manager = new BackoffLogicManager(TimeSpan.Zero, TimeSpan.FromMilliseconds(1));
+
+ manager.ReportBackoffEnabled(500);
+ Thread.Sleep(1);
+ manager.ReportBackoffEnabled(500);
+
+ Assert.Equal(2, manager.ConsecutiveErrors);
+ }
+
+ [TestMethod]
+ public void ConsecutiveErrorsCanAlwaysBeResetTo0()
+ {
+ BackoffLogicManager manager = new BackoffLogicManager(TimeSpan.Zero, TimeSpan.FromDays(1));
+
+ manager.ReportBackoffEnabled(500);
+ manager.ResetConsecutiveErrors();
+
+ Assert.Equal(0, manager.ConsecutiveErrors);
+ }
+ }
+ }
+}
diff --git a/Test/ServerTelemetryChannel.Test/Shared.Tests/Implementation/ErrorHandlingTransmissionPolicyTest.cs b/Test/ServerTelemetryChannel.Test/Shared.Tests/Implementation/ErrorHandlingTransmissionPolicyTest.cs
index 2782294353..4aed80e0a8 100644
--- a/Test/ServerTelemetryChannel.Test/Shared.Tests/Implementation/ErrorHandlingTransmissionPolicyTest.cs
+++ b/Test/ServerTelemetryChannel.Test/Shared.Tests/Implementation/ErrorHandlingTransmissionPolicyTest.cs
@@ -4,15 +4,14 @@
#if NET45
using System.Diagnostics.Tracing;
#endif
- using System.Collections.Generic;
- using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights.Channel;
- using Microsoft.ApplicationInsights.DataContracts;
+ using Microsoft.ApplicationInsights.Channel.Implementation;
using Microsoft.ApplicationInsights.Extensibility.Implementation;
+ using Microsoft.ApplicationInsights.WindowsServer.Channel.Helpers;
using Microsoft.ApplicationInsights.Web.TestFramework;
using Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.Helpers;
#if NET40
@@ -20,9 +19,8 @@
#endif
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Assert = Xunit.Assert;
-#if NET45
- using TaskEx = System.Threading.Tasks.Task;
-#endif
+ using System.Text;
+ using System.IO;
public class ErrorHandlingTransmissionPolicyTest
{
@@ -33,16 +31,15 @@ public class HandleTransmissionSentEvent : ErrorHandlingTransmissionPolicyTest
public void StopsTransmissionSendingWhenTransmissionTimesOut()
{
var policyApplied = new AutoResetEvent(false);
- var transmitter = new StubTransmitter();
+ var transmitter = new StubTransmitter(new TestableBackoffLogicManager(TimeSpan.FromSeconds(10)));
transmitter.OnApplyPolicies = () =>
{
policyApplied.Set();
};
- var policy = new TestableErrorHandlingTransmissionPolicy();
+ var policy = new ErrorHandlingTransmissionPolicy();
policy.Initialize(transmitter);
- policy.BackOffTime = TimeSpan.FromSeconds(10);
transmitter.OnTransmissionSent(new TransmissionProcessedEventArgs(new StubTransmission(), CreateException(statusCode: 408)));
Assert.True(policyApplied.WaitOne(100));
@@ -53,16 +50,15 @@ public void StopsTransmissionSendingWhenTransmissionTimesOut()
public void ResumesTransmissionSenderAfterPauseDuration()
{
var policyApplied = new AutoResetEvent(false);
- var transmitter = new StubTransmitter();
+ var transmitter = new StubTransmitter(new TestableBackoffLogicManager(TimeSpan.FromMilliseconds(1)));
transmitter.OnApplyPolicies = () =>
{
policyApplied.Set();
};
- var policy = new TestableErrorHandlingTransmissionPolicy();
+ var policy = new ErrorHandlingTransmissionPolicy();
policy.Initialize(transmitter);
- policy.BackOffTime = TimeSpan.FromMilliseconds(1);
transmitter.OnTransmissionSent(new TransmissionProcessedEventArgs(new StubTransmission(), CreateException(statusCode: 408)));
Assert.True(policyApplied.WaitOne(100));
@@ -73,14 +69,11 @@ public void ResumesTransmissionSenderAfterPauseDuration()
[TestMethod]
public void KeepsTransmissionSenderPausedWhenAdditionalTransmissionsFail()
{
- var transmitter = new StubTransmitter();
- var policy = new TestableErrorHandlingTransmissionPolicy();
+ var transmitter = new StubTransmitter(new TestableBackoffLogicManager(TimeSpan.FromMinutes(1)));
+ var policy = new ErrorHandlingTransmissionPolicy();
policy.Initialize(transmitter);
- policy.BackOffTime = TimeSpan.FromMilliseconds(10);
transmitter.OnTransmissionSent(new TransmissionProcessedEventArgs(new StubTransmission(), CreateException(statusCode: 408)));
-
- policy.BackOffTime = TimeSpan.FromMilliseconds(50);
transmitter.OnTransmissionSent(new TransmissionProcessedEventArgs(new StubTransmission(), CreateException(statusCode: 408)));
Thread.Sleep(TimeSpan.FromMilliseconds(30));
@@ -111,14 +104,14 @@ public void RetriesFailedTransmissionIfItsNumberOfAttemptsDidNotReachMaximum()
public void RetriesFailedTransmissionInfinitely()
{
Transmission enqueuedTransmission = null;
- var transmitter = new StubTransmitter();
+
+ var transmitter = new StubTransmitter(new TestableBackoffLogicManager(TimeSpan.FromMilliseconds(10)));
transmitter.OnEnqueue = transmission =>
{
enqueuedTransmission = transmission;
};
- var policy = new TestableErrorHandlingTransmissionPolicy();
- policy.BackOffTime = TimeSpan.FromMilliseconds(10);
+ var policy = new ErrorHandlingTransmissionPolicy();
policy.Initialize(transmitter);
var failedTransmission = new StubTransmission();
@@ -145,7 +138,7 @@ public void DoesNotRetrySuccessfulTransmission()
transmitter.OnTransmissionSent(new TransmissionProcessedEventArgs(successfulTransmission));
Assert.Null(enqueuedTransmission);
- Assert.Equal(0, policy.ConsecutiveErrors);
+ Assert.Equal(0, transmitter.BackoffLogicManager.ConsecutiveErrors);
}
[TestMethod]
@@ -169,9 +162,9 @@ public void CatchesAndLogsAsynchronousExceptionsThrownByTransmitterWhenPausingTr
[TestMethod, Timeout(1000)]
public void CatchesAndLogsSynchronousExceptionsThrownByTransmitterWhenResumingTransmission()
{
- var policy = new TestableErrorHandlingTransmissionPolicy { BackOffTime = TimeSpan.FromMilliseconds(1) };
+ var policy = new ErrorHandlingTransmissionPolicy();
var exception = CreateException(statusCode: 408);
- var transmitter = new StubTransmitter();
+ var transmitter = new StubTransmitter(new TestableBackoffLogicManager(TimeSpan.FromMilliseconds(1)));
transmitter.OnApplyPolicies = () =>
{
if (policy.MaxSenderCapacity == null)
@@ -182,6 +175,39 @@ public void CatchesAndLogsSynchronousExceptionsThrownByTransmitterWhenResumingTr
CatchesAndLogsExceptionThrownByTransmitter(policy, transmitter, exception);
}
+ [TestMethod]
+ public void LogsAdditionalTracesIfResponseIsProvided()
+ {
+ using (var listener = new TestEventListener())
+ {
+ // Arrange:
+ const long AllKeywords = -1;
+ listener.EnableEvents(TelemetryChannelEventSource.Log, EventLevel.LogAlways, (EventKeywords)AllKeywords);
+
+ Transmission enqueuedTransmission = null;
+ var transmitter = new StubTransmitter (new BackoffLogicManager(TimeSpan.FromMilliseconds(10)))
+ {
+ OnEnqueue = transmission => { enqueuedTransmission = transmission; }
+ };
+
+ var policy = new ErrorHandlingTransmissionPolicy();
+ policy.Initialize(transmitter);
+
+ var failedTransmission = new StubTransmission();
+ var response = new HttpWebResponseWrapper {Content = BackendResponseHelper.CreateBackendResponse(2, 1, new[] { "123" })};
+
+ // Act:
+ transmitter.OnTransmissionSent(new TransmissionProcessedEventArgs(failedTransmission, CreateException(statusCode: 408), response));
+
+ // Assert:
+ var traces = listener.Messages.Where(item => item.Level == EventLevel.Warning).ToList();
+ Assert.Equal(2, traces.Count);
+ Assert.Equal(23, traces[0].EventId); // failed to send
+ Assert.Equal(7, traces[1].EventId); // additional trace
+ Assert.Equal("Explanation", traces[1].Payload[0]);
+ }
+ }
+
private static Task ThrowAsync(Exception e)
{
var tcs = new TaskCompletionSource