This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push: new d4792ba9226 Otel (#11909) d4792ba9226 is described below commit d4792ba9226cb31de91393b10fad5d6f914600d0 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Sun Nov 5 16:22:48 2023 +0100 Otel (#11909) CAMEL-20083: camel-opentelemtry - Make it easier to configure for camel-main --- .../main/camel-main-configuration-metadata.json | 4 + .../camel/observation/AttributeProcessor.java | 12 +-- .../observation/MicrometerObservationTracer.java | 2 +- .../SetCorrelationContextProcessor.java | 3 - .../OpenTelemetryTracerConfigurer.java | 94 +++++++++++++++++ ....apache.camel.opentelemetry.OpenTelemetryTracer | 2 + .../services/org/apache/camel/opentelemetry-tracer | 2 + .../src/main/docs/opentelemetry.adoc | 21 +++- .../camel/opentelemetry/OpenTelemetryTracer.java | 40 +++++--- .../main/java/org/apache/camel/tracing/Tracer.java | 4 +- .../org/apache/camel/spi/CamelTracingService.java | 27 +++++ .../OtelConfigurationPropertiesConfigurer.java | 61 +++++++++++ .../camel-main-configuration-metadata.json | 4 + ...g.apache.camel.main.OtelConfigurationProperties | 2 + core/camel-main/src/main/docs/main.adoc | 12 +++ .../org/apache/camel/main/BaseMainSupport.java | 65 +++++++++++- .../camel/main/MainConfigurationProperties.java | 22 ++++ .../camel/main/OtelConfigurationProperties.java | 112 +++++++++++++++++++++ .../maven/packaging/PrepareCamelMainMojo.java | 5 + 19 files changed, 457 insertions(+), 37 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json index 3235de257b0..3a89ff173c1 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json @@ -10,6 +10,7 @@ { "name": "camel.vault.aws", "description": "Camel AWS Vault configurations", "sourceType": "org.apache.camel.vault.AwsVaultConfiguration" }, { "name": "camel.vault.gcp", "description": "Camel GCP Vault configurations", "sourceType": "org.apache.camel.vault.GcpVaultConfiguration" }, { "name": "camel.vault.azure", "description": "Camel Azure Key Vault configurations", "sourceType": "org.apache.camel.vault.AzureVaultConfiguration" }, + { "name": "camel.opentelemetry", "description": "Camel OpenTelemtry configurations", "sourceType": "org.apache.camel.main.OtelConfigurationProperties" }, { "name": "camel.faulttolerance", "description": "Fault Tolerance EIP Circuit Breaker configurations", "sourceType": "org.apache.camel.main.FaultToleranceConfigurationProperties" }, { "name": "camel.resilience4j", "description": "Resilience4j EIP Circuit Breaker configurations", "sourceType": "org.apache.camel.main.Resilience4jConfigurationProperties" }, { "name": "camel.lra", "description": "Camel Saga EIP (Long Running Actions) configurations", "sourceType": "org.apache.camel.main.LraConfigurationProperties" } @@ -177,6 +178,9 @@ { "name": "camel.lra.coordinatorUrl", "description": "The URL for the LRA coordinator service that orchestrates the transactions", "sourceType": "org.apache.camel.main.LraConfigurationProperties", "type": "string", "javaType": "java.lang.String" }, { "name": "camel.lra.localParticipantContextPath", "description": "The context-path for the local participant. Is default \/lra-participant", "sourceType": "org.apache.camel.main.LraConfigurationProperties", "type": "string", "javaType": "java.lang.String", "defaultValue": "\/lra-participant" }, { "name": "camel.lra.localParticipantUrl", "description": "The URL for the local participant", "sourceType": "org.apache.camel.main.LraConfigurationProperties", "type": "string", "javaType": "java.lang.String" }, + { "name": "camel.opentelemetry.encoding", "description": "Sets whether the header keys need to be encoded (connector specific) or not. The value is a boolean. Dashes need for instances to be encoded for JMS property keys.", "sourceType": "org.apache.camel.main.OtelConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, + { "name": "camel.opentelemetry.excludePatterns", "description": "Adds an exclude pattern that will disable tracing for Camel messages that matches the pattern. Multiple patterns can be separated by comma.", "sourceType": "org.apache.camel.main.OtelConfigurationProperties", "type": "string", "javaType": "java.lang.String" }, + { "name": "camel.opentelemetry.instrumentationName", "description": "A name uniquely identifying the instrumentation scope, such as the instrumentation library, package, or fully qualified class name. Must not be null.", "sourceType": "org.apache.camel.main.OtelConfigurationProperties", "type": "string", "javaType": "java.lang.String", "defaultValue": "camel" }, { "name": "camel.resilience4j.automaticTransitionFromOpenToHalfOpenEnabled", "description": "Enables automatic transition from OPEN to HALF_OPEN state once the waitDurationInOpenState has passed.", "sourceType": "org.apache.camel.main.Resilience4jConfigurationProperties", "type": "boolean", "javaType": "java.lang.Boolean", "defaultValue": "false" }, { "name": "camel.resilience4j.bulkheadEnabled", "description": "Whether bulkhead is enabled or not on the circuit breaker.", "sourceType": "org.apache.camel.main.Resilience4jConfigurationProperties", "type": "boolean", "javaType": "java.lang.Boolean", "defaultValue": false }, { "name": "camel.resilience4j.bulkheadMaxConcurrentCalls", "description": "Configures the max amount of concurrent calls the bulkhead will support.", "sourceType": "org.apache.camel.main.Resilience4jConfigurationProperties", "type": "integer", "javaType": "java.lang.Integer" }, diff --git a/components/camel-observation/src/main/java/org/apache/camel/observation/AttributeProcessor.java b/components/camel-observation/src/main/java/org/apache/camel/observation/AttributeProcessor.java index c551908aa4a..cdca8f440ca 100644 --- a/components/camel-observation/src/main/java/org/apache/camel/observation/AttributeProcessor.java +++ b/components/camel-observation/src/main/java/org/apache/camel/observation/AttributeProcessor.java @@ -30,7 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * A processor which adds a attribute on the active {@link Observation} with an {@link org.apache.camel.Expression} + * A processor which adds an attribute on the active {@link Observation} with an {@link org.apache.camel.Expression} */ public class AttributeProcessor extends AsyncProcessorSupport implements Traceable, IdAware, RouteIdAware { @@ -99,16 +99,6 @@ public class AttributeProcessor extends AsyncProcessorSupport implements Traceab return expression; } - @Override - protected void doStart() throws Exception { - // noop - } - - @Override - protected void doStop() throws Exception { - // noop - } - @Override public String toString() { return id; diff --git a/components/camel-observation/src/main/java/org/apache/camel/observation/MicrometerObservationTracer.java b/components/camel-observation/src/main/java/org/apache/camel/observation/MicrometerObservationTracer.java index aa5baec9d4f..74f90818965 100644 --- a/components/camel-observation/src/main/java/org/apache/camel/observation/MicrometerObservationTracer.java +++ b/components/camel-observation/src/main/java/org/apache/camel/observation/MicrometerObservationTracer.java @@ -160,7 +160,7 @@ public class MicrometerObservationTracer extends org.apache.camel.tracing.Tracer @Override protected void initContextPropagators() { - + // noop } private static Observation getParentObservation(SpanAdapter parentObservation) { diff --git a/components/camel-observation/src/main/java/org/apache/camel/observation/SetCorrelationContextProcessor.java b/components/camel-observation/src/main/java/org/apache/camel/observation/SetCorrelationContextProcessor.java index 80d329ffafa..9d2d86cde84 100644 --- a/components/camel-observation/src/main/java/org/apache/camel/observation/SetCorrelationContextProcessor.java +++ b/components/camel-observation/src/main/java/org/apache/camel/observation/SetCorrelationContextProcessor.java @@ -28,9 +28,6 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** - * @author rvargasp - */ public class SetCorrelationContextProcessor extends AsyncProcessorSupport implements Traceable, IdAware, RouteIdAware { private static final Logger LOG = LoggerFactory.getLogger(SetCorrelationContextProcessor.class); diff --git a/components/camel-opentelemetry/src/generated/java/org/apache/camel/opentelemetry/OpenTelemetryTracerConfigurer.java b/components/camel-opentelemetry/src/generated/java/org/apache/camel/opentelemetry/OpenTelemetryTracerConfigurer.java new file mode 100644 index 00000000000..0cd14dce133 --- /dev/null +++ b/components/camel-opentelemetry/src/generated/java/org/apache/camel/opentelemetry/OpenTelemetryTracerConfigurer.java @@ -0,0 +1,94 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.opentelemetry; + +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.spi.ExtendedPropertyConfigurerGetter; +import org.apache.camel.spi.PropertyConfigurerGetter; +import org.apache.camel.spi.ConfigurerStrategy; +import org.apache.camel.spi.GeneratedPropertyConfigurer; +import org.apache.camel.util.CaseInsensitiveMap; +import org.apache.camel.opentelemetry.OpenTelemetryTracer; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@SuppressWarnings("unchecked") +public class OpenTelemetryTracerConfigurer extends org.apache.camel.support.component.PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter { + + @Override + public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { + org.apache.camel.opentelemetry.OpenTelemetryTracer target = (org.apache.camel.opentelemetry.OpenTelemetryTracer) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "camelcontext": + case "CamelContext": target.setCamelContext(property(camelContext, org.apache.camel.CamelContext.class, value)); return true; + case "contextpropagators": + case "ContextPropagators": target.setContextPropagators(property(camelContext, io.opentelemetry.context.propagation.ContextPropagators.class, value)); return true; + case "encoding": + case "Encoding": target.setEncoding(property(camelContext, boolean.class, value)); return true; + case "excludepatterns": + case "ExcludePatterns": target.setExcludePatterns(property(camelContext, java.util.Set.class, value)); return true; + case "instrumentationname": + case "InstrumentationName": target.setInstrumentationName(property(camelContext, java.lang.String.class, value)); return true; + case "tracer": + case "Tracer": target.setTracer(property(camelContext, io.opentelemetry.api.trace.Tracer.class, value)); return true; + case "tracingstrategy": + case "TracingStrategy": target.setTracingStrategy(property(camelContext, org.apache.camel.spi.InterceptStrategy.class, value)); return true; + default: return false; + } + } + + @Override + public Class<?> getOptionType(String name, boolean ignoreCase) { + switch (ignoreCase ? name.toLowerCase() : name) { + case "camelcontext": + case "CamelContext": return org.apache.camel.CamelContext.class; + case "contextpropagators": + case "ContextPropagators": return io.opentelemetry.context.propagation.ContextPropagators.class; + case "encoding": + case "Encoding": return boolean.class; + case "excludepatterns": + case "ExcludePatterns": return java.util.Set.class; + case "instrumentationname": + case "InstrumentationName": return java.lang.String.class; + case "tracer": + case "Tracer": return io.opentelemetry.api.trace.Tracer.class; + case "tracingstrategy": + case "TracingStrategy": return org.apache.camel.spi.InterceptStrategy.class; + default: return null; + } + } + + @Override + public Object getOptionValue(Object obj, String name, boolean ignoreCase) { + org.apache.camel.opentelemetry.OpenTelemetryTracer target = (org.apache.camel.opentelemetry.OpenTelemetryTracer) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "camelcontext": + case "CamelContext": return target.getCamelContext(); + case "contextpropagators": + case "ContextPropagators": return target.getContextPropagators(); + case "encoding": + case "Encoding": return target.isEncoding(); + case "excludepatterns": + case "ExcludePatterns": return target.getExcludePatterns(); + case "instrumentationname": + case "InstrumentationName": return target.getInstrumentationName(); + case "tracer": + case "Tracer": return target.getTracer(); + case "tracingstrategy": + case "TracingStrategy": return target.getTracingStrategy(); + default: return null; + } + } + + @Override + public Object getCollectionValueType(Object target, String name, boolean ignoreCase) { + switch (ignoreCase ? name.toLowerCase() : name) { + case "excludepatterns": + case "ExcludePatterns": return java.lang.String.class; + default: return null; + } + } +} + diff --git a/components/camel-opentelemetry/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.opentelemetry.OpenTelemetryTracer b/components/camel-opentelemetry/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.opentelemetry.OpenTelemetryTracer new file mode 100644 index 00000000000..bfc37ca1862 --- /dev/null +++ b/components/camel-opentelemetry/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.opentelemetry.OpenTelemetryTracer @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.opentelemetry.OpenTelemetryTracerConfigurer diff --git a/components/camel-opentelemetry/src/generated/resources/META-INF/services/org/apache/camel/opentelemetry-tracer b/components/camel-opentelemetry/src/generated/resources/META-INF/services/org/apache/camel/opentelemetry-tracer new file mode 100644 index 00000000000..2482cc2f2ed --- /dev/null +++ b/components/camel-opentelemetry/src/generated/resources/META-INF/services/org/apache/camel/opentelemetry-tracer @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.opentelemetry.OpenTelemetryTracer diff --git a/components/camel-opentelemetry/src/main/docs/opentelemetry.adoc b/components/camel-opentelemetry/src/main/docs/opentelemetry.adoc index c61cdd1bde8..c8768469cd2 100644 --- a/components/camel-opentelemetry/src/main/docs/opentelemetry.adoc +++ b/components/camel-opentelemetry/src/main/docs/opentelemetry.adoc @@ -24,7 +24,7 @@ The configuration properties for the OpenTelemetry tracer are: [width="100%",cols="10%,10%,80%",options="header",] |======================================================================= |Option |Default |Description - +|instrumentationName| camel | A name uniquely identifying the instrumentation scope, such as the instrumentation library, package, or fully qualified class name. Must not be null. |excludePatterns | | Sets exclude pattern(s) that will disable tracing for Camel messages that matches the pattern. The content is a Set<String> where the key is a pattern. The pattern uses the rules from Intercept. @@ -34,7 +34,7 @@ Dashes need for instances to be encoded for JMS property keys. |======================================================================= -=== Configuration +== Using Camel OpenTelemetry Include the `camel-opentelemetry` component in your POM, along with any specific dependencies associated with the chosen OpenTelemetry compliant Tracer. @@ -52,6 +52,23 @@ otelTracer.setTracer(...); otelTracer.init(camelContext); -------------------------------------------------------------------------------------------------- +You would still need OpenTelemetry to instrument your code, which can be done via a Java agent (see further below). + +=== Using with standalone Camel + +If you use `camel-main` as standalone Camel, then you can enable and use OpenTelemetry without Java code. + +Add `camel-opentelemetry` component in your POM, and configure in `application.properties`: + +[source,properties] +---- +camel.opentelemetry.enabled = true +# you can confiure the other options +# camel.opentelemetry.instrumentationName = myApp +---- + +You would still need OpenTelemetry to instrument your code, which can be done via a Java agent (see further below). + == Spring Boot If you are using Spring Boot then you can add diff --git a/components/camel-opentelemetry/src/main/java/org/apache/camel/opentelemetry/OpenTelemetryTracer.java b/components/camel-opentelemetry/src/main/java/org/apache/camel/opentelemetry/OpenTelemetryTracer.java index 2d8fe70a54a..6858d20e819 100644 --- a/components/camel-opentelemetry/src/main/java/org/apache/camel/opentelemetry/OpenTelemetryTracer.java +++ b/components/camel-opentelemetry/src/main/java/org/apache/camel/opentelemetry/OpenTelemetryTracer.java @@ -16,8 +16,6 @@ */ package org.apache.camel.opentelemetry; -import java.util.Set; - import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.baggage.Baggage; import io.opentelemetry.api.trace.Span; @@ -27,18 +25,28 @@ import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.ContextPropagators; import org.apache.camel.Exchange; +import org.apache.camel.api.management.ManagedAttribute; import org.apache.camel.api.management.ManagedResource; import org.apache.camel.opentelemetry.propagators.OpenTelemetryGetter; import org.apache.camel.opentelemetry.propagators.OpenTelemetrySetter; +import org.apache.camel.spi.Configurer; +import org.apache.camel.spi.annotations.JdkService; +import org.apache.camel.support.CamelContextHelper; import org.apache.camel.tracing.ExtractAdapter; import org.apache.camel.tracing.InjectAdapter; import org.apache.camel.tracing.SpanAdapter; import org.apache.camel.tracing.SpanDecorator; import org.apache.camel.tracing.decorators.AbstractInternalSpanDecorator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +@JdkService("opentelemetry-tracer") +@Configurer @ManagedResource(description = "OpenTelemetryTracer") public class OpenTelemetryTracer extends org.apache.camel.tracing.Tracer { + private static final Logger LOG = LoggerFactory.getLogger(OpenTelemetryTracer.class); + private Tracer tracer; private String instrumentationName = "camel"; private ContextPropagators contextPropagators; @@ -51,6 +59,15 @@ public class OpenTelemetryTracer extends org.apache.camel.tracing.Tracer { this.tracer = tracer; } + @ManagedAttribute + public String getInstrumentationName() { + return instrumentationName; + } + + /** + * A name uniquely identifying the instrumentation scope, such as the instrumentation library, package, or fully + * qualified class name. Must not be null. + */ public void setInstrumentationName(String instrumentationName) { this.instrumentationName = instrumentationName; } @@ -81,12 +98,8 @@ public class OpenTelemetryTracer extends org.apache.camel.tracing.Tracer { @Override protected void initTracer() { if (tracer == null) { - Set<Tracer> tracers = getCamelContext().getRegistry().findByType(Tracer.class); - if (tracers.size() == 1) { - tracer = tracers.iterator().next(); - } + tracer = CamelContextHelper.findSingleByType(getCamelContext(), Tracer.class); } - if (tracer == null) { // GlobalOpenTelemetry.get() is always NotNull, falls back to OpenTelemetry.noop() tracer = GlobalOpenTelemetry.get().getTracer(instrumentationName); @@ -96,13 +109,8 @@ public class OpenTelemetryTracer extends org.apache.camel.tracing.Tracer { @Override protected void initContextPropagators() { if (contextPropagators == null) { - Set<ContextPropagators> contextPropagatorsSet - = getCamelContext().getRegistry().findByType(ContextPropagators.class); - if (contextPropagatorsSet.size() == 1) { - contextPropagators = contextPropagatorsSet.iterator().next(); - } + contextPropagators = CamelContextHelper.findSingleByType(getCamelContext(), ContextPropagators.class); } - if (contextPropagators == null) { // GlobalOpenTelemetry.get() is always NotNull, falls back to OpenTelemetry.noop() contextPropagators = GlobalOpenTelemetry.get().getPropagators(); @@ -169,4 +177,10 @@ public class OpenTelemetryTracer extends org.apache.camel.tracing.Tracer { GlobalOpenTelemetry.get().getPropagators().getTextMapPropagator().inject(ctx, adapter, new OpenTelemetrySetter()); } + @Override + protected void doStart() throws Exception { + super.doStart(); + + LOG.info("OpenTelemetryTracer enabled using instrumentation-name: {}", instrumentationName); + } } diff --git a/components/camel-tracing/src/main/java/org/apache/camel/tracing/Tracer.java b/components/camel-tracing/src/main/java/org/apache/camel/tracing/Tracer.java index 1b9c5297112..3afbeae663f 100644 --- a/components/camel-tracing/src/main/java/org/apache/camel/tracing/Tracer.java +++ b/components/camel-tracing/src/main/java/org/apache/camel/tracing/Tracer.java @@ -23,7 +23,6 @@ import java.util.ServiceLoader; import java.util.Set; import org.apache.camel.CamelContext; -import org.apache.camel.CamelContextAware; import org.apache.camel.Component; import org.apache.camel.Endpoint; import org.apache.camel.Exchange; @@ -33,6 +32,7 @@ import org.apache.camel.RuntimeCamelException; import org.apache.camel.StaticService; import org.apache.camel.spi.CamelEvent; import org.apache.camel.spi.CamelLogger; +import org.apache.camel.spi.CamelTracingService; import org.apache.camel.spi.InterceptStrategy; import org.apache.camel.spi.LogListener; import org.apache.camel.spi.RoutePolicy; @@ -48,7 +48,7 @@ import org.apache.camel.util.StringHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public abstract class Tracer extends ServiceSupport implements RoutePolicyFactory, StaticService, CamelContextAware { +public abstract class Tracer extends ServiceSupport implements CamelTracingService, RoutePolicyFactory, StaticService { protected static final Map<String, SpanDecorator> DECORATORS = new HashMap<>(); static final AutoCloseable NOOP_CLOSEABLE = () -> { }; diff --git a/core/camel-api/src/generated/java/org/apache/camel/spi/CamelTracingService.java b/core/camel-api/src/generated/java/org/apache/camel/spi/CamelTracingService.java new file mode 100644 index 00000000000..e71ebc047fe --- /dev/null +++ b/core/camel-api/src/generated/java/org/apache/camel/spi/CamelTracingService.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.spi; + +import org.apache.camel.CamelContextAware; +import org.apache.camel.Service; + +/** + * A Camel tracing service is a factory for telemetry tracers. + */ +public interface CamelTracingService extends Service, CamelContextAware { + +} diff --git a/core/camel-main/src/generated/java/org/apache/camel/main/OtelConfigurationPropertiesConfigurer.java b/core/camel-main/src/generated/java/org/apache/camel/main/OtelConfigurationPropertiesConfigurer.java new file mode 100644 index 00000000000..2cd0234490c --- /dev/null +++ b/core/camel-main/src/generated/java/org/apache/camel/main/OtelConfigurationPropertiesConfigurer.java @@ -0,0 +1,61 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.main; + +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.spi.ExtendedPropertyConfigurerGetter; +import org.apache.camel.spi.PropertyConfigurerGetter; +import org.apache.camel.spi.ConfigurerStrategy; +import org.apache.camel.spi.GeneratedPropertyConfigurer; +import org.apache.camel.util.CaseInsensitiveMap; +import org.apache.camel.main.OtelConfigurationProperties; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@SuppressWarnings("unchecked") +public class OtelConfigurationPropertiesConfigurer extends org.apache.camel.support.component.PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter { + + @Override + public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { + org.apache.camel.main.OtelConfigurationProperties target = (org.apache.camel.main.OtelConfigurationProperties) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "encoding": + case "Encoding": target.setEncoding(property(camelContext, boolean.class, value)); return true; + case "excludepatterns": + case "ExcludePatterns": target.setExcludePatterns(property(camelContext, java.lang.String.class, value)); return true; + case "instrumentationname": + case "InstrumentationName": target.setInstrumentationName(property(camelContext, java.lang.String.class, value)); return true; + default: return false; + } + } + + @Override + public Class<?> getOptionType(String name, boolean ignoreCase) { + switch (ignoreCase ? name.toLowerCase() : name) { + case "encoding": + case "Encoding": return boolean.class; + case "excludepatterns": + case "ExcludePatterns": return java.lang.String.class; + case "instrumentationname": + case "InstrumentationName": return java.lang.String.class; + default: return null; + } + } + + @Override + public Object getOptionValue(Object obj, String name, boolean ignoreCase) { + org.apache.camel.main.OtelConfigurationProperties target = (org.apache.camel.main.OtelConfigurationProperties) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "encoding": + case "Encoding": return target.isEncoding(); + case "excludepatterns": + case "ExcludePatterns": return target.getExcludePatterns(); + case "instrumentationname": + case "InstrumentationName": return target.getInstrumentationName(); + default: return null; + } + } +} + diff --git a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json index 3235de257b0..3a89ff173c1 100644 --- a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json +++ b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json @@ -10,6 +10,7 @@ { "name": "camel.vault.aws", "description": "Camel AWS Vault configurations", "sourceType": "org.apache.camel.vault.AwsVaultConfiguration" }, { "name": "camel.vault.gcp", "description": "Camel GCP Vault configurations", "sourceType": "org.apache.camel.vault.GcpVaultConfiguration" }, { "name": "camel.vault.azure", "description": "Camel Azure Key Vault configurations", "sourceType": "org.apache.camel.vault.AzureVaultConfiguration" }, + { "name": "camel.opentelemetry", "description": "Camel OpenTelemtry configurations", "sourceType": "org.apache.camel.main.OtelConfigurationProperties" }, { "name": "camel.faulttolerance", "description": "Fault Tolerance EIP Circuit Breaker configurations", "sourceType": "org.apache.camel.main.FaultToleranceConfigurationProperties" }, { "name": "camel.resilience4j", "description": "Resilience4j EIP Circuit Breaker configurations", "sourceType": "org.apache.camel.main.Resilience4jConfigurationProperties" }, { "name": "camel.lra", "description": "Camel Saga EIP (Long Running Actions) configurations", "sourceType": "org.apache.camel.main.LraConfigurationProperties" } @@ -177,6 +178,9 @@ { "name": "camel.lra.coordinatorUrl", "description": "The URL for the LRA coordinator service that orchestrates the transactions", "sourceType": "org.apache.camel.main.LraConfigurationProperties", "type": "string", "javaType": "java.lang.String" }, { "name": "camel.lra.localParticipantContextPath", "description": "The context-path for the local participant. Is default \/lra-participant", "sourceType": "org.apache.camel.main.LraConfigurationProperties", "type": "string", "javaType": "java.lang.String", "defaultValue": "\/lra-participant" }, { "name": "camel.lra.localParticipantUrl", "description": "The URL for the local participant", "sourceType": "org.apache.camel.main.LraConfigurationProperties", "type": "string", "javaType": "java.lang.String" }, + { "name": "camel.opentelemetry.encoding", "description": "Sets whether the header keys need to be encoded (connector specific) or not. The value is a boolean. Dashes need for instances to be encoded for JMS property keys.", "sourceType": "org.apache.camel.main.OtelConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, + { "name": "camel.opentelemetry.excludePatterns", "description": "Adds an exclude pattern that will disable tracing for Camel messages that matches the pattern. Multiple patterns can be separated by comma.", "sourceType": "org.apache.camel.main.OtelConfigurationProperties", "type": "string", "javaType": "java.lang.String" }, + { "name": "camel.opentelemetry.instrumentationName", "description": "A name uniquely identifying the instrumentation scope, such as the instrumentation library, package, or fully qualified class name. Must not be null.", "sourceType": "org.apache.camel.main.OtelConfigurationProperties", "type": "string", "javaType": "java.lang.String", "defaultValue": "camel" }, { "name": "camel.resilience4j.automaticTransitionFromOpenToHalfOpenEnabled", "description": "Enables automatic transition from OPEN to HALF_OPEN state once the waitDurationInOpenState has passed.", "sourceType": "org.apache.camel.main.Resilience4jConfigurationProperties", "type": "boolean", "javaType": "java.lang.Boolean", "defaultValue": "false" }, { "name": "camel.resilience4j.bulkheadEnabled", "description": "Whether bulkhead is enabled or not on the circuit breaker.", "sourceType": "org.apache.camel.main.Resilience4jConfigurationProperties", "type": "boolean", "javaType": "java.lang.Boolean", "defaultValue": false }, { "name": "camel.resilience4j.bulkheadMaxConcurrentCalls", "description": "Configures the max amount of concurrent calls the bulkhead will support.", "sourceType": "org.apache.camel.main.Resilience4jConfigurationProperties", "type": "integer", "javaType": "java.lang.Integer" }, diff --git a/core/camel-main/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.main.OtelConfigurationProperties b/core/camel-main/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.main.OtelConfigurationProperties new file mode 100644 index 00000000000..91c564399fe --- /dev/null +++ b/core/camel-main/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.main.OtelConfigurationProperties @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.main.OtelConfigurationPropertiesConfigurer diff --git a/core/camel-main/src/main/docs/main.adoc b/core/camel-main/src/main/docs/main.adoc index bd0442cc8b0..3dee56918ae 100644 --- a/core/camel-main/src/main/docs/main.adoc +++ b/core/camel-main/src/main/docs/main.adoc @@ -332,6 +332,18 @@ The camel.vault.azure supports 11 options, which are listed below. |=== +=== Camel OpenTelemtry configurations +The camel.opentelemetry supports 3 options, which are listed below. + +[width="100%",cols="2,5,^1,2",options="header"] +|=== +| Name | Description | Default | Type +| *camel.opentelemetry.encoding* | Sets whether the header keys need to be encoded (connector specific) or not. The value is a boolean. Dashes need for instances to be encoded for JMS property keys. | false | boolean +| *camel.opentelemetry.exclude{zwsp}Patterns* | Adds an exclude pattern that will disable tracing for Camel messages that matches the pattern. Multiple patterns can be separated by comma. | | String +| *{zwsp}camel.opentelemetry.instrumentation{zwsp}Name* | A name uniquely identifying the instrumentation scope, such as the instrumentation library, package, or fully qualified class name. Must not be null. | camel | String +|=== + + === Fault Tolerance EIP Circuit Breaker configurations The camel.faulttolerance supports 13 options, which are listed below. diff --git a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java index e3fc6bcdead..bbaff3f289e 100644 --- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java +++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java @@ -57,6 +57,7 @@ import org.apache.camel.spi.AutowiredLifecycleStrategy; import org.apache.camel.spi.BacklogDebugger; import org.apache.camel.spi.CamelBeanPostProcessor; import org.apache.camel.spi.CamelEvent; +import org.apache.camel.spi.CamelTracingService; import org.apache.camel.spi.ContextReloadStrategy; import org.apache.camel.spi.DataFormat; import org.apache.camel.spi.Language; @@ -950,6 +951,7 @@ public abstract class BaseMainSupport extends BaseService { OrderedLocationProperties threadPoolProperties = new OrderedLocationProperties(); OrderedLocationProperties healthProperties = new OrderedLocationProperties(); OrderedLocationProperties lraProperties = new OrderedLocationProperties(); + OrderedLocationProperties otelProperties = new OrderedLocationProperties(); OrderedLocationProperties routeTemplateProperties = new OrderedLocationProperties(); OrderedLocationProperties beansProperties = new OrderedLocationProperties(); OrderedLocationProperties devConsoleProperties = new OrderedLocationProperties(); @@ -1007,6 +1009,12 @@ public abstract class BaseMainSupport extends BaseService { String option = key.substring(10); validateOptionAndValue(key, option, value); lraProperties.put(loc, optionKey(option), value); + } else if (key.startsWith("camel.opentelemetry.")) { + // grab the value + String value = prop.getProperty(key); + String option = key.substring(20); + validateOptionAndValue(key, option, value); + otelProperties.put(loc, optionKey(option), value); } else if (key.startsWith("camel.routeTemplate")) { // grab the value String value = prop.getProperty(key); @@ -1114,6 +1122,11 @@ public abstract class BaseMainSupport extends BaseService { setLraCheckProperties(camelContext, lraProperties, mainConfigurationProperties.isAutoConfigurationFailFast(), autoConfiguredProperties); } + if (!otelProperties.isEmpty() || mainConfigurationProperties.hasOtelConfiguration()) { + LOG.debug("Auto-configuring OpenTelemetry from loaded properties: {}", otelProperties.size()); + setOtelProperties(camelContext, otelProperties, mainConfigurationProperties.isAutoConfigurationFailFast(), + autoConfiguredProperties); + } if (!devConsoleProperties.isEmpty()) { LOG.debug("Auto-configuring Dev Console from loaded properties: {}", devConsoleProperties.size()); setDevConsoleProperties(camelContext, devConsoleProperties, @@ -1198,6 +1211,11 @@ public abstract class BaseMainSupport extends BaseService { LOG.warn("Property not auto-configured: camel.lra.{}={}", k, v); }); } + if (!otelProperties.isEmpty()) { + otelProperties.forEach((k, v) -> { + LOG.warn("Property not auto-configured: camel.opentelemetry.{}={}", k, v); + }); + } if (!httpServerProperties.isEmpty()) { httpServerProperties.forEach((k, v) -> { LOG.warn("Property not auto-configured: camel.server.{}={}", k, v); @@ -1342,15 +1360,39 @@ public abstract class BaseMainSupport extends BaseService { boolean failIfNotSet, OrderedLocationProperties autoConfiguredProperties) throws Exception { + String loc = lraProperties.getLocation("enabled"); Object obj = lraProperties.remove("enabled"); if (ObjectHelper.isNotEmpty(obj)) { - String loc = lraProperties.getLocation("enabled"); autoConfiguredProperties.put(loc, "camel.lra.enabled", obj.toString()); } boolean enabled = obj != null ? CamelContextHelper.parseBoolean(camelContext, obj.toString()) : true; if (enabled) { CamelSagaService css = resolveLraSagaService(camelContext); setPropertiesOnTarget(camelContext, css, lraProperties, "camel.lra.", failIfNotSet, true, autoConfiguredProperties); + // add as service so saga can be active + camelContext.addService(css, true, true); + } + } + + private void setOtelProperties( + CamelContext camelContext, OrderedLocationProperties otelProperties, + boolean failIfNotSet, OrderedLocationProperties autoConfiguredProperties) + throws Exception { + + String loc = otelProperties.getLocation("enabled"); + Object obj = otelProperties.remove("enabled"); + if (ObjectHelper.isNotEmpty(obj)) { + autoConfiguredProperties.put(loc, "camel.opentelemetry.enabled", obj.toString()); + } + boolean enabled = obj != null ? CamelContextHelper.parseBoolean(camelContext, obj.toString()) : true; + if (enabled) { + CamelTracingService otel = resolveOtelService(camelContext); + setPropertiesOnTarget(camelContext, otel, otelProperties, "camel.opentelemetry.", failIfNotSet, true, + autoConfiguredProperties); + if (camelContext.hasService(CamelTracingService.class) == null) { + // add as service so tracing can be active + camelContext.addService(otel, true, true); + } } } @@ -1982,16 +2024,29 @@ public abstract class BaseMainSupport extends BaseService { } private static CamelSagaService resolveLraSagaService(CamelContext camelContext) throws Exception { - // lookup in service registry first - CamelSagaService answer = camelContext.getRegistry().findSingleByType(CamelSagaService.class); + CamelSagaService answer = camelContext.hasService(CamelSagaService.class); + if (answer == null) { + answer = camelContext.getRegistry().findSingleByType(CamelSagaService.class); + } if (answer == null) { answer = camelContext.getCamelContextExtension().getBootstrapFactoryFinder() .newInstance("lra-saga-service", CamelSagaService.class) .orElseThrow(() -> new IllegalArgumentException( "Cannot find LRASagaService on classpath. Add camel-lra to classpath.")); + } + return answer; + } - // add as service so its discover by saga eip - camelContext.addService(answer, true, false); + private static CamelTracingService resolveOtelService(CamelContext camelContext) throws Exception { + CamelTracingService answer = camelContext.hasService(CamelTracingService.class); + if (answer == null) { + answer = camelContext.getRegistry().findSingleByType(CamelTracingService.class); + } + if (answer == null) { + answer = camelContext.getCamelContextExtension().getBootstrapFactoryFinder() + .newInstance("opentelemetry-tracer", CamelTracingService.class) + .orElseThrow(() -> new IllegalArgumentException( + "Cannot find OpenTelemetryTracer on classpath. Add camel-opentelemetry to classpath.")); } return answer; } diff --git a/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java index ea12f3631bf..79273789d92 100644 --- a/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java +++ b/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java @@ -53,6 +53,7 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties< // extended configuration private HealthConfigurationProperties healthConfigurationProperties; private LraConfigurationProperties lraConfigurationProperties; + private OtelConfigurationProperties otelConfigurationProperties; private ThreadPoolConfigurationProperties threadPoolProperties; private Resilience4jConfigurationProperties resilience4jConfigurationProperties; private FaultToleranceConfigurationProperties faultToleranceConfigurationProperties; @@ -72,6 +73,10 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties< lraConfigurationProperties.close(); lraConfigurationProperties = null; } + if (otelConfigurationProperties != null) { + otelConfigurationProperties.close(); + otelConfigurationProperties = null; + } if (threadPoolProperties != null) { threadPoolProperties.close(); threadPoolProperties = null; @@ -151,6 +156,23 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties< return lraConfigurationProperties != null; } + /** + * To configure OpenTelemetry. + */ + public OtelConfigurationProperties otel() { + if (otelConfigurationProperties == null) { + otelConfigurationProperties = new OtelConfigurationProperties(this); + } + return otelConfigurationProperties; + } + + /** + * Whether there has been any OpenTelemetry configuration specified + */ + public boolean hasOtelConfiguration() { + return otelConfigurationProperties != null; + } + /** * To configure embedded HTTP server (for standalone applications; not Spring Boot or Quarkus) */ diff --git a/core/camel-main/src/main/java/org/apache/camel/main/OtelConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/OtelConfigurationProperties.java new file mode 100644 index 00000000000..a41c23a1694 --- /dev/null +++ b/core/camel-main/src/main/java/org/apache/camel/main/OtelConfigurationProperties.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.main; + +import org.apache.camel.spi.BootstrapCloseable; +import org.apache.camel.spi.Configurer; +import org.apache.camel.spi.Metadata; + +/** + * Global configuration for OpenTelemetry + */ +@Configurer(bootstrap = true) +public class OtelConfigurationProperties implements BootstrapCloseable { + + private MainConfigurationProperties parent; + + @Metadata(defaultValue = "camel", required = true) + private String instrumentationName = "camel"; + private boolean encoding; + private String excludePatterns; + + public OtelConfigurationProperties(MainConfigurationProperties parent) { + this.parent = parent; + } + + public MainConfigurationProperties end() { + return parent; + } + + @Override + public void close() { + parent = null; + } + + public String getInstrumentationName() { + return instrumentationName; + } + + /** + * A name uniquely identifying the instrumentation scope, such as the instrumentation library, package, or fully + * qualified class name. Must not be null. + */ + public void setInstrumentationName(String instrumentationName) { + this.instrumentationName = instrumentationName; + } + + public boolean isEncoding() { + return encoding; + } + + /** + * Sets whether the header keys need to be encoded (connector specific) or not. The value is a boolean. Dashes need + * for instances to be encoded for JMS property keys. + */ + public void setEncoding(boolean encoding) { + this.encoding = encoding; + } + + public String getExcludePatterns() { + return excludePatterns; + } + + /** + * Adds an exclude pattern that will disable tracing for Camel messages that matches the pattern. Multiple patterns + * can be separated by comma. + */ + public void setExcludePatterns(String excludePatterns) { + this.excludePatterns = excludePatterns; + } + + /** + * A name uniquely identifying the instrumentation scope, such as the instrumentation library, package, or fully + * qualified class name. Must not be null. + */ + public OtelConfigurationProperties withInstrumentationName(String instrumentationName) { + this.instrumentationName = instrumentationName; + return this; + } + + /** + * Sets whether the header keys need to be encoded (connector specific) or not. The value is a boolean. Dashes need + * for instances to be encoded for JMS property keys. + */ + public OtelConfigurationProperties withEncoding(boolean encoding) { + this.encoding = encoding; + return this; + } + + /** + * Adds an exclude pattern that will disable tracing for Camel messages that matches the pattern. Multiple patterns + * can be separated by comma. + */ + public OtelConfigurationProperties withExcludePatterns(String excludePatterns) { + this.excludePatterns = excludePatterns; + return this; + } + +} diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelMainMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelMainMojo.java index 3ac979cc46e..e5d80ece234 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelMainMojo.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelMainMojo.java @@ -204,6 +204,8 @@ public class PrepareCamelMainMojo extends AbstractGeneratorMojo { prefix = "camel.health."; } else if (file.getName().contains("Lra")) { prefix = "camel.lra."; + } else if (file.getName().contains("Otel")) { + prefix = "camel.opentelemetry."; } else if (file.getName().contains("HttpServer")) { prefix = "camel.server."; } else if (file.getName().contains("ThreadPoolProfileConfigurationProperties")) { @@ -316,6 +318,9 @@ public class PrepareCamelMainMojo extends AbstractGeneratorMojo { "camel.vault.azure", "Camel Azure Key Vault configurations", "org.apache.camel.vault.AzureVaultConfiguration")); // TODO: add more vault providers here + model.getGroups().add(new MainGroupModel( + "camel.opentelemetry", "Camel OpenTelemtry configurations", + "org.apache.camel.main.OtelConfigurationProperties")); model.getGroups() .add(new MainGroupModel( "camel.faulttolerance", "Fault Tolerance EIP Circuit Breaker configurations",