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); } } } }
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); } } }
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()); }
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())); }