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);
        }
Exemple #2
0
 private static string CreateAndSerializeNewTrackingContext()
 {
     TrackingContext.NewCurrentIfEmpty();
     Debug.Assert(TrackingContext.Current != null);
     byte[] byteArray = TrackingContext.Serialize(TrackingContext.Current);
     return(byteArray.ByteArrayToBase64String());
 }
Exemple #3
0
        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);
            }
        }
Exemple #4
0
        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);
        }
Exemple #6
0
        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);
        }
Exemple #7
0
        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();
        }
Exemple #8
0
        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);
            }
        }
Exemple #9
0
        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);
        }
Exemple #10
0
        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);
        }
Exemple #11
0
        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();
            }
        }
Exemple #12
0
        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();
        }
Exemple #13
0
        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);
        }