Jonas Beyer created CAMEL-22672:
-----------------------------------

             Summary: [camel-observation] observations and metrics
                 Key: CAMEL-22672
                 URL: https://issues.apache.org/jira/browse/CAMEL-22672
             Project: Camel
          Issue Type: Bug
    Affects Versions: 4.13.0
            Reporter: Jonas Beyer


This is a bit of a revival of CAMEL-22349, which as far as I can tell has not 
been resolved. The idea seems to be that 
[https://github.com/apache/camel-spring-boot/pull/1493/files] would cause 
{{@ConditionalOnMissingBean(MeterObservationHandler.class)}} in 
{{org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration}}
 to fail, which would prevent the creation of metrics ... in the whole context.

This did not work because the Camel {{ObservationAutoConfiguration}} has 
{{@AutoConfigureAfter}} the Spring one. And so Spring tried to create both 
beans leading to CAMEL-22612. The fix renamed the bean, so now both are 
successfully created and with the Spring one also metrics.

I ran into the problem of many metrics on 4.14.0 and my main issue was the 
naming, as {{process2}} or {{to13}} are no sensible identifiers. By naming them 
hierarchically they could be conveniently suppressed via the property 
{{management.metrics.enable.[prefix]: false}} or a configurable {{MeterFilter}} 
instead of disabling the functionality.

Our current workaround is:
{code:java}
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
import org.apache.camel.NamedNode;
import org.apache.camel.observation.MicrometerObservationTracer;
import org.apache.camel.spi.NodeIdFactory;
import org.apache.camel.support.ExtendedExchangeExtension;
import org.apache.commons.lang3.Strings;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Delegate;
import lombok.val;

public class CamelNodeIdFactory implements NodeIdFactory {

  private static final String PREFIX = "camel-node";
  private static final Map<String, AtomicInteger> NODE_COUNTERS = new 
ConcurrentHashMap<>();

  @Override
  public String createId(NamedNode definition) {
    val id = getId(definition);
    val counter = NODE_COUNTERS.computeIfAbsent(id, k -> new 
AtomicInteger()).incrementAndGet();
    return id + (counter > 1 ? counter : "");
  }

  private String getId(NamedNode definition) {
    if (definition == null) {
      return PREFIX;
    } else if (definition.getId() != null) {
      return Strings.CS.prependIfMissing(definition.getId(), PREFIX + ".");
    } else {
      val parentId = getId(definition.getParent());
      return parentId + "." + definition.getShortName();
    }
  }

  @RequiredArgsConstructor
  public static class SpanDecorator implements 
org.apache.camel.tracing.SpanDecorator {

    @Delegate
    private final org.apache.camel.tracing.SpanDecorator delegate;

    @Override
    public String getOperationName(Exchange exchange, Endpoint endpoint) {
      val exchangeExtension = (ExtendedExchangeExtension) 
exchange.getExchangeExtension();
      val nodeId = exchangeExtension.getHistoryNodeId();
      return nodeId != null ? nodeId : 
Strings.CS.prependIfMissing(exchangeExtension.getFromRouteId(), PREFIX + ".");
    }
  }

  public static class Tracer extends MicrometerObservationTracer {

    @Override
    protected org.apache.camel.tracing.SpanDecorator getSpanDecorator(Endpoint 
endpoint) {
      return new SpanDecorator(super.getSpanDecorator(endpoint));
    }
  }
}{code}
with
{code:java}
  @Bean(initMethod = "", destroyMethod = "") // Camel handles the lifecycle of 
this bean
  public MicrometerObservationTracer micrometerObservationTracer(CamelContext 
camelContext,
      Tracer tracer,
      ObservationRegistry registry) {
    
camelContext.getCamelContextExtension().addContextPlugin(NodeIdFactory.class, 
new CamelNodeIdFactory());
    val micrometerObservationTracer = new CamelNodeIdFactory.Tracer();
    micrometerObservationTracer.setTracer(tracer);
    micrometerObservationTracer.setObservationRegistry(registry);
    micrometerObservationTracer.setTracingStrategy(new 
MicrometerTracingStrategy(micrometerObservationTracer));
    micrometerObservationTracer.init(camelContext);
    return micrometerObservationTracer;
  } {code}
This could of course be cleaner if properly integrated into Camel.

It already enables us to turn off all route-specific metrics by default
{code:java}
management.metrics.enable.camel-node: false{code}
and turn on specific ones
{code:java}
management.metrics.enable.camel-node.[node-id]: true {code}
 

This is not perfect as the node id is part of the metric name instead of a tag, 
but it is clear which part of the route is measured 
({{{}camel_node_heartbeat_log_seconds{}}} instead of {{{}log[n]{}}}) - and it 
doesn't interfere with the rest of the Spring Boot context.

cc [~squakez] 



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to