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);
        }
Example #2
0
        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);
        }
Example #3
0
        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);
        }
Example #4
0
 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));
 }
Example #5
0
 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);
 }
Example #6
0
        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);
        }
Example #7
0
        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);
        }
Example #8
0
 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));
        }