예제 #1
0
        private static void DoWork(IDatabase db, ActivitySource activitySource)
        {
            // Start another activity. If another activity was already started, it'll use that activity as the parent activity.
            // In this example, the main method already started a activity, so that'll be the parent activity, and this will be
            // a child activity.
            using (Activity activity = activitySource.StartActivity("DoWork"))
            {
                try
                {
                    db.StringSet("key", "value " + DateTime.Now.ToLongDateString());

                    System.Console.WriteLine("Doing busy work");
                    Thread.Sleep(1000);

                    // run a command, in this case a GET
                    var myVal = db.StringGet("key");

                    System.Console.WriteLine(myVal);
                }
                catch (ArgumentOutOfRangeException e)
                {
                    // Set status upon error
                    activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(Status.Internal.CanonicalCode));
                    activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, e.ToString());
                }

                // Annotate our activity to capture metadata about our operation
                var attributes = new Dictionary <string, object>
                {
                    { "use", "demo" },
                };
                activity.AddEvent(new ActivityEvent("Invoking DoWork", attributes));
            }
        }
        public override void OnException(Activity activity, object payload)
        {
            if (activity.IsAllDataRequested)
            {
                if (!(this.stopExceptionFetcher.Fetch(payload) is Exception exc))
                {
                    InstrumentationEventSource.Log.NullPayload(nameof(HttpHandlerDiagnosticListener), nameof(this.OnException));
                    return;
                }

                if (exc is HttpRequestException)
                {
                    if (exc.InnerException is SocketException exception)
                    {
                        switch (exception.SocketErrorCode)
                        {
                        case SocketError.HostNotFound:
                            Status status = Status.InvalidArgument;
                            activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
                            activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, exc.Message);
                            return;
                        }
                    }

                    if (exc.InnerException != null)
                    {
                        Status status = Status.Unknown;
                        activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
                        activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, exc.Message);
                    }
                }
            }
        }
예제 #3
0
        public override void OnStopActivity(Activity activity, object payload)
        {
            if (activity.IsAllDataRequested)
            {
                var requestTaskStatus = this.stopRequestStatusFetcher.Fetch(payload) as TaskStatus?;

                if (requestTaskStatus.HasValue)
                {
                    if (requestTaskStatus != TaskStatus.RanToCompletion)
                    {
                        if (requestTaskStatus == TaskStatus.Canceled)
                        {
                            Status status = Status.Cancelled;
                            activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
                        }
                        else if (requestTaskStatus != TaskStatus.Faulted)
                        {
                            // Faults are handled in OnException and should already have a span.Status of Unknown w/ Description.
                            Status status = Status.Unknown;
                            activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
                        }
                    }
                }

                if (this.stopResponseFetcher.Fetch(payload) is HttpResponseMessage response)
                {
                    // response could be null for DNS issues, timeouts, etc...
                    activity.AddTag(SpanAttributeConstants.HttpStatusCodeKey, response.StatusCode.ToString());

                    Status status = SpanHelper.ResolveSpanStatusForHttpStatusCode((int)response.StatusCode);
                    activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
                    activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, response.ReasonPhrase);
                }
            }
        }
예제 #4
0
        public override void OnException(Activity activity, object valueValue)
        {
            Status status = Status.Unknown;

            activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
            activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, valueValue?.ToString());
        }
예제 #5
0
        private static void VerifyActivityData(Activity activity, bool isSet, EndPoint endPoint)
        {
            if (isSet)
            {
                Assert.Equal("SETEX", activity.DisplayName);
                Assert.Equal("SETEX", activity.Tags.FirstOrDefault(t => t.Key == SemanticConventions.AttributeDbStatement).Value);
            }
            else
            {
                Assert.Equal("GET", activity.DisplayName);
                Assert.Equal("GET", activity.Tags.FirstOrDefault(t => t.Key == SemanticConventions.AttributeDbStatement).Value);
            }

            Assert.Equal(SpanHelper.GetCachedCanonicalCodeString(StatusCanonicalCode.Ok), activity.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.StatusCodeKey).Value);
            Assert.Equal("redis", activity.Tags.FirstOrDefault(t => t.Key == SemanticConventions.AttributeDbSystem).Value);
            Assert.Equal("0", activity.Tags.FirstOrDefault(t => t.Key == StackExchangeRedisCallsInstrumentation.RedisDatabaseIndexKeyName).Value);

            if (endPoint is IPEndPoint ipEndPoint)
            {
                Assert.Equal(ipEndPoint.Address.ToString(), activity.Tags.FirstOrDefault(t => t.Key == SemanticConventions.AttributeNetPeerIp).Value);
                Assert.Equal(ipEndPoint.Port.ToString(), activity.Tags.FirstOrDefault(t => t.Key == SemanticConventions.AttributeNetPeerPort).Value);
            }
            else if (endPoint is DnsEndPoint dnsEndPoint)
            {
                Assert.Equal(dnsEndPoint.Host, activity.Tags.FirstOrDefault(t => t.Key == SemanticConventions.AttributeNetPeerName).Value);
                Assert.Equal(dnsEndPoint.Port.ToString(), activity.Tags.FirstOrDefault(t => t.Key == SemanticConventions.AttributeNetPeerPort).Value);
            }
            else
            {
                Assert.Equal(endPoint.ToString(), activity.Tags.FirstOrDefault(t => t.Key == SemanticConventions.AttributePeerService).Value);
            }
        }
        public static string GetGrpcStatusCodeFromActivity(Activity activity)
        {
            var status = Status.Unknown;

            var grpcStatusCodeTag = activity.Tags.FirstOrDefault(tag => tag.Key == GrpcStatusCodeTagName).Value;

            if (int.TryParse(grpcStatusCodeTag, out var statusCode))
            {
                status = SpanHelper.ResolveSpanStatusForGrpcStatusCode(statusCode);
            }

            return(SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
        }
        private static void AddResponseTags(HttpWebResponse response, Activity activity)
        {
            activity.SetCustomProperty("HttpWebRequest.Response", response);

            if (activity.IsAllDataRequested)
            {
                activity.AddTag(SpanAttributeConstants.HttpStatusCodeKey, HttpTagHelper.GetStatusCodeTagValueFromHttpStatusCode(response.StatusCode));

                Status status = SpanHelper.ResolveSpanStatusForHttpStatusCode((int)response.StatusCode);

                activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
                activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, response.StatusDescription);
            }
        }
        public override void OnStopActivity(Activity activity, object payload)
        {
            const string EventNameSuffix = ".OnStopActivity";

            if (activity.IsAllDataRequested)
            {
                var context = HttpContext.Current;
                if (context == null)
                {
                    InstrumentationEventSource.Log.NullPayload(nameof(HttpInListener) + EventNameSuffix);
                    return;
                }

                var response = context.Response;
                activity.AddTag(SpanAttributeConstants.HttpStatusCodeKey, response.StatusCode.ToString());
                Status status = SpanHelper.ResolveSpanStatusForHttpStatusCode((int)response.StatusCode);
                activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
                activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, response.StatusDescription);

                var routeData = context.Request.RequestContext.RouteData;

                string template = null;
                if (routeData.Values.TryGetValue("MS_SubRoutes", out object msSubRoutes))
                {
                    // WebAPI attribute routing flows here. Use reflection to not take a dependency on microsoft.aspnet.webapi.core\[version]\lib\[framework]\System.Web.Http.

                    if (msSubRoutes is Array attributeRouting && attributeRouting.Length == 1)
                    {
                        var subRouteData = attributeRouting.GetValue(0);

                        var route = this.routeFetcher.Fetch(subRouteData);
                        template = this.routeTemplateFetcher.Fetch(route) as string;
                    }
                }
                else if (routeData.Route is Route route)
                {
                    // MVC + WebAPI traditional routing & MVC attribute routing flow here.

                    template = route.Url;
                }

                if (!string.IsNullOrEmpty(template))
                {
                    // Override the name that was previously set to the path part of URL.
                    activity.DisplayName = template;
                    activity.AddTag(SpanAttributeConstants.HttpRouteKey, template);
                }
            }
        }
        private void OnEndExecute(EventWrittenEventArgs eventData)
        {
            /*
             * Expected payload:
             *  [0] -> ObjectId
             *  [1] -> CompositeState bitmask (0b001 -> successFlag, 0b010 -> isSqlExceptionFlag , 0b100 -> synchronousFlag)
             *  [2] -> SqlExceptionNumber
             */

            if ((eventData?.Payload?.Count ?? 0) < 3)
            {
                InstrumentationEventSource.Log.InvalidPayload(nameof(SqlEventSourceListener), nameof(this.OnEndExecute));
                return;
            }

            var activity = Activity.Current;

            if (activity?.Source != SqlClientActivitySource)
            {
                return;
            }

            try
            {
                if (activity.IsAllDataRequested)
                {
                    int compositeState = (int)eventData.Payload[1];
                    if ((compositeState & 0b001) == 0b001)
                    {
                        activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(StatusCanonicalCode.Ok));
                    }
                    else
                    {
                        activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(StatusCanonicalCode.Unknown));
                        if ((compositeState & 0b010) == 0b010)
                        {
                            activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, $"SqlExceptionNumber {eventData.Payload[2]} thrown.");
                        }
                        else
                        {
                            activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, $"Unknown Sql failure.");
                        }
                    }
        public override void OnStopActivity(Activity activity, object payload)
        {
            if (activity.IsAllDataRequested)
            {
                if (!(this.stopContextFetcher.Fetch(payload) is HttpContext context))
                {
                    InstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnStopActivity));
                    return;
                }

                var response = context.Response;
                activity.AddTag(SpanAttributeConstants.HttpStatusCodeKey, response.StatusCode.ToString());

                Status status = SpanHelper.ResolveSpanStatusForHttpStatusCode((int)response.StatusCode);
                activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));

                var statusDescription = response.HttpContext.Features.Get <IHttpResponseFeature>().ReasonPhrase;
                if (!string.IsNullOrEmpty(statusDescription))
                {
                    activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, statusDescription);
                }
            }

            if (activity.OperationName.Equals(ActivityNameByHttpInListener))
            {
                // If instrumentation started a new Activity, it must
                // be stopped here.
                activity.Stop();

                // After the activity.Stop() code, Activity.Current becomes null.
                // If Asp.Net Core uses Activity.Current?.Stop() - it'll not stop the activity
                // it created.
                // Currently Asp.Net core does not use Activity.Current, instead it stores a
                // reference to its activity, and calls .Stop on it.

                // TODO: Should we still restore Activity.Current here?
                // If yes, then we need to store the asp.net core activity inside
                // the one created by the instrumentation.
                // And retrieve it here, and set it to Current.
            }

            this.activitySource.Stop(activity);
        }
        public override void OnStopActivity(Activity activity, object payload)
        {
            const string EventNameSuffix = ".OnStopActivity";

            if (activity.IsAllDataRequested)
            {
                if (!(this.stopContextFetcher.Fetch(payload) is HttpContext context))
                {
                    InstrumentationEventSource.Log.NullPayload(nameof(HttpInListener) + EventNameSuffix);
                    return;
                }

                var response = context.Response;
                activity.AddTag(SpanAttributeConstants.HttpStatusCodeKey, response.StatusCode.ToString());

                Status status = SpanHelper.ResolveSpanStatusForHttpStatusCode((int)response.StatusCode);
                activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
                activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, response.HttpContext.Features.Get <IHttpResponseFeature>().ReasonPhrase);
            }
        }
        public static Activity ProfilerCommandToActivity(Activity parentActivity, IProfiledCommand command)
        {
            var name = command.Command; // Example: SET;

            if (string.IsNullOrEmpty(name))
            {
                name = StackExchangeRedisCallsInstrumentation.ActivityName;
            }

            var activity = StackExchangeRedisCallsInstrumentation.ActivitySource.StartActivity(
                name,
                ActivityKind.Client,
                parentActivity?.Context ?? default,
                startTime: command.CommandCreated);

            if (activity == null)
            {
                return(null);
            }

            if (activity.IsAllDataRequested == true)
            {
                // see https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md

                // Timing example:
                // command.CommandCreated; //2019-01-10 22:18:28Z

                // command.CreationToEnqueued;      // 00:00:32.4571995
                // command.EnqueuedToSending;       // 00:00:00.0352838
                // command.SentToResponse;          // 00:00:00.0060586
                // command.ResponseToCompletion;    // 00:00:00.0002601

                // Total:
                // command.ElapsedTime;             // 00:00:32.4988020

                activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(StatusCanonicalCode.Ok));
                activity.AddTag(SpanAttributeConstants.DatabaseSystemKey, "redis");
                activity.AddTag(StackExchangeRedisCallsInstrumentation.RedisFlagsKeyName, command.Flags.ToString());

                if (command.Command != null)
                {
                    // Example: "db.statement": SET;
                    activity.AddTag(SpanAttributeConstants.DatabaseStatementKey, command.Command);
                }

                if (command.EndPoint != null)
                {
                    if (command.EndPoint is IPEndPoint ipEndPoint)
                    {
                        activity.AddTag(SpanAttributeConstants.NetPeerIp, ipEndPoint.Address.ToString());
                        activity.AddTag(SpanAttributeConstants.NetPeerPort, ipEndPoint.Port.ToString());
                    }
                    else if (command.EndPoint is DnsEndPoint dnsEndPoint)
                    {
                        activity.AddTag(SpanAttributeConstants.NetPeerName, dnsEndPoint.Host);
                        activity.AddTag(SpanAttributeConstants.NetPeerPort, dnsEndPoint.Port.ToString());
                    }
                    else
                    {
                        activity.AddTag(SpanAttributeConstants.PeerServiceKey, command.EndPoint.ToString());
                    }
                }

                activity.AddTag(StackExchangeRedisCallsInstrumentation.RedisDatabaseIndexKeyName, command.Db.ToString());

                // TODO: deal with the re-transmission
                // command.RetransmissionOf;
                // command.RetransmissionReason;

                var enqueued = command.CommandCreated.Add(command.CreationToEnqueued);
                var send     = enqueued.Add(command.EnqueuedToSending);
                var response = send.Add(command.SentToResponse);

                activity.AddEvent(new ActivityEvent("Enqueued", enqueued));
                activity.AddEvent(new ActivityEvent("Sent", send));
                activity.AddEvent(new ActivityEvent("ResponseReceived", response));

                activity.SetEndTime(command.CommandCreated + command.ElapsedTime);
            }

            activity.Stop();

            return(activity);
        }
        private static void AddExceptionTags(Exception exception, Activity activity)
        {
            activity.SetCustomProperty("HttpWebRequest.Exception", exception);

            if (!activity.IsAllDataRequested)
            {
                return;
            }

            Status status;

            if (exception is WebException wexc)
            {
                if (wexc.Response is HttpWebResponse response)
                {
                    activity.AddTag(SpanAttributeConstants.HttpStatusCodeKey, HttpTagHelper.GetStatusCodeTagValueFromHttpStatusCode(response.StatusCode));

                    status = SpanHelper.ResolveSpanStatusForHttpStatusCode((int)response.StatusCode).WithDescription(response.StatusDescription);
                }
                else
                {
                    switch (wexc.Status)
                    {
                    case WebExceptionStatus.Timeout:
                        status = Status.DeadlineExceeded;
                        break;

                    case WebExceptionStatus.NameResolutionFailure:
                        status = Status.InvalidArgument.WithDescription(exception.Message);
                        break;

                    case WebExceptionStatus.SendFailure:
                    case WebExceptionStatus.ConnectFailure:
                    case WebExceptionStatus.SecureChannelFailure:
                    case WebExceptionStatus.TrustFailure:
                        status = Status.FailedPrecondition.WithDescription(exception.Message);
                        break;

                    case WebExceptionStatus.ServerProtocolViolation:
                        status = Status.Unimplemented.WithDescription(exception.Message);
                        break;

                    case WebExceptionStatus.RequestCanceled:
                        status = Status.Cancelled;
                        break;

                    case WebExceptionStatus.MessageLengthLimitExceeded:
                        status = Status.ResourceExhausted.WithDescription(exception.Message);
                        break;

                    default:
                        status = Status.Unknown.WithDescription(exception.Message);
                        break;
                    }
                }
            }
            else
            {
                status = Status.Unknown.WithDescription(exception.Message);
            }

            activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
            activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, status.Description);
        }
        public override void OnStopActivity(Activity activity, object payload)
        {
            Activity activityToEnrich = activity;

            if (!(this.options.TextFormat is TraceContextFormatActivity))
            {
                // If using custom context propagator, then the activity here
                // could be either the one from Asp.Net, or the one
                // this instrumentation created in Start.
                // This is because Asp.Net, under certain circumstances, restores Activity.Current
                // to its own activity.
                if (activity.OperationName.Equals("Microsoft.AspNet.HttpReqIn.Start"))
                {
                    // This block is hit if Asp.Net did restore Current to its own activity,
                    // then we need to retrieve the one created by HttpInListener
                    // and populate tags to it.
                    activityToEnrich = (Activity)activity.GetCustomProperty("ActivityByHttpInListener");
                }
            }

            if (activityToEnrich.IsAllDataRequested)
            {
                var context = HttpContext.Current;
                if (context == null)
                {
                    InstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnStopActivity));
                    return;
                }

                var response = context.Response;
                activityToEnrich.AddTag(SpanAttributeConstants.HttpStatusCodeKey, response.StatusCode.ToString());
                Status status = SpanHelper.ResolveSpanStatusForHttpStatusCode((int)response.StatusCode);
                activityToEnrich.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
                activityToEnrich.AddTag(SpanAttributeConstants.StatusDescriptionKey, response.StatusDescription);

                var routeData = context.Request.RequestContext.RouteData;

                string template = null;
                if (routeData.Values.TryGetValue("MS_SubRoutes", out object msSubRoutes))
                {
                    // WebAPI attribute routing flows here. Use reflection to not take a dependency on microsoft.aspnet.webapi.core\[version]\lib\[framework]\System.Web.Http.

                    if (msSubRoutes is Array attributeRouting && attributeRouting.Length == 1)
                    {
                        var subRouteData = attributeRouting.GetValue(0);

                        var route = this.routeFetcher.Fetch(subRouteData);
                        template = this.routeTemplateFetcher.Fetch(route) as string;
                    }
                }
                else if (routeData.Route is Route route)
                {
                    // MVC + WebAPI traditional routing & MVC attribute routing flow here.
                    template = route.Url;
                }

                if (!string.IsNullOrEmpty(template))
                {
                    // Override the name that was previously set to the path part of URL.
                    activityToEnrich.DisplayName = template;
                    activityToEnrich.AddTag(SpanAttributeConstants.HttpRouteKey, template);
                }
            }

            if (!(this.options.TextFormat is TraceContextFormatActivity))
            {
                if (activity.OperationName.Equals(ActivityNameByHttpInListener))
                {
                    // If instrumentation started a new Activity, it must
                    // be stopped here.
                    activity.Stop();

                    // Restore the original activity as Current.
                    var activityByAspNet = (Activity)activity.GetCustomProperty("ActivityByAspNet");
                    Activity.Current = activityByAspNet;
                }
                else if (activity.OperationName.Equals("Microsoft.AspNet.HttpReqIn.Start"))
                {
                    // This block is hit if Asp.Net did restore Current to its own activity,
                    // then we need to retrieve the one created by HttpInListener
                    // and stop it.
                    var activityByHttpInListener = (Activity)activity.GetCustomProperty("ActivityByHttpInListener");
                    activityByHttpInListener.Stop();

                    // Restore current back to the one created by Asp.Net
                    Activity.Current = activity;
                }
            }

            this.activitySource.Stop(activityToEnrich);
        }
        public async Task SuccessfulTemplateControllerCallGeneratesASpan(
            string urlPath,
            string userAgent,
            int statusCode,
            string reasonPhrase)
        {
            var processor = new Mock <ActivityProcessor>();

            // Arrange
            using (var client = this.factory
                                .WithWebHostBuilder(builder =>
                                                    builder.ConfigureTestServices((IServiceCollection services) =>
            {
                services.AddSingleton <CallbackMiddleware.CallbackMiddlewareImpl>(new TestCallbackMiddlewareImpl(statusCode, reasonPhrase));
                services.AddOpenTelemetryTracing((builder) => builder.AddAspNetCoreInstrumentation()
                                                 .AddProcessor(processor.Object));
            }))
                                .CreateClient())
            {
                try
                {
                    if (!string.IsNullOrEmpty(userAgent))
                    {
                        client.DefaultRequestHeaders.Add("User-Agent", userAgent);
                    }

                    // Act
                    var response = await client.GetAsync(urlPath);
                }
                catch (Exception)
                {
                    // ignore errors
                }

                for (var i = 0; i < 10; i++)
                {
                    if (processor.Invocations.Count == 2)
                    {
                        break;
                    }

                    // We need to let End callback execute as it is executed AFTER response was returned.
                    // In unit tests environment there may be a lot of parallel unit tests executed, so
                    // giving some breezing room for the End callback to complete
                    await Task.Delay(TimeSpan.FromSeconds(1));
                }
            }

            Assert.Equal(2, processor.Invocations.Count); // begin and end was called
            var activity = (Activity)processor.Invocations[1].Arguments[0];

            Assert.Equal(ActivityKind.Server, activity.Kind);
            Assert.Equal("localhost", activity.GetTagValue(SemanticConventions.AttributeHttpHost));
            Assert.Equal("GET", activity.GetTagValue(SemanticConventions.AttributeHttpMethod));
            Assert.Equal(urlPath, activity.GetTagValue(SpanAttributeConstants.HttpPathKey));
            Assert.Equal($"http://localhost{urlPath}", activity.GetTagValue(SemanticConventions.AttributeHttpUrl));
            Assert.Equal(statusCode, activity.GetTagValue(SemanticConventions.AttributeHttpStatusCode));

            Status status = SpanHelper.ResolveSpanStatusForHttpStatusCode(statusCode);

            Assert.Equal(SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode), activity.GetTagValue(SpanAttributeConstants.StatusCodeKey));
            this.ValidateTagValue(activity, SpanAttributeConstants.StatusDescriptionKey, reasonPhrase);
            this.ValidateTagValue(activity, SemanticConventions.AttributeHttpUserAgent, userAgent);
        }
        internal static ZipkinSpan ToZipkinSpan(this SpanData otelSpan, ZipkinEndpoint defaultLocalEndpoint, bool useShortTraceIds = false)
        {
            var context        = otelSpan.Context;
            var startTimestamp = ToEpochMicroseconds(otelSpan.StartTimestamp);
            var endTimestamp   = ToEpochMicroseconds(otelSpan.EndTimestamp);

            string parentId = null;

            if (otelSpan.ParentSpanId != default)
            {
                parentId = EncodeSpanId(otelSpan.ParentSpanId);
            }

            var attributeEnumerationState = new AttributeEnumerationState
            {
                Tags = PooledList <KeyValuePair <string, string> > .Create(),
            };

            DictionaryEnumerator <string, object, AttributeEnumerationState> .AllocationFreeForEach(otelSpan.Attributes, ref attributeEnumerationState, ProcessAttributesRef);

            DictionaryEnumerator <string, object, AttributeEnumerationState> .AllocationFreeForEach(otelSpan.LibraryResource.Attributes, ref attributeEnumerationState, ProcessLibraryResourcesRef);

            var localEndpoint = defaultLocalEndpoint;

            var serviceName = attributeEnumerationState.ServiceName;

            // override default service name
            if (!string.IsNullOrWhiteSpace(serviceName))
            {
                if (!string.IsNullOrWhiteSpace(attributeEnumerationState.ServiceNamespace))
                {
                    serviceName = attributeEnumerationState.ServiceNamespace + "." + serviceName;
                }

                if (!LocalEndpointCache.TryGetValue(serviceName, out localEndpoint))
                {
                    localEndpoint = defaultLocalEndpoint.Clone(serviceName);
                    LocalEndpointCache.TryAdd(serviceName, localEndpoint);
                }
            }

            ZipkinEndpoint remoteEndpoint = null;

            if ((otelSpan.Kind == SpanKind.Client || otelSpan.Kind == SpanKind.Producer) && attributeEnumerationState.RemoteEndpointServiceName != null)
            {
                remoteEndpoint = RemoteEndpointCache.GetOrAdd(attributeEnumerationState.RemoteEndpointServiceName, ZipkinEndpoint.Create);
            }

            var status = otelSpan.Status;

            if (status.IsValid)
            {
                PooledList <KeyValuePair <string, string> > .Add(ref attributeEnumerationState.Tags, new KeyValuePair <string, string>(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode)));

                if (status.Description != null)
                {
                    PooledList <KeyValuePair <string, string> > .Add(ref attributeEnumerationState.Tags, new KeyValuePair <string, string>(SpanAttributeConstants.StatusDescriptionKey, status.Description));
                }
            }

            var annotations = PooledList <ZipkinAnnotation> .Create();

            ListEnumerator <Event, PooledList <ZipkinAnnotation> > .AllocationFreeForEach(otelSpan.Events, ref annotations, ProcessEventsRef);

            return(new ZipkinSpan(
                       EncodeTraceId(context.TraceId, useShortTraceIds),
                       parentId,
                       EncodeSpanId(context.SpanId),
                       ToSpanKind(otelSpan),
                       otelSpan.Name,
                       ToEpochMicroseconds(otelSpan.StartTimestamp),
                       duration: endTimestamp - startTimestamp,
                       localEndpoint,
                       remoteEndpoint,
                       annotations,
                       attributeEnumerationState.Tags,
                       null,
                       null));
        }
        public override void OnCustom(string name, Activity activity, object payload)
        {
            switch (name)
            {
            case SqlDataBeforeExecuteCommand:
            case SqlMicrosoftBeforeExecuteCommand:
            {
                var command = this.commandFetcher.Fetch(payload);

                if (command == null)
                {
                    InstrumentationEventSource.Log.NullPayload($"{nameof(SqlClientDiagnosticListener)}-{name}");
                    return;
                }

                var connection = this.connectionFetcher.Fetch(command);
                var database   = this.databaseFetcher.Fetch(connection);

                // TODO: Avoid the reflection hack once .NET ships new Activity with Kind settable.
                activity.GetType().GetProperty("Kind").SetValue(activity, ActivityKind.Client);
                activity.DisplayName = (string)database;

                this.activitySource.Start(activity);

                if (activity.IsAllDataRequested)
                {
                    var dataSource  = this.dataSourceFetcher.Fetch(connection);
                    var commandText = this.commandTextFetcher.Fetch(command);

                    activity.AddTag(SpanAttributeConstants.ComponentKey, "sql");
                    activity.AddTag(SpanAttributeConstants.DatabaseTypeKey, "sql");
                    activity.AddTag(SpanAttributeConstants.PeerServiceKey, (string)dataSource);
                    activity.AddTag(SpanAttributeConstants.DatabaseInstanceKey, (string)database);

                    if (this.commandTypeFetcher.Fetch(command) is CommandType commandType)
                    {
                        activity.AddTag(DatabaseStatementTypeSpanAttributeKey, commandType.ToString());

                        switch (commandType)
                        {
                        case CommandType.StoredProcedure:
                            if (this.options.CaptureStoredProcedureCommandName)
                            {
                                activity.AddTag(SpanAttributeConstants.DatabaseStatementKey, (string)commandText);
                            }

                            break;

                        case CommandType.Text:
                            if (this.options.CaptureTextCommandContent)
                            {
                                activity.AddTag(SpanAttributeConstants.DatabaseStatementKey, (string)commandText);
                            }

                            break;
                        }
                    }
                }
            }

            break;

            case SqlDataAfterExecuteCommand:
            case SqlMicrosoftAfterExecuteCommand:
            {
                this.activitySource.Stop(activity);
            }

            break;

            case SqlDataWriteCommandError:
            case SqlMicrosoftWriteCommandError:
            {
                if (activity.IsAllDataRequested)
                {
                    if (this.exceptionFetcher.Fetch(payload) is Exception exception)
                    {
                        Status status = Status.Unknown;
                        activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
                        activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, exception.Message);
                    }
                    else
                    {
                        InstrumentationEventSource.Log.NullPayload($"{nameof(SqlClientDiagnosticListener)}-{name}");
                    }
                }

                this.activitySource.Stop(activity);
            }

            break;
            }
        }
        public static JaegerSpan ToJaegerSpan(this SpanData span)
        {
            var jaegerTags = new TagState
            {
                Tags = PooledList <JaegerTag> .Create(),
            };

            DictionaryEnumerator <string, object, TagState> .AllocationFreeForEach(
                span.Attributes,
                ref jaegerTags,
                ProcessAttributeRef);

            string peerServiceName = null;

            if ((span.Kind == SpanKind.Client || span.Kind == SpanKind.Producer) && jaegerTags.PeerService != null)
            {
                // Send peer.service for remote calls.
                peerServiceName = jaegerTags.PeerService;

                // If priority = 0 that means peer.service was already included in tags.
                if (jaegerTags.PeerServicePriority > 0)
                {
                    PooledList <JaegerTag> .Add(ref jaegerTags.Tags, new JaegerTag(SpanAttributeConstants.PeerServiceKey, JaegerTagType.STRING, vStr : peerServiceName));
                }
            }

            // The Span.Kind must translate into a tag.
            // See https://opentracing.io/specification/conventions/
            if (span.Kind.HasValue)
            {
                string spanKind = null;

                if (span.Kind.Value == SpanKind.Server)
                {
                    spanKind = "server";
                }
                else if (span.Kind.Value == SpanKind.Client)
                {
                    spanKind = "client";
                }
                else if (span.Kind.Value == SpanKind.Consumer)
                {
                    spanKind = "consumer";
                }
                else if (span.Kind.Value == SpanKind.Producer)
                {
                    spanKind = "producer";
                }

                if (spanKind != null)
                {
                    PooledList <JaegerTag> .Add(ref jaegerTags.Tags, new JaegerTag("span.kind", JaegerTagType.STRING, vStr : spanKind));
                }
            }

            DictionaryEnumerator <string, object, TagState> .AllocationFreeForEach(
                span.LibraryResource?.Attributes ?? Array.Empty <KeyValuePair <string, object> >(),
                ref jaegerTags,
                ProcessLibraryAttributeRef);

            var status = span.Status;

            if (status.IsValid)
            {
                PooledList <JaegerTag> .Add(ref jaegerTags.Tags, new JaegerTag(SpanAttributeConstants.StatusCodeKey, JaegerTagType.STRING, vStr : SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode)));

                if (status.Description != null)
                {
                    PooledList <JaegerTag> .Add(ref jaegerTags.Tags, new JaegerTag(SpanAttributeConstants.StatusDescriptionKey, JaegerTagType.STRING, vStr : status.Description));
                }
            }

            var traceId      = Int128.Empty;
            var spanId       = Int128.Empty;
            var parentSpanId = Int128.Empty;

            if (span.Context.IsValid)
            {
                traceId      = new Int128(span.Context.TraceId);
                spanId       = new Int128(span.Context.SpanId);
                parentSpanId = new Int128(span.ParentSpanId);
            }

            return(new JaegerSpan(
                       peerServiceName: peerServiceName,
                       traceIdLow: traceId.Low,
                       traceIdHigh: traceId.High,
                       spanId: spanId.Low,
                       parentSpanId: parentSpanId.Low,
                       operationName: span.Name,
                       flags: (span.Context.TraceFlags & ActivityTraceFlags.Recorded) > 0 ? 0x1 : 0,
                       startTime: ToEpochMicroseconds(span.StartTimestamp),
                       duration: ToEpochMicroseconds(span.EndTimestamp) - ToEpochMicroseconds(span.StartTimestamp),
                       references: span.Links.ToJaegerSpanRefs(),
                       tags: jaegerTags.Tags,
                       logs: span.Events.ToJaegerLogs()));
        }