private bool MatchesIgnoreMessageQueues(string name) { if (name != null && _realAgent != null) { var matcher = WildcardMatcher.AnyMatch(_realAgent.ConfigStore.CurrentSnapshot.IgnoreMessageQueues, name); if (matcher != null) { Logger.Debug()?.Log( "Not tracing message from {QueueName} because it matched IgnoreMessageQueues pattern {Matcher}", name, matcher.GetMatcher()); return(true); } } return(false); }
public void TestMatchAnyStartsWith() { var matcher1 = WildcardMatcher.ValueOf("foo*"); var matcher2 = WildcardMatcher.ValueOf("bar*"); WildcardMatcher.AnyMatch(new List <WildcardMatcher> { matcher1, matcher2 }, "foo").Should().Be(matcher1); WildcardMatcher.AnyMatch(new List <WildcardMatcher> { matcher1, matcher2 }, "bar").Should().Be(matcher2); WildcardMatcher.AnyMatch(new List <WildcardMatcher> { matcher1, matcher2 }, "baz").Should().BeNull(); WildcardMatcher.AnyMatch(new List <WildcardMatcher> { matcher1, matcher2 }, "fo", "o").Should().Be(matcher1); WildcardMatcher.AnyMatch(new List <WildcardMatcher> { matcher1, matcher2 }, "ba", "r").Should().Be(matcher2); WildcardMatcher.AnyMatch(new List <WildcardMatcher> { matcher1, matcher2 }, "ba", "z").Should().BeNull(); }
internal static ISpan CreateProducerSpan(IApmAgent agent, ITopicPartition topicPartition, bool isTombstone, bool finishOnClose) { ISpan span = null; try { // no current transaction, don't create a span var currentTransaction = agent.Tracer.CurrentTransaction; if (currentTransaction is null) { return(span); } var topic = topicPartition?.Topic; var matcher = WildcardMatcher.AnyMatch(currentTransaction.Configuration.IgnoreMessageQueues, topic); if (matcher != null) { agent.Logger.Trace() ?.Log( "Not tracing message from {Queue} because it matched IgnoreMessageQueues pattern {Matcher}", topic, matcher.GetMatcher()); return(span); } var spanName = string.IsNullOrEmpty(topic) ? "Kafka SEND" : $"Kafka SEND to {topic}"; span = agent.GetCurrentExecutionSegment().StartSpan( spanName, ApiConstants.TypeMessaging, Subtype, isExitSpan: true); if (!string.IsNullOrEmpty(topic)) { span.Context.Message = new Message { Queue = new Queue { Name = topic } } } ; if (topicPartition?.Partition is not null && !topicPartition.Partition.IsSpecial) { span.SetLabel("partition", topicPartition.Partition.ToString()); } if (isTombstone) { span.SetLabel("tombstone", "true"); } } catch (Exception ex) { agent.Logger.Error()?.LogException(ex, "Error creating or populating kafka span."); } return(span); }
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); }
/// <summary> /// OnMethodEnd callback /// </summary> /// <typeparam name="TTarget">Type of the target</typeparam> /// <typeparam name="TResult">Type of the BasicGetResult</typeparam> /// <param name="instance">Instance value, aka `this` of the instrumented method.</param> /// <param name="basicGetResult">BasicGetResult instance</param> /// <param name="exception">Exception instance in case the original code threw an exception.</param> /// <param name="state">Calltarget state value</param> /// <returns>A default CallTargetReturn to satisfy the CallTarget contract</returns> public static CallTargetReturn <TResult> OnMethodEnd <TTarget, TResult>(TTarget instance, TResult basicGetResult, Exception exception, CallTargetState state) where TResult : IBasicGetResult, IDuckType { var queue = (string)state.State; var startTime = state.StartTime; var agent = Agent.Instance; var transaction = agent.Tracer.CurrentTransaction; if (transaction is null) { return(new CallTargetReturn <TResult>(basicGetResult)); } var matcher = WildcardMatcher.AnyMatch(transaction.Configuration.IgnoreMessageQueues, queue); if (matcher != null) { agent.Logger.Trace() ?.Log( "Not tracing message from {Queue} because it matched IgnoreMessageQueues pattern {Matcher}", queue, matcher.GetMatcher()); return(new CallTargetReturn <TResult>(basicGetResult)); } // check if there is an actual instance of the duck-typed type. RabbitMQ client can return null when the server // answers that there are no messages available var instanceNotNull = basicGetResult.Instance != null; if (instanceNotNull) { var normalizedExchange = RabbitMqIntegration.NormalizeExchangeName(basicGetResult.Exchange); matcher = WildcardMatcher.AnyMatch(transaction.Configuration.IgnoreMessageQueues, normalizedExchange); if (matcher != null) { agent.Logger.Trace() ?.Log( "Not tracing message from {Queue} because it matched IgnoreMessageQueues pattern {Matcher}", normalizedExchange, matcher.GetMatcher()); return(new CallTargetReturn <TResult>(basicGetResult)); } } var normalizedQueue = RabbitMqIntegration.NormalizeQueueName(queue); var span = agent.Tracer.CurrentExecutionSegment().StartSpan( $"{RabbitMqIntegration.Name} POLL from {normalizedQueue}", ApiConstants.TypeMessaging, RabbitMqIntegration.Subtype); if (startTime.HasValue && span is Span realSpan) { realSpan.Timestamp = TimeUtils.ToTimestamp(startTime.Value); } span.Context.Message = new Message { Queue = new Queue { Name = queue } }; if (instanceNotNull) { span.SetLabel("message_size", basicGetResult.Body?.Length ?? 0); if (!string.IsNullOrEmpty(basicGetResult.RoutingKey)) { span.Context.Message.RoutingKey = basicGetResult.RoutingKey; } } span.EndCapturingException(exception); return(new CallTargetReturn <TResult>(basicGetResult)); }