public static ISpan ProfilerCommandToSpan(ITracer tracer, ISpan parentSpan, IProfiledCommand command) { var name = command.Command; // Example: SET; if (string.IsNullOrEmpty(name)) { name = "name"; } var span = tracer.StartSpan(name, parentSpan, SpanKind.Client, command.CommandCreated); if (span.IsRecordingEvents) { // use https://github.com/opentracing/specification/blob/master/semantic_conventions.md for now // 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 span.Status = Status.Ok; span.SetAttribute("db.type", "redis"); span.SetAttribute("redis.flags", command.Flags.ToString()); if (command.Command != null) { // Example: "db.statement": SET; span.SetAttribute("db.statement", command.Command); } if (command.EndPoint != null) { // Example: "db.instance": Unspecified/localhost:6379[0] span.SetAttribute("db.instance", string.Concat(command.EndPoint, "[", command.Db, "]")); } // 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); span.AddEvent(new Event("Enqueued", enqueued)); span.AddEvent(new Event("Sent", send)); span.AddEvent(new Event("ResponseReceived", response)); span.End(command.CommandCreated.Add(command.ElapsedTime)); } return(span); }
private static void ProcessCommand(IProfiledCommand profiledCommand, IExecutionSegment executionSegment) { var name = GetCommand(profiledCommand); if (profiledCommand.RetransmissionOf != null) { var retransmissionName = GetCommand(profiledCommand.RetransmissionOf); name += $" (Retransmission of {retransmissionName}: {profiledCommand.RetransmissionReason})"; } executionSegment.CaptureSpan(name, ApiConstants.TypeDb, span => { span.Context.Db = new Database { Instance = profiledCommand.Db.ToString(CultureInfo.InvariantCulture), Statement = GetCommandAndKey(profiledCommand) ?? name, Type = ApiConstants.SubTypeRedis }; string address = null; int?port = null; switch (profiledCommand.EndPoint) { case IPEndPoint ipEndPoint: address = ipEndPoint.Address.ToString(); port = ipEndPoint.Port; break; case DnsEndPoint dnsEndPoint: address = dnsEndPoint.Host; port = dnsEndPoint.Port; break; } if (address != null) { span.Context.Destination = new Destination { Address = address, Port = port } } ; // update the timestamp to reflect the point at which the command was created if (span is Span realSpan) { realSpan.Timestamp = TimeUtils.ToTimestamp(profiledCommand.CommandCreated); } span.Duration = profiledCommand.ElapsedTime.TotalMilliseconds; // profiled commands are always successful span.Outcome = Outcome.Success; // TODO: clear the raw stacktrace as it won't be representative of the call stack at // the point at which the call to redis happens, and therefore misleading to include }, ApiConstants.SubTypeRedis, "query", true); }
private static string GetCommandAndKey(IProfiledCommand profiledCommand) { if (profiledCommand.GetType() != _profiledCommandType || MessageFetcher == null) { return(null); } var message = MessageFetcher.Invoke(profiledCommand); return(CommandAndKeyFetcher.Invoke(message) as string); }
private static void AssertProfiledCommandValues(IProfiledCommand command, IConnectionMultiplexer conn, int dbId) { Assert.Equal(dbId, command.Db); Assert.Equal(conn.GetEndPoints()[0], command.EndPoint); Assert.True(command.CreationToEnqueued > TimeSpan.Zero, nameof(command.CreationToEnqueued)); Assert.True(command.EnqueuedToSending > TimeSpan.Zero, nameof(command.EnqueuedToSending)); Assert.True(command.SentToResponse > TimeSpan.Zero, nameof(command.SentToResponse)); Assert.True(command.ResponseToCompletion >= TimeSpan.Zero, nameof(command.ResponseToCompletion)); Assert.True(command.ElapsedTime > TimeSpan.Zero, nameof(command.ElapsedTime)); Assert.True(command.ElapsedTime > command.CreationToEnqueued && command.ElapsedTime > command.EnqueuedToSending && command.ElapsedTime > command.SentToResponse, "Comparisons"); Assert.True(command.RetransmissionOf == null, nameof(command.RetransmissionOf)); Assert.True(command.RetransmissionReason == null, nameof(command.RetransmissionReason)); }
private static void AssertProfiledCommandValues(IProfiledCommand command, ConnectionMultiplexer conn) { Assert.Equal(4, command.Db); Assert.Equal(conn.GetEndPoints()[0], command.EndPoint); Assert.True(command.CreationToEnqueued > TimeSpan.Zero); Assert.True(command.EnqueuedToSending > TimeSpan.Zero); Assert.True(command.SentToResponse > TimeSpan.Zero); Assert.True(command.ResponseToCompletion > TimeSpan.Zero); Assert.True(command.ElapsedTime > TimeSpan.Zero); Assert.True(command.ElapsedTime > command.CreationToEnqueued && command.ElapsedTime > command.EnqueuedToSending && command.ElapsedTime > command.SentToResponse); Assert.True(command.RetransmissionOf == null); Assert.True(command.RetransmissionReason == null); }
public ValueTask Collect(string session, IProfiledCommand command) { if (string.IsNullOrWhiteSpace(session)) { throw new ArgumentException("Value cannot be null or whitespace.", nameof(session)); } _ = command ?? throw new ArgumentNullException(nameof(command)); var commonLabels = new[] { session, command.Command }; RedisElapsedTime .WithLabels(commonLabels) .Observe(command.ElapsedTime.TotalMilliseconds); RedisCreationToEnqueuedTime .WithLabels(commonLabels) .Observe(command.CreationToEnqueued.TotalMilliseconds); RedisEnqueuedToSendingTime .WithLabels(commonLabels) .Observe(command.EnqueuedToSending.TotalMilliseconds); RedisResponseToCompletionTime .WithLabels(commonLabels) .Observe(command.ResponseToCompletion.TotalMilliseconds); RedisSentToReponseTime .WithLabels(commonLabels) .Observe(command.SentToResponse.TotalMilliseconds); return(ValueTasksExtensions.EmptyValueTask); }
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.SetStatus(Status.Ok); activity.SetTag(SemanticConventions.AttributeDbSystem, "redis"); activity.SetTag(StackExchangeRedisCallsInstrumentation.RedisFlagsKeyName, command.Flags.ToString()); if (command.Command != null) { // Example: "db.statement": SET; activity.SetTag(SemanticConventions.AttributeDbStatement, command.Command); } if (command.EndPoint != null) { if (command.EndPoint is IPEndPoint ipEndPoint) { activity.SetTag(SemanticConventions.AttributeNetPeerIp, ipEndPoint.Address.ToString()); activity.SetTag(SemanticConventions.AttributeNetPeerPort, ipEndPoint.Port.ToString()); } else if (command.EndPoint is DnsEndPoint dnsEndPoint) { activity.SetTag(SemanticConventions.AttributeNetPeerName, dnsEndPoint.Host); activity.SetTag(SemanticConventions.AttributeNetPeerPort, dnsEndPoint.Port.ToString()); } else { activity.SetTag(SemanticConventions.AttributePeerService, command.EndPoint.ToString()); } } activity.SetTag(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 string GetCommand(IProfiledCommand profiledCommand) => !string.IsNullOrEmpty(profiledCommand.Command) ? profiledCommand.Command : "UNKNOWN";
internal static ISpanData ProfiledCommandToSpanData(ISpanContext context, string name, ISpanId parentSpanId, IProfiledCommand command) { var hasRemoteParent = false; // use https://github.com/opentracing/specification/blob/master/semantic_conventions.md for now // 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 // TODO: make timestamp with the better precision Timestamp startTimestamp = Timestamp.FromMillis(new DateTimeOffset(command.CommandCreated).ToUnixTimeMilliseconds()); var timestamp = new DateTimeOffset(command.CommandCreated).Add(command.CreationToEnqueued); var events = TimedEvents <IEvent> .Create( new List <ITimedEvent <IEvent> >() { TimedEvent <IEvent> .Create(Timestamp.FromMillis(timestamp.ToUnixTimeMilliseconds()), Event.Create("Enqueued")), TimedEvent <IEvent> .Create(Timestamp.FromMillis((timestamp = timestamp.Add(command.EnqueuedToSending)).ToUnixTimeMilliseconds()), Event.Create("Sent")), TimedEvent <IEvent> .Create(Timestamp.FromMillis((timestamp = timestamp.Add(command.SentToResponse)).ToUnixTimeMilliseconds()), Event.Create("ResponseRecieved")), }, droppedEventsCount : 0); Timestamp endTimestamp = Timestamp.FromMillis(new DateTimeOffset(command.CommandCreated.Add(command.ElapsedTime)).ToUnixTimeMilliseconds()); // TODO: deal with the re-transmission // command.RetransmissionOf; // command.RetransmissionReason; var attributesMap = new Dictionary <string, IAttributeValue>() { // TODO: pre-allocate constant attribute and reuse { "db.type", AttributeValue.StringAttributeValue("redis") }, // Example: "redis.flags": None, DemandMaster { "redis.flags", AttributeValue.StringAttributeValue(command.Flags.ToString()) }, }; if (command.Command != null) { // Example: "db.statement": SET; attributesMap.Add("db.statement", AttributeValue.StringAttributeValue(command.Command)); } if (command.EndPoint != null) { // Example: "db.instance": Unspecified/localhost:6379[0] attributesMap.Add("db.instance", AttributeValue.StringAttributeValue(command.EndPoint.ToString() + "[" + command.Db + "]")); } var attributes = Attributes.Create(attributesMap, 0); ILinks links = null; int? childSpanCount = 0; // TODO: this is strange that IProfiledCommand doesn't give the result Status status = Status.Ok; SpanKind kind = SpanKind.Client; return(SpanData.Create(context, parentSpanId, hasRemoteParent, name, startTimestamp, attributes, events, links, childSpanCount, status, kind, endTimestamp)); }
internal static SpanData ProfiledCommandToSpanData(SpanContext context, string name, ActivitySpanId parentSpanId, IProfiledCommand command) { // use https://github.com/opentracing/specification/blob/master/semantic_conventions.md for now // 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 // TODO: make timestamp with the better precision var startTimestamp = command.CommandCreated; var enqueued = command.CommandCreated.Add(command.CreationToEnqueued); var send = enqueued.Add(command.EnqueuedToSending); var response = send.Add(command.SentToResponse); var events = TimedEvents <IEvent> .Create( new List <ITimedEvent <IEvent> >() { TimedEvent <IEvent> .Create(enqueued, Event.Create("Enqueued")), TimedEvent <IEvent> .Create(send, Event.Create("Sent")), TimedEvent <IEvent> .Create(response, Event.Create("ResponseReceived")), }, droppedEventsCount : 0); var endTimestamp = command.CommandCreated.Add(command.ElapsedTime); // TODO: deal with the re-transmission // command.RetransmissionOf; // command.RetransmissionReason; // TODO: determine what to do with Resource in this context var resource = Resource.Empty; var attributesMap = new Dictionary <string, object>() { // TODO: pre-allocate constant attribute and reuse { "db.type", "redis" }, // Example: "redis.flags": None, DemandMaster { "redis.flags", command.Flags.ToString() }, }; if (command.Command != null) { // Example: "db.statement": SET; attributesMap.Add("db.statement", command.Command); } if (command.EndPoint != null) { // Example: "db.instance": Unspecified/localhost:6379[0] attributesMap.Add("db.instance", command.EndPoint.ToString() + "[" + command.Db + "]"); } var attributes = Attributes.Create(attributesMap, 0); ILinks links = null; int? childSpanCount = 0; // TODO: this is strange that IProfiledCommand doesn't give the result var status = Status.Ok; var kind = SpanKind.Client; return(SpanData.Create(context, parentSpanId, resource, name, startTimestamp, attributes, events, links, childSpanCount, status, kind, endTimestamp)); }