Exemplo n.º 1
0
        /// <summary>
        /// Try to inject the trace context into the Kafka message headers
        /// </summary>
        /// <param name="agent">The agent</param>
        /// <param name="segment">The outgoing distributed tracing data to propagate</param>
        /// <param name="message">The duck-typed Kafka Message object</param>
        /// <typeparam name="TTopicPartitionMarker">The TopicPartition type (used  optimisation purposes)</typeparam>
        /// <typeparam name="TMessage">The type of the duck-type proxy</typeparam>
        internal static void TryInjectHeaders <TTopicPartitionMarker, TMessage>(IApmAgent agent, IExecutionSegment segment, TMessage message)
            where TMessage : IMessage
        {
            if (!HeadersInjectionEnabled)
            {
                return;
            }

            try
            {
                message.Headers ??= CachedMessageHeadersHelper <TTopicPartitionMarker> .CreateHeaders();

                var adapter = new KafkaHeadersCollection(message.Headers, agent.Logger);
                var distributedTracingData = segment.OutgoingDistributedTracingData;
                adapter.Set(TraceContext.TraceParentBinaryHeaderName, distributedTracingData.SerializeToString());
                adapter.Set(TraceContext.TraceStateHeaderName, distributedTracingData.TraceState.ToTextHeader());
            }
            catch (Exception ex)
            {
                // don't keep trying if we run into problems
                HeadersInjectionEnabled = false;
                agent.Logger.Warning()?.LogException(ex, "There was a problem injecting headers into the Kafka record. Disabling headers injection");
            }
        }
Exemplo n.º 2
0
        internal static ITransaction CreateConsumerTransaction(
            IApmAgent agent,
            string topic,
            Partition?partition,
            Offset?offset,
            IMessage message)
        {
            ITransaction transaction = null;

            try
            {
                if (agent.Tracer.CurrentTransaction is not null)
                {
                    return(null);
                }

                if (agent is ApmAgent apmAgent)
                {
                    var matcher = WildcardMatcher.AnyMatch(apmAgent.ConfigurationStore.CurrentSnapshot.IgnoreMessageQueues, topic);
                    if (matcher != null)
                    {
                        agent.Logger.Trace()
                        ?.Log(
                            "Not tracing message from {Queue} because it matched IgnoreMessageQueues pattern {Matcher}",
                            topic,
                            matcher.GetMatcher());

                        return(null);
                    }
                }

                DistributedTracingData distributedTracingData = null;
                if (message?.Headers != null)
                {
                    var headers = new KafkaHeadersCollection(message.Headers, agent.Logger);
                    try
                    {
                        var traceParent = string.Join(",", headers.GetValues(TraceContext.TraceParentBinaryHeaderName));
                        var traceState  = headers.GetValues(TraceContext.TraceStateHeaderName).FirstOrDefault();
                        distributedTracingData = TraceContext.TryExtractTracingData(traceParent, traceState);
                    }
                    catch (Exception ex)
                    {
                        agent.Logger.Error()?.LogException(ex, "Error extracting propagated headers from Kafka message");
                    }
                }

                var name = string.IsNullOrEmpty(topic)
                                        ? "Kafka RECEIVE"
                                        : $"Kafka RECEIVE from {topic}";

                transaction = agent.Tracer.StartTransaction(name, ApiConstants.TypeMessaging, distributedTracingData);

                if (partition is not null)
                {
                    transaction.SetLabel("partition", partition.ToString());
                }

                if (offset is not null)
                {
                    transaction.SetLabel("offset", offset.ToString());
                }

                // record only queue topic name and age on context for now. capture body and headers potentially in future
                transaction.Context.Message = new Message {
                    Queue = new Queue {
                        Name = topic
                    }
                };
                if (transaction is Transaction realTransaction && message is not null && message.Timestamp.Type != 0)
                {
                    var consumeTime = TimeUtils.ToDateTime(realTransaction.Timestamp);
                    var produceTime = message.Timestamp.UtcDateTime;

                    var age = Math.Max(0, (consumeTime - produceTime).TotalMilliseconds);
                    if (age > 0 && age < MaxAge)
                    {
                        transaction.Context.Message.Age = new Age {
                            Ms = (long)age
                        }
                    }
                    ;
                }

                if (message is not null && message.Value is null)
                {
                    transaction.SetLabel("tombstone", "true");
                }
            }
            catch (Exception ex)
            {
                agent.Logger.Error()?.LogException(ex, "Error creating or populating transaction.");
            }

            return(transaction);
        }