public async Task ExtractContextIrrespectiveOfSamplingDecision(SamplingDecision samplingDecision) { try { var expectedTraceId = ActivityTraceId.CreateRandom(); var expectedParentSpanId = ActivitySpanId.CreateRandom(); var expectedTraceState = "rojo=1,congo=2"; var activityContext = new ActivityContext(expectedTraceId, expectedParentSpanId, ActivityTraceFlags.Recorded, expectedTraceState); var expectedBaggage = Baggage.SetBaggage("key1", "value1").SetBaggage("key2", "value2"); OpenTelemetrySdk.SetDefaultTextMapPropagator(new ExtractOnlyPropagator(activityContext, expectedBaggage)); // Arrange using (var testFactory = this.factory .WithWebHostBuilder(builder => builder.ConfigureTestServices(services => { this.openTelemetrySdk = OpenTelemetrySdk.CreateTracerProviderBuilder() .SetSampler(new TestSampler(samplingDecision)) .AddAspNetCoreInstrumentation() .Build(); }))) { using var client = testFactory.CreateClient(); // Test TraceContext Propagation var request = new HttpRequestMessage(HttpMethod.Get, "/api/GetChildActivityTraceContext"); var response = await client.SendAsync(request); var childActivityTraceContext = JsonConvert.DeserializeObject <Dictionary <string, string> >(response.Content.ReadAsStringAsync().Result); response.EnsureSuccessStatusCode(); Assert.Equal(expectedTraceId.ToString(), childActivityTraceContext["TraceId"]); Assert.Equal(expectedTraceState, childActivityTraceContext["TraceState"]); Assert.NotEqual(expectedParentSpanId.ToString(), childActivityTraceContext["ParentSpanId"]); // there is a new activity created in instrumentation therefore the ParentSpanId is different that what is provided in the headers // Test Baggage Context Propagation request = new HttpRequestMessage(HttpMethod.Get, "/api/GetChildActivityBaggageContext"); response = await client.SendAsync(request); var childActivityBaggageContext = JsonConvert.DeserializeObject <IReadOnlyDictionary <string, string> >(response.Content.ReadAsStringAsync().Result); response.EnsureSuccessStatusCode(); Assert.Single(childActivityBaggageContext, item => item.Key == "key1" && item.Value == "value1"); Assert.Single(childActivityBaggageContext, item => item.Key == "key2" && item.Value == "value2"); } } finally { OpenTelemetrySdk.SetDefaultTextMapPropagator(new CompositeTextMapPropagator(new TextMapPropagator[] { new TraceContextPropagator(), new BaggagePropagator(), })); } }
public void GrpcAspNetCoreInstrumentationAddsCorrectAttributesWhenItCreatesNewActivity(bool?enableGrpcAspNetCoreSupport) { try { // B3Propagator along with the headers passed to the client.SayHello ensure that the instrumentation creates a sibling activity OpenTelemetrySdk.SetDefaultTextMapPropagator(new B3Propagator()); var processor = new Mock <BaseProcessor <Activity> >(); var tracerProviderBuilder = OpenTelemetrySdk.CreateTracerProviderBuilder(); if (enableGrpcAspNetCoreSupport.HasValue) { tracerProviderBuilder.AddAspNetCoreInstrumentation(options => { options.EnableGrpcAspNetCoreSupport = enableGrpcAspNetCoreSupport.Value; }); } else { tracerProviderBuilder.AddAspNetCoreInstrumentation(); } using var tracerProvider = tracerProviderBuilder .AddProcessor(processor.Object) .Build(); var clientLoopbackAddresses = new[] { IPAddress.Loopback.ToString(), IPAddress.IPv6Loopback.ToString() }; var uri = new Uri($"http://localhost:{this.server.Port}"); using var channel = GrpcChannel.ForAddress(uri); var client = new Greeter.GreeterClient(channel); var headers = new Metadata(); headers.Add("traceparent", "00-120dc44db5b736468afb112197b0dbd3-5dfbdf27ec544544-01"); headers.Add("x-b3-traceid", "120dc44db5b736468afb112197b0dbd3"); headers.Add("x-b3-spanid", "b0966f651b9e0126"); headers.Add("x-b3-sampled", "1"); client.SayHello(new HelloRequest(), headers); WaitForProcessorInvocations(processor, 4); Assert.Equal(4, processor.Invocations.Count); // SetParentProvider, OnStart (framework activity), OnStart (instrumentation activity), OnStop (instrumentation activity) var activity = GetActivityFromProcessorInvocation(processor, nameof(processor.Object.OnEnd), OperationNameHttpRequestIn); Assert.Equal(ActivityKind.Server, activity.Kind); if (!enableGrpcAspNetCoreSupport.HasValue || enableGrpcAspNetCoreSupport.Value) { Assert.Equal("grpc", activity.GetTagValue(SemanticConventions.AttributeRpcSystem)); Assert.Equal("greet.Greeter", activity.GetTagValue(SemanticConventions.AttributeRpcService)); Assert.Equal("SayHello", activity.GetTagValue(SemanticConventions.AttributeRpcMethod)); Assert.Contains(activity.GetTagValue(SemanticConventions.AttributeNetPeerIp), clientLoopbackAddresses); Assert.NotEqual(0, activity.GetTagValue(SemanticConventions.AttributeNetPeerPort)); Assert.Null(activity.GetTagValue(GrpcTagHelper.GrpcMethodTagName)); Assert.Null(activity.GetTagValue(GrpcTagHelper.GrpcStatusCodeTagName)); Assert.Equal(0, activity.GetTagValue(SemanticConventions.AttributeRpcGrpcStatusCode)); } else { Assert.NotNull(activity.GetTagValue(GrpcTagHelper.GrpcMethodTagName)); Assert.NotNull(activity.GetTagValue(GrpcTagHelper.GrpcStatusCodeTagName)); } Assert.Equal(Status.Unset, activity.GetStatus()); // The following are http.* attributes that are also included on the span for the gRPC invocation. Assert.Equal($"localhost:{this.server.Port}", activity.GetTagValue(SemanticConventions.AttributeHttpHost)); Assert.Equal("POST", activity.GetTagValue(SemanticConventions.AttributeHttpMethod)); Assert.Equal("/greet.Greeter/SayHello", activity.GetTagValue(SpanAttributeConstants.HttpPathKey)); Assert.Equal($"http://localhost:{this.server.Port}/greet.Greeter/SayHello", activity.GetTagValue(SemanticConventions.AttributeHttpUrl)); Assert.StartsWith("grpc-dotnet", activity.GetTagValue(SemanticConventions.AttributeHttpUserAgent) as string); } finally { // Set the SDK to use the default propagator for other unit tests OpenTelemetrySdk.SetDefaultTextMapPropagator(new CompositeTextMapPropagator(new TextMapPropagator[] { new TraceContextPropagator(), new BaggagePropagator(), })); } }
public async Task CustomPropagator() { try { var activityProcessor = new Mock <BaseProcessor <Activity> >(); var expectedTraceId = ActivityTraceId.CreateRandom(); var expectedSpanId = ActivitySpanId.CreateRandom(); var propagator = new Mock <TextMapPropagator>(); propagator.Setup(m => m.Extract(It.IsAny <PropagationContext>(), It.IsAny <HttpRequest>(), It.IsAny <Func <HttpRequest, string, IEnumerable <string> > >())).Returns( new PropagationContext( new ActivityContext( expectedTraceId, expectedSpanId, ActivityTraceFlags.Recorded), default)); // Arrange using (var testFactory = this.factory .WithWebHostBuilder(builder => builder.ConfigureTestServices(services => { OpenTelemetrySdk.SetDefaultTextMapPropagator(propagator.Object); this.openTelemetrySdk = OpenTelemetrySdk.CreateTracerProviderBuilder() .AddAspNetCoreInstrumentation() .AddProcessor(activityProcessor.Object) .Build(); }))) { using var client = testFactory.CreateClient(); var response = await client.GetAsync("/api/values/2"); response.EnsureSuccessStatusCode(); // Status Code 200-299 WaitForProcessorInvocations(activityProcessor, 4); } // List of invocations on the processor // 1. SetParentProvider for TracerProviderSdk // 2. OnStart for the activity created by AspNetCore with the OperationName: Microsoft.AspNetCore.Hosting.HttpRequestIn // 3. OnStart for the sibling activity created by the instrumentation library with the OperationName: Microsoft.AspNetCore.Hosting.HttpRequestIn and the first tag that is added is (IsCreatedByInstrumentation, bool.TrueString) // 4. OnEnd for the sibling activity created by the instrumentation library with the OperationName: Microsoft.AspNetCore.Hosting.HttpRequestIn and the first tag that is added is (IsCreatedByInstrumentation, bool.TrueString) Assert.Equal(4, activityProcessor.Invocations.Count); var startedActivities = activityProcessor.Invocations.Where(invo => invo.Method.Name == "OnStart"); var stoppedActivities = activityProcessor.Invocations.Where(invo => invo.Method.Name == "OnEnd"); Assert.Equal(2, startedActivities.Count()); Assert.Single(stoppedActivities); // The activity created by the framework and the sibling activity are both sent to Processor.OnStart Assert.Equal(2, startedActivities.Count(item => { var startedActivity = item.Arguments[0] as Activity; return(startedActivity.OperationName == HttpInListener.ActivityOperationName); })); // we should only call Processor.OnEnd once for the sibling activity Assert.Single(activityProcessor.Invocations, invo => invo.Method.Name == "OnEnd"); var activity = activityProcessor.Invocations.FirstOrDefault(invo => invo.Method.Name == "OnEnd").Arguments[0] as Activity; Assert.True(activity.Duration != TimeSpan.Zero); Assert.Equal("api/Values/{id}", activity.DisplayName); Assert.Equal(expectedTraceId, activity.Context.TraceId); Assert.Equal(expectedSpanId, activity.ParentSpanId); ValidateAspNetCoreActivity(activity, "/api/values/2"); } finally { OpenTelemetrySdk.SetDefaultTextMapPropagator(new CompositeTextMapPropagator(new TextMapPropagator[] { new TraceContextPropagator(), new BaggagePropagator(), })); } }