public static Metadata ProcessHeaders(Metadata headers) { if (headers == null) { throw new ArgumentNullException(nameof(headers)); } Metadata.Entry trackingEntry = headers.FirstOrDefault(x => string.CompareOrdinal(x.Key, s_TrackingContextKeyName) == 0); // Retrieve the tracking context from the message header, if it exists. if (trackingEntry != null) { // If an tracking context exists in the message header, always use it to replace the ambient context. TrackingContext tc = TrackingContext.DeSerialize(trackingEntry.ValueBytes); tc.SetAsCurrent(); } else { // If no tracking context exists then create one. TrackingContext.NewCurrentIfEmpty(); Debug.Assert(TrackingContext.Current != null); // Copy the tracking context to the message header. byte[] byteArray = TrackingContext.Serialize(TrackingContext.Current); headers.Add(s_TrackingContextKeyName, byteArray); } return(headers); }
private static string CreateAndSerializeNewTrackingContext() { TrackingContext.NewCurrentIfEmpty(); Debug.Assert(TrackingContext.Current != null); byte[] byteArray = TrackingContext.Serialize(TrackingContext.Current); return(byteArray.ByteArrayToBase64String()); }
public async Task Invoke(HttpContext httpContext) { IDictionary <string, string> extraHeaders = m_SetupFunc?.Invoke(httpContext) ?? new Dictionary <string, string>(); Debug.Assert(extraHeaders != null); TrackingContext.NewCurrentIfEmpty(extraHeaders); using (LogContext.Push(new TrackingContextEnricher())) { // Must await the next middleware, otherwise the log context will unwind at the first asyncronious operation. await m_Next.Invoke(httpContext).ConfigureAwait(false); } }
public async Task TrackingContext_NestedMultipleTasks_CallChainIdPersists() { TrackingContext.NewCurrentIfEmpty(); Guid masterCallChainId = TrackingContext.Current.CallChainId; bool result0 = false; bool result1 = false; bool result2 = false; bool result3 = false; bool result4 = false; await Task.Run(async() => { TrackingContext tc0 = TrackingContext.Current; result0 = masterCallChainId == tc0.CallChainId; await Task.Run(async() => { TrackingContext tc1 = TrackingContext.Current; result1 = masterCallChainId == tc1.CallChainId; await Task.Run(() => { TrackingContext tc2 = TrackingContext.Current; result2 = masterCallChainId == tc2.CallChainId; }); }); await Task.Run(async() => { TrackingContext tc3 = TrackingContext.Current; result3 = masterCallChainId == tc3.CallChainId; await Task.Run(() => { TrackingContext tc4 = TrackingContext.Current; result4 = masterCallChainId == tc4.CallChainId; }); }); }); Assert.IsTrue(result0); Assert.IsTrue(result1); Assert.IsTrue(result2); Assert.IsTrue(result3); Assert.IsTrue(result4); Assert.IsTrue(masterCallChainId == TrackingContext.Current.CallChainId); }
public async Task TrackingContext_GivenNestedMultipleTasks_ThenCallChainIdPersists() { TrackingContext.NewCurrentIfEmpty(); Guid masterCallChainId = TrackingContext.Current.CallChainId; bool result0 = false; bool result1 = false; bool result2 = false; bool result3 = false; bool result4 = false; await Task.Run(async() => { TrackingContext tc0 = TrackingContext.Current; result0 = masterCallChainId == tc0.CallChainId; await Task.Run(async() => { TrackingContext tc1 = TrackingContext.Current; result1 = masterCallChainId == tc1.CallChainId; await Task.Run(() => { TrackingContext tc2 = TrackingContext.Current; result2 = masterCallChainId == tc2.CallChainId; }); }); await Task.Run(async() => { TrackingContext tc3 = TrackingContext.Current; result3 = masterCallChainId == tc3.CallChainId; await Task.Run(() => { TrackingContext tc4 = TrackingContext.Current; result4 = masterCallChainId == tc4.CallChainId; }); }); }); result0.Should().BeTrue(); result1.Should().BeTrue(); result2.Should().BeTrue(); result3.Should().BeTrue(); result4.Should().BeTrue(); TrackingContext.Current.CallChainId.Should().Be(masterCallChainId); }
protected override async Task InterceptAsync( IInvocation invocation, Func <IInvocation, Task> proceed) { if (invocation == null) { throw new ArgumentNullException(nameof(invocation)); } if (proceed == null) { throw new ArgumentNullException(nameof(proceed)); } TrackingContext.NewCurrentIfEmpty(); await proceed(invocation).ConfigureAwait(false); }
public async Task Startup_GivenTrackingContextMiddleware_WhenTrackingContextSent_ThenNoTrackingContextReturned() { var textWriterSink = new StringWriter(); var hostBuilder = new HostBuilder() .ConfigureWebHost(webHost => { webHost.UseTestServer(); webHost.UseStartup <Startup>(); webHost.ConfigureServices((services) => StartupBase.ConfigureServicesWithLogSink( services, textWriterSink, $"{{{nameof(TrackingContext.CallChainId)}}}{textWriterSink.NewLine}")); }); var host = await hostBuilder.StartAsync(); var client = host.GetTestClient(); TrackingContext.NewCurrentIfEmpty(); TrackingContext currentContext = TrackingContext.Current; currentContext.Should().NotBeNull(); var requestMessage = new HttpRequestMessage(HttpMethod.Get, @"/api/values"); string tcHeader = TrackingContext.Serialize(currentContext).ByteArrayToBase64String(); requestMessage.Headers.Add(TrackingContextHelper.TrackingContextKeyName, tcHeader); var response = await client.SendAsync(requestMessage); response.EnsureSuccessStatusCode(); response.Headers.TryGetValues(TrackingContextHelper.TrackingContextKeyName, out IEnumerable <string> values).Should().BeFalse(); TrackingContext.Current.Should().NotBeNull(); TrackingContext.Current.Should().BeEquivalentTo(currentContext); IList <string> callChainIds = textWriterSink .ToString() .Split(textWriterSink.NewLine, StringSplitOptions.RemoveEmptyEntries) .ToList(); callChainIds.Count().Should().Be(10); // The call chain ID should be different on the server side. callChainIds.All(x => !x.Equals(currentContext.CallChainId.ToDashedString())).Should().BeTrue(); }
public async Task TrackingContext_MultipleTasks_ExtraHeadersPersists() { var extraHeaders = new Dictionary <string, string>() { { "AAA", "AAA" }, { "BBB", "BBB" }, }; TrackingContext.NewCurrentIfEmpty(extraHeaders); List <KeyValuePair <string, string> > masterExtraHeaders = TrackingContext.Current.ExtraHeaders.OrderBy(x => x.Key).ToList(); var task0 = Task.Run(() => { TrackingContext tc = TrackingContext.Current; return(masterExtraHeaders.SequenceEqual(tc.ExtraHeaders.OrderBy(x => x.Key).ToList())); }); var task1 = Task.Run(() => { TrackingContext tc = TrackingContext.Current; return(masterExtraHeaders.SequenceEqual(tc.ExtraHeaders.OrderBy(x => x.Key).ToList())); }); var task2 = Task.Run(() => { TrackingContext tc = TrackingContext.Current; return(masterExtraHeaders.SequenceEqual(tc.ExtraHeaders.OrderBy(x => x.Key).ToList())); }); var tasks = new OrderedTaskCollection <bool>( new[] { task0, task1, task2, }); var results = new List <bool>(); foreach (Task <bool> task in tasks) { results.Add(await task); } foreach (bool result in results) { Assert.IsTrue(result); } }
public async Task TrackingContext_MultipleTasks_OriginatorUtcTimestampPersists() { TrackingContext.NewCurrentIfEmpty(); DateTime masterOriginatorUtcTimestamp = TrackingContext.Current.OriginatorUtcTimestamp; var task0 = Task.Run(() => { TrackingContext tc = TrackingContext.Current; return(masterOriginatorUtcTimestamp == tc.OriginatorUtcTimestamp); }); var task1 = Task.Run(() => { TrackingContext tc = TrackingContext.Current; return(masterOriginatorUtcTimestamp == tc.OriginatorUtcTimestamp); }); var task2 = Task.Run(() => { TrackingContext tc = TrackingContext.Current; return(masterOriginatorUtcTimestamp == tc.OriginatorUtcTimestamp); }); var tasks = new OrderedTaskCollection <bool>( new[] { task0, task1, task2, }); var results = new List <bool>(); foreach (Task <bool> task in tasks) { results.Add(await task); } foreach (bool result in results) { Assert.IsTrue(result); } Assert.IsTrue(masterOriginatorUtcTimestamp == TrackingContext.Current.OriginatorUtcTimestamp); }
public async Task TrackingContext_MultipleTasks_CallChainIdPersists() { TrackingContext.NewCurrentIfEmpty(); Guid masterCallChainId = TrackingContext.Current.CallChainId; var task0 = Task.Run(() => { TrackingContext tc = TrackingContext.Current; return(masterCallChainId == tc.CallChainId); }); var task1 = Task.Run(() => { TrackingContext tc = TrackingContext.Current; return(masterCallChainId == tc.CallChainId); }); var task2 = Task.Run(() => { TrackingContext tc = TrackingContext.Current; return(masterCallChainId == tc.CallChainId); }); var tasks = new OrderedTaskCollection <bool>( new[] { task0, task1, task2, }); var results = new List <bool>(); foreach (Task <bool> task in tasks) { results.Add(await task); } foreach (bool result in results) { Assert.IsTrue(result); } Assert.IsTrue(masterCallChainId == TrackingContext.Current.CallChainId); }
private static void CreateTrackingContextFromHeaderValues(IEnumerable <string> values) { if (values == null) { throw new ArgumentNullException(nameof(values)); } if (values.Count() > 1) { throw new InvalidOperationException(Properties.Resources.CannotHaveMoreThanOneSerializedTrackingContextInHTTPHeaders); } string tcBase64 = values.FirstOrDefault(); if (string.IsNullOrWhiteSpace(tcBase64)) { TrackingContext.NewCurrentIfEmpty(); } else { // If an tracking context exists in the message header then use it to replace the current context. TrackingContext tc = TrackingContext.DeSerialize(tcBase64.Base64StringToByteArray()); tc.SetAsCurrent(); } }
public async Task Startup_GivenTrackingContextWithMergeAndOverwriteMiddleware_WhenTrackingContextSent_ThenTrackingContextReturned() { var textWriterSink = new StringWriter(); var hostBuilder = new HostBuilder() .ConfigureWebHost(webHost => { webHost.UseTestServer(); webHost.UseStartup <StartupWithMergeAndOverwrite>(); webHost.ConfigureServices((services) => StartupBase.ConfigureServicesWithLogSink( services, textWriterSink, $"{{{StartupBase.CountryOfOriginName}}}{textWriterSink.NewLine}")); }); var host = await hostBuilder.StartAsync(); var client = host.GetTestClient(); string headerKey = Guid.NewGuid().ToFlatString(); string headerValue = Guid.NewGuid().ToFlatString(); TrackingContext.NewCurrentIfEmpty(new Dictionary <string, string>() { { headerKey, headerValue }, { StartupBase.CountryOfOriginName, @"Germany" } }); TrackingContext currentContext = TrackingContext.Current; currentContext.Should().NotBeNull(); var requestMessage = new HttpRequestMessage(HttpMethod.Get, @"/api/values"); string tcHeader = TrackingContext.Serialize(currentContext).ByteArrayToBase64String(); requestMessage.Headers.Add(TrackingContextHelper.TrackingContextKeyName, tcHeader); var response = await client.SendAsync(requestMessage); response.EnsureSuccessStatusCode(); response.Headers.TryGetValues(TrackingContextHelper.TrackingContextKeyName, out IEnumerable <string> values).Should().BeTrue(); string tcBase64 = values.First(); TrackingContext tc = TrackingContext.DeSerialize(tcBase64.Base64StringToByteArray()); tc.CallChainId.Should().NotBeEmpty(); tc.OriginatorUtcTimestamp.Should().BeCloseTo(DateTime.UtcNow, 10000); tc.ExtraHeaders.Count.Should().Be(4); tc.ExtraHeaders[headerKey].Should().Be(headerValue); tc.ExtraHeaders[StartupBase.TraceIdentifierName].Should().NotBeNullOrWhiteSpace(); tc.ExtraHeaders[StartupBase.CountryOfOriginName].Should().Be(@"France"); tc.ExtraHeaders[StartupBase.RandomStringGeneratedWitEachCallName].Should().NotBeNullOrWhiteSpace(); TrackingContext.Current.Should().NotBeNull(); TrackingContext.Current.Should().BeEquivalentTo(currentContext); IList <string> countriesOfOrigin = textWriterSink .ToString() .Split(textWriterSink.NewLine, StringSplitOptions.RemoveEmptyEntries) .ToList(); countriesOfOrigin.Count().Should().Be(10); countriesOfOrigin.All(x => x.Equals(tc.ExtraHeaders[StartupBase.CountryOfOriginName])).Should().BeTrue(); }
public async Task TrackingContext_NestedMultipleTasks_CallChainIdCorresponds() { TrackingContext.NewCurrentIfEmpty(); Guid masterCallChainId = TrackingContext.Current.CallChainId; bool result0 = false; bool result1 = false; bool result2 = false; bool result3 = false; bool result4 = false; bool result5 = false; bool result6 = false; bool result7 = false; bool result8 = false; bool result9 = false; bool result10 = false; bool result11 = false; await Task.Run(async() => { TrackingContext tc0 = TrackingContext.Current; result0 = masterCallChainId == tc0.CallChainId; await Task.Run(async() => { TrackingContext tc1 = TrackingContext.Current; result1 = masterCallChainId == tc1.CallChainId; TrackingContext.NewCurrent(); TrackingContext tc2 = TrackingContext.Current; result2 = tc1.CallChainId != tc2.CallChainId && tc2.CallChainId != Guid.Empty; await Task.Run(() => { TrackingContext tc3 = TrackingContext.Current; result3 = tc2.CallChainId == tc3.CallChainId; TrackingContext.NewCurrent(); TrackingContext tc4 = TrackingContext.Current; result4 = tc3.CallChainId != tc4.CallChainId && tc4.CallChainId != Guid.Empty; }); TrackingContext tc5 = TrackingContext.Current; result5 = tc2.CallChainId == tc5.CallChainId; }); await Task.Run(async() => { TrackingContext tc1 = TrackingContext.Current; result6 = masterCallChainId == tc1.CallChainId; TrackingContext.NewCurrent(); TrackingContext tc2 = TrackingContext.Current; result7 = tc1.CallChainId != tc2.CallChainId && tc2.CallChainId != Guid.Empty; await Task.Run(() => { TrackingContext tc3 = TrackingContext.Current; result8 = tc2.CallChainId == tc3.CallChainId; TrackingContext.NewCurrent(); TrackingContext tc4 = TrackingContext.Current; result9 = tc3.CallChainId != tc4.CallChainId && tc4.CallChainId != Guid.Empty; }); TrackingContext tc5 = TrackingContext.Current; result10 = tc2.CallChainId == tc5.CallChainId; }); TrackingContext tc6 = TrackingContext.Current; result11 = tc0.CallChainId == tc6.CallChainId; }); Assert.IsTrue(result0); Assert.IsTrue(result1); Assert.IsTrue(result2); Assert.IsTrue(result3); Assert.IsTrue(result4); Assert.IsTrue(result5); Assert.IsTrue(result6); Assert.IsTrue(result7); Assert.IsTrue(result8); Assert.IsTrue(result9); Assert.IsTrue(result10); Assert.IsTrue(result11); Assert.IsTrue(masterCallChainId == TrackingContext.Current.CallChainId); }
protected override async Task InterceptAsync(IInvocation invocation, Func <IInvocation, Task> proceed) { TrackingContext.NewCurrentIfEmpty(); await proceed(invocation).ConfigureAwait(false); }
public async Task TrackingContext_GivenNestedMultipleTasks_ThenCallChainIdCorresponds() { TrackingContext.NewCurrentIfEmpty(); Guid masterCallChainId = TrackingContext.Current.CallChainId; bool result0 = false; bool result1 = false; bool result2 = false; bool result3 = false; bool result4 = false; bool result5 = false; bool result6 = false; bool result7 = false; bool result8 = false; bool result9 = false; bool result10 = false; bool result11 = false; await Task.Run(async() => { TrackingContext tc0 = TrackingContext.Current; result0 = masterCallChainId == tc0.CallChainId; await Task.Run(async() => { TrackingContext tc1 = TrackingContext.Current; result1 = masterCallChainId == tc1.CallChainId; TrackingContext.NewCurrent(); TrackingContext tc2 = TrackingContext.Current; result2 = tc1.CallChainId != tc2.CallChainId && tc2.CallChainId != Guid.Empty; await Task.Run(() => { TrackingContext tc3 = TrackingContext.Current; result3 = tc2.CallChainId == tc3.CallChainId; TrackingContext.NewCurrent(); TrackingContext tc4 = TrackingContext.Current; result4 = tc3.CallChainId != tc4.CallChainId && tc4.CallChainId != Guid.Empty; }); TrackingContext tc5 = TrackingContext.Current; result5 = tc2.CallChainId == tc5.CallChainId; }); await Task.Run(async() => { TrackingContext tc1 = TrackingContext.Current; result6 = masterCallChainId == tc1.CallChainId; TrackingContext.NewCurrent(); TrackingContext tc2 = TrackingContext.Current; result7 = tc1.CallChainId != tc2.CallChainId && tc2.CallChainId != Guid.Empty; await Task.Run(() => { TrackingContext tc3 = TrackingContext.Current; result8 = tc2.CallChainId == tc3.CallChainId; TrackingContext.NewCurrent(); TrackingContext tc4 = TrackingContext.Current; result9 = tc3.CallChainId != tc4.CallChainId && tc4.CallChainId != Guid.Empty; }); TrackingContext tc5 = TrackingContext.Current; result10 = tc2.CallChainId == tc5.CallChainId; }); TrackingContext tc6 = TrackingContext.Current; result11 = tc0.CallChainId == tc6.CallChainId; }); result0.Should().BeTrue(); result1.Should().BeTrue(); result2.Should().BeTrue(); result3.Should().BeTrue(); result4.Should().BeTrue(); result5.Should().BeTrue(); result6.Should().BeTrue(); result7.Should().BeTrue(); result8.Should().BeTrue(); result9.Should().BeTrue(); result10.Should().BeTrue(); result11.Should().BeTrue(); TrackingContext.Current.CallChainId.Should().Be(masterCallChainId); }