This is an automated email from the ASF dual-hosted git repository. pcongiusti 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 eaa18a72439 feat(components): camel-telemetry-simple first implementation eaa18a72439 is described below commit eaa18a724393b6ed0e0dc75b938b8557eddb86a3 Author: Pasquale Congiusti <pasquale.congiu...@gmail.com> AuthorDate: Thu Feb 6 10:25:25 2025 +0100 feat(components): camel-telemetry-simple first implementation --- bom/camel-bom/pom.xml | 5 + catalog/camel-allcomponents/pom.xml | 5 + .../main/camel-main-configuration-metadata.json | 5 + .../org/apache/camel/catalog/others.properties | 1 + .../apache/camel/catalog/others/telemetry-dev.json | 15 ++ components/camel-telemetry-dev/pom.xml | 96 ++++++++++++ .../telemetrydev/TelemetryDevTracerConfigurer.java | 75 ++++++++++ ...rg.apache.camel.telemetrydev.TelemetryDevTracer | 2 + .../services/org/apache/camel/other.properties | 7 + .../services/org/apache/camel/telemetry-dev-tracer | 2 + .../src/generated/resources/telemetry-dev.json | 15 ++ .../src/main/docs/telemetry-dev.adoc | 74 ++++++++++ .../apache/camel/telemetrydev/DevSpanAdapter.java | 120 +++++++++++++++ .../org/apache/camel/telemetrydev/DevTrace.java | 66 +++++++++ .../apache/camel/telemetrydev/DevTraceFormat.java | 156 ++++++++++++++++++++ .../camel/telemetrydev/InMemoryCollector.java | 54 +++++++ .../camel/telemetrydev/TelemetryDevTracer.java | 164 +++++++++++++++++++++ .../apache/camel/telemetrydev}/AsyncCXFTest.java | 56 ++++--- .../camel/telemetrydev}/AsyncDirectTest.java | 62 ++++---- .../camel/telemetrydev}/AsyncWiretapTest.java | 63 ++++---- .../camel/telemetrydev/DisableEndpointTest.java | 85 +++++++++++ .../camel/telemetrydev/EnableProcessorsTest.java | 114 ++++++++++++++ .../camel/telemetrydev/SpanPropagationTest.java | 88 +++++++++++ .../camel/telemetrydev/TelemetryDevTracerTest.java | 126 ++++++++++++++++ .../TelemetryDevTracerTestSupport.java | 84 +++++++++++ .../src/test/resources/log4j2.properties | 16 +- components/camel-telemetry/pom.xml | 1 + .../camel-telemetry/src/main/docs/telemetry.adoc | 4 +- .../TraceProcessorsInterceptStrategy.java | 4 +- .../java/org/apache/camel/telemetry/Tracer.java | 4 + .../org/apache/camel/telemetry/AsyncCXFTest.java | 5 +- .../apache/camel/telemetry/AsyncDirectTest.java | 5 +- .../apache/camel/telemetry/AsyncWiretapTest.java | 7 +- .../src/test/resources/log4j2.properties | 22 --- components/pom.xml | 1 + ...emetryDevConfigurationPropertiesConfigurer.java | 81 ++++++++++ ...trySimpleConfigurationPropertiesConfigurer.java | 81 ++++++++++ .../camel-main-configuration-metadata.json | 5 + ....camel.main.TelemetryDevConfigurationProperties | 2 + ...mel.main.TelemetrySimpleConfigurationProperties | 2 + core/camel-main/src/main/docs/main.adoc | 13 ++ .../org/apache/camel/main/BaseMainSupport.java | 59 +++++++- .../camel/main/MainConfigurationProperties.java | 8 + .../main/TelemetryDevConfigurationProperties.java | 114 ++++++++++++++ .../others/examples/json/telemetry-dev.json | 1 + docs/components/modules/others/nav.adoc | 1 + .../modules/others/pages/telemetry-dev.adoc | 1 + parent/pom.xml | 5 + .../maven/packaging/PrepareCamelMainMojo.java | 5 + 49 files changed, 1844 insertions(+), 143 deletions(-) diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml index bcefff1aa67..f9bde6a6f5b 100644 --- a/bom/camel-bom/pom.xml +++ b/bom/camel-bom/pom.xml @@ -2012,6 +2012,11 @@ <artifactId>camel-telemetry</artifactId> <version>4.11.0-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-telemetry-dev</artifactId> + <version>4.11.0-SNAPSHOT</version> + </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-tensorflow-serving</artifactId> diff --git a/catalog/camel-allcomponents/pom.xml b/catalog/camel-allcomponents/pom.xml index 352e00c304a..62c135fdef4 100644 --- a/catalog/camel-allcomponents/pom.xml +++ b/catalog/camel-allcomponents/pom.xml @@ -1811,6 +1811,11 @@ <artifactId>camel-telemetry</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-telemetry-dev</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-tensorflow-serving</artifactId> 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 71a1bfd7be6..dfd524df0fa 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 @@ -17,6 +17,7 @@ { "name": "camel.vault.kubernetescm", "description": "Camel Kubernetes Configmaps Vault configurations", "sourceType": "org.apache.camel.vault.KubernetesConfigMapVaultConfiguration" }, { "name": "camel.vault.hashicorp", "description": "Camel Hashicorp Vault configurations", "sourceType": "org.apache.camel.vault.HashicorpVaultConfiguration" }, { "name": "camel.opentelemetry", "description": "Camel OpenTelemetry configurations", "sourceType": "org.apache.camel.main.OtelConfigurationProperties" }, + { "name": "camel.telemetryDev", "description": "Camel Telemetry Dev configurations", "sourceType": "org.apache.camel.main.TelemetryDevConfigurationProperties" }, { "name": "camel.metrics", "description": "Camel Micrometer Metrics configurations", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties" }, { "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" }, @@ -318,6 +319,10 @@ { "name": "camel.startupcondition.interval", "description": "Interval in millis between checking conditions.", "sourceType": "org.apache.camel.main.StartupConditionConfigurationProperties", "type": "integer", "javaType": "int", "defaultValue": 500 }, { "name": "camel.startupcondition.onTimeout", "description": "What action, to do on timeout. fail = do not startup, and throw an exception causing camel to fail stop = do not startup, and stop camel ignore = log a WARN and continue to startup", "sourceType": "org.apache.camel.main.StartupConditionConfigurationProperties", "type": "string", "javaType": "java.lang.String", "defaultValue": "stop", "enum": [ "fail", "stop", "ignore" ] }, { "name": "camel.startupcondition.timeout", "description": "Total timeout (in millis) for all startup conditions.", "sourceType": "org.apache.camel.main.StartupConditionConfigurationProperties", "type": "integer", "javaType": "int", "defaultValue": 20000 }, + { "name": "camel.telemetryDev.enabled", "description": "To enable TelemetryDev", "sourceType": "org.apache.camel.main.TelemetryDevConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, + { "name": "camel.telemetryDev.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.TelemetryDevConfigurationProperties", "type": "string", "javaType": "java.lang.String" }, + { "name": "camel.telemetryDev.traceFormat", "description": "The output format for traces.", "sourceType": "org.apache.camel.main.TelemetryDevConfigurationProperties", "type": "string", "javaType": "java.lang.String" }, + { "name": "camel.telemetryDev.traceProcessors", "description": "Setting this to true will create new TelemetrySimple Spans for each Camel Processors. Use the excludePattern property to filter out Processors.", "sourceType": "org.apache.camel.main.TelemetryDevConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, { "name": "camel.threadpool.allowCoreThreadTimeOut", "description": "Sets default whether to allow core threads to timeout", "sourceType": "org.apache.camel.main.ThreadPoolConfigurationProperties", "type": "boolean", "javaType": "java.lang.Boolean", "defaultValue": "false" }, { "name": "camel.threadpool.config", "description": "Adds a configuration for a specific thread pool profile (inherits default values)", "sourceType": "org.apache.camel.main.ThreadPoolConfigurationProperties", "type": "object", "javaType": "java.util.Map" }, { "name": "camel.threadpool.keepAliveTime", "description": "Sets the default keep alive time for inactive threads", "sourceType": "org.apache.camel.main.ThreadPoolConfigurationProperties", "type": "integer", "javaType": "java.lang.Long" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others.properties b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others.properties index 3b1b6cd29d0..1c26a3657eb 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others.properties +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others.properties @@ -49,6 +49,7 @@ spring-main spring-security spring-xml telemetry +telemetry-dev test-junit5 test-main-junit5 test-spring-junit5 diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others/telemetry-dev.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others/telemetry-dev.json new file mode 100644 index 00000000000..cc12eb9b485 --- /dev/null +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others/telemetry-dev.json @@ -0,0 +1,15 @@ +{ + "other": { + "kind": "other", + "name": "telemetry-dev", + "title": "Temetry Dev", + "description": "Basic implementation of Camel Telemetry useful for development purposes", + "deprecated": false, + "firstVersion": "4.11.0", + "label": "monitoring,microservice", + "supportLevel": "Preview", + "groupId": "org.apache.camel", + "artifactId": "camel-telemetry-dev", + "version": "4.11.0-SNAPSHOT" + } +} diff --git a/components/camel-telemetry-dev/pom.xml b/components/camel-telemetry-dev/pom.xml new file mode 100644 index 00000000000..12a16f533e6 --- /dev/null +++ b/components/camel-telemetry-dev/pom.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.camel</groupId> + <artifactId>components</artifactId> + <version>4.11.0-SNAPSHOT</version> + </parent> + + <artifactId>camel-telemetry-dev</artifactId> + <packaging>jar</packaging> + <name>Camel :: Telemetry :: Dev</name> + <description>Basic implementation of Camel Telemetry useful for development purposes</description> + + <properties> + <firstVersion>4.11.0</firstVersion> + <label>monitoring,microservice</label> + <title>Temetry Dev</title> + <supportLevel>Preview</supportLevel> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-telemetry</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + <version>${jackson2-version}</version> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-test-spring-junit5</artifactId> + <scope>test</scope> + </dependency> + + <!-- Required to test CXF async --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-cxf-rest</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-cxf-common</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-transports-http-undertow</artifactId> + <version>${cxf-version}</version> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>io.undertow</groupId> + <artifactId>undertow-servlet</artifactId> + </exclusion> + <exclusion> + <groupId>io.undertow</groupId> + <artifactId>undertow-servlet-jakarta</artifactId> + </exclusion> + <exclusion> + <groupId>io.undertow</groupId> + <artifactId>undertow-core</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-undertow</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/components/camel-telemetry-dev/src/generated/java/org/apache/camel/telemetrydev/TelemetryDevTracerConfigurer.java b/components/camel-telemetry-dev/src/generated/java/org/apache/camel/telemetrydev/TelemetryDevTracerConfigurer.java new file mode 100644 index 00000000000..f2be9dfb3ea --- /dev/null +++ b/components/camel-telemetry-dev/src/generated/java/org/apache/camel/telemetrydev/TelemetryDevTracerConfigurer.java @@ -0,0 +1,75 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.telemetrydev; + +import javax.annotation.processing.Generated; +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.telemetrydev.TelemetryDevTracer; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@Generated("org.apache.camel.maven.packaging.GenerateConfigurerMojo") +@SuppressWarnings("unchecked") +public class TelemetryDevTracerConfigurer 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.telemetrydev.TelemetryDevTracer target = (org.apache.camel.telemetrydev.TelemetryDevTracer) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "camelcontext": + case "camelContext": target.setCamelContext(property(camelContext, org.apache.camel.CamelContext.class, value)); return true; + case "excludepatterns": + case "excludePatterns": target.setExcludePatterns(property(camelContext, java.lang.String.class, value)); return true; + case "spanlifecyclemanager": + case "spanLifecycleManager": target.setSpanLifecycleManager(property(camelContext, org.apache.camel.telemetry.SpanLifecycleManager.class, value)); return true; + case "traceformat": + case "traceFormat": target.setTraceFormat(property(camelContext, java.lang.String.class, value)); return true; + case "traceprocessors": + case "traceProcessors": target.setTraceProcessors(property(camelContext, boolean.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 "excludepatterns": + case "excludePatterns": return java.lang.String.class; + case "spanlifecyclemanager": + case "spanLifecycleManager": return org.apache.camel.telemetry.SpanLifecycleManager.class; + case "traceformat": + case "traceFormat": return java.lang.String.class; + case "traceprocessors": + case "traceProcessors": return boolean.class; + default: return null; + } + } + + @Override + public Object getOptionValue(Object obj, String name, boolean ignoreCase) { + org.apache.camel.telemetrydev.TelemetryDevTracer target = (org.apache.camel.telemetrydev.TelemetryDevTracer) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "camelcontext": + case "camelContext": return target.getCamelContext(); + case "excludepatterns": + case "excludePatterns": return target.getExcludePatterns(); + case "spanlifecyclemanager": + case "spanLifecycleManager": return target.getSpanLifecycleManager(); + case "traceformat": + case "traceFormat": return target.getTraceFormat(); + case "traceprocessors": + case "traceProcessors": return target.isTraceProcessors(); + default: return null; + } + } +} + diff --git a/components/camel-telemetry-dev/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.telemetrydev.TelemetryDevTracer b/components/camel-telemetry-dev/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.telemetrydev.TelemetryDevTracer new file mode 100644 index 00000000000..a66d2dfb9a9 --- /dev/null +++ b/components/camel-telemetry-dev/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.telemetrydev.TelemetryDevTracer @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.telemetrydev.TelemetryDevTracerConfigurer diff --git a/components/camel-telemetry-dev/src/generated/resources/META-INF/services/org/apache/camel/other.properties b/components/camel-telemetry-dev/src/generated/resources/META-INF/services/org/apache/camel/other.properties new file mode 100644 index 00000000000..e90a4aa6854 --- /dev/null +++ b/components/camel-telemetry-dev/src/generated/resources/META-INF/services/org/apache/camel/other.properties @@ -0,0 +1,7 @@ +# Generated by camel build tools - do NOT edit this file! +name=telemetry-dev +groupId=org.apache.camel +artifactId=camel-telemetry-dev +version=4.11.0-SNAPSHOT +projectName=Camel :: Telemetry :: Dev +projectDescription=Basic implementation of Camel Telemetry useful for development purposes diff --git a/components/camel-telemetry-dev/src/generated/resources/META-INF/services/org/apache/camel/telemetry-dev-tracer b/components/camel-telemetry-dev/src/generated/resources/META-INF/services/org/apache/camel/telemetry-dev-tracer new file mode 100644 index 00000000000..a79529dbe39 --- /dev/null +++ b/components/camel-telemetry-dev/src/generated/resources/META-INF/services/org/apache/camel/telemetry-dev-tracer @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.telemetrydev.TelemetryDevTracer diff --git a/components/camel-telemetry-dev/src/generated/resources/telemetry-dev.json b/components/camel-telemetry-dev/src/generated/resources/telemetry-dev.json new file mode 100644 index 00000000000..cc12eb9b485 --- /dev/null +++ b/components/camel-telemetry-dev/src/generated/resources/telemetry-dev.json @@ -0,0 +1,15 @@ +{ + "other": { + "kind": "other", + "name": "telemetry-dev", + "title": "Temetry Dev", + "description": "Basic implementation of Camel Telemetry useful for development purposes", + "deprecated": false, + "firstVersion": "4.11.0", + "label": "monitoring,microservice", + "supportLevel": "Preview", + "groupId": "org.apache.camel", + "artifactId": "camel-telemetry-dev", + "version": "4.11.0-SNAPSHOT" + } +} diff --git a/components/camel-telemetry-dev/src/main/docs/telemetry-dev.adoc b/components/camel-telemetry-dev/src/main/docs/telemetry-dev.adoc new file mode 100644 index 00000000000..a4d0be2162b --- /dev/null +++ b/components/camel-telemetry-dev/src/main/docs/telemetry-dev.adoc @@ -0,0 +1,74 @@ += Temetry Dev Component +:doctitle: Temetry Dev +:shortname: telemetry-dev +:artifactid: camel-telemetry-dev +:description: Basic implementation of Camel Telemetry useful for development purposes +:since: 4.11 +:supportlevel: Preview +:tabs-sync-option: + +*Since Camel {since}* + +This module is a basic implementation of the common `camel-telemetry` interface. You can use this component as a basic tracing component when you can't use any other production grade telemetry component in order to troubleshoot or test the telemetry of your application. + +The component contains a basic mechanism to get the traces coming from the application and store to the very same log of the application in a variety of formats. The user may extract the result by scraping the log and aggregating the information accordingly. + +WARNING: this is an experimental component which is not suitable for production or system critical workflows. + +Ideally you can also configure the logger to output the telemetry results in a separate log file. This configuration depends on the concrete logging implementation you have in place knowing that the output will be logged at an `INFO` level from a logger named `LOG_TRACE`. + +== Configuration + +The configuration properties for the Telemetry component are: + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Option |Default |Description +|`camel.telemetryDev.enabled`| false | Turn the tracing on/off. +|`camel.telemetryDev.traceFormat`| default | The format used to trace in the log (default, tree, json). +|`camel.telemetryDev.traceProcessors`| false | Trace inner processors. +|`camel.telemetryDev.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. +|======================================================================= + +== Tracing format + +The component provides a few different traces representation formats you can use, depending on the scope of your telemetry or the integration with any third party telemetry collectors. + +=== Tree + +As the component is mainly targeted to have a quick visualization of a running application, then, the `tree` format is the recommended one. The output will be like the following: + +``` +| d6c36bc6f3374ed0bd6edb638535f130 +├── | timer://start (camel-timer) [2030 millis] <-- + ├── | process (to1-to) [2021 millis] + ├── | direct://new (camel-direct) [2020 millis] --> + ├── | direct://new (camel-direct) [2019 millis] <-- + ├── | process (delay1-delay) [2002 millis] + ├── | process (log2-log) [2 millis] + └── | process (to3-to) [3 millis] + └── | log://new (camel-log) [2 millis] --> + └── | process (to4-to) [1 millis] + └── | mock://end (camel-mock) [0 millis] --> + ├── | process (log1-log) [0 millis] + └── | process (to2-to) [2 millis] + └── | log://info (camel-log) [1 millis] --> +``` + +This is a quick visual tree Trace representation of a given execution of a Camel route. You can quickly verify the Camel URI, the component and the time spent to execute each Span. + +NOTE: the --> and <-- represents an outgoing or incoming message. + +=== Json + +This format is suitable if you need to integrate with any third party. The output of each trace will come in the following format: + +```json +{"traceId":"4ee161eb055644fca713f4c02ab2fb50","spans":[{"logEntries":[],"traceid":"4ee161eb055644fca713f4c02ab2fb50","spanid":"8785debcdc09409abeed65d08aaaca1b","exchangeId":"D1B1814C509A376-0000000000000000","op":"EVENT_RECEIVED","component":"camel-timer","url.path":"start","initTimestamp":"4085653460994","camel.uri":"timer://start","url.scheme":"timer","endTimestamp":"4087698388888","isDone":"true"},{"logEntries":[],"traceid":"4ee161eb055644fca713f4c02ab2fb50","spanid":"51cc3929d34e452 [...] +``` + +=== Default + +Default is a very simple serialization into the default Java object `toString()` representation. diff --git a/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/DevSpanAdapter.java b/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/DevSpanAdapter.java new file mode 100644 index 00000000000..3f5f6fe84e3 --- /dev/null +++ b/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/DevSpanAdapter.java @@ -0,0 +1,120 @@ +/* + * 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.telemetrydev; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import org.apache.camel.telemetry.Span; +import org.apache.camel.telemetry.TagConstants; + +public class DevSpanAdapter implements Span { + + private List<LogEntry> logEntries = new ArrayList<>(); + private final Map<String, String> tags = new HashMap<>(); + + public static long nowMicros() { + return System.currentTimeMillis() * 1000; + } + + public static DevSpanAdapter buildSpan(String operation) { + return new DevSpanAdapter(); + } + + @JsonAnyGetter + public Map<String, String> tags() { + return tags; + } + + @Override + public void setComponent(String component) { + this.tags.put(TagConstants.COMPONENT, component); + } + + @Override + public void setError(boolean error) { + this.tags.put(TagConstants.ERROR, "" + error); + } + + @JsonAnySetter + @Override + public void setTag(String key, String value) { + this.tags.put(key, value); + } + + public String getTag(String key) { + return this.tags.get(key); + } + + @Override + public void log(Map<String, String> fields) { + this.logEntries.add(new LogEntry(fields)); + } + + public List<LogEntry> getLogEntries() { + return new ArrayList<>(this.logEntries); + } + + public void setLogEntries(List<LogEntry> logEntries) { + this.logEntries = logEntries; + } + + public static final class LogEntry { + private final long timestampMicros; + private final Map<String, ?> fields; + + public LogEntry() { + this(new HashMap<>()); + } + + public LogEntry(Map<String, ?> fields) { + this.timestampMicros = nowMicros(); + this.fields = fields; + } + + public long timestampMicros() { + return timestampMicros; + } + + public Map<String, ?> getFields() { + return fields; + } + + @Override + public String toString() { + return fields.toString(); + } + } + + @Override + public String toString() { + String toString = this.tags.get("traceid") + "-" + this.tags.get("spanid") + "-["; + for (Entry<String, String> entry : this.tags().entrySet()) { + if (!entry.getKey().equals("traceid") && !entry.getKey().equals("spanid")) { + toString += entry.getKey() + "=" + entry.getValue() + ","; + } + } + toString += "]"; + return toString; + } + +} diff --git a/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/DevTrace.java b/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/DevTrace.java new file mode 100644 index 00000000000..5d97053266b --- /dev/null +++ b/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/DevTrace.java @@ -0,0 +1,66 @@ +/* + * 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.telemetrydev; + +import java.util.Collections; +import java.util.List; + +public class DevTrace { + + private String traceId; + private List<DevSpanAdapter> spans; + + DevTrace() { + + } + + public DevTrace(String traceId, List<DevSpanAdapter> spans) { + this.traceId = traceId; + this.spans = spans; + Collections.sort(this.spans, new SpanComparator()); + } + + @Override + public String toString() { + return traceId + " " + spans.toString(); + } + + public String getTraceId() { + return this.traceId; + } + + void setTraceId(String traceId) { + this.traceId = traceId; + } + + public List<DevSpanAdapter> getSpans() { + return this.spans; + } + + void setSpans(List<DevSpanAdapter> spans) { + this.spans = spans; + } +} + +class SpanComparator implements java.util.Comparator<DevSpanAdapter> { + @Override + public int compare(DevSpanAdapter a, DevSpanAdapter b) { + DevSpanAdapter msa = (DevSpanAdapter) a; + DevSpanAdapter msb = (DevSpanAdapter) b; + return (int) (Long.parseLong(msa.getTag("initTimestamp")) - Long.parseLong(msb.getTag("initTimestamp"))); + } +} diff --git a/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/DevTraceFormat.java b/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/DevTraceFormat.java new file mode 100644 index 00000000000..d1dc272a385 --- /dev/null +++ b/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/DevTraceFormat.java @@ -0,0 +1,156 @@ +/* + * 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.telemetrydev; + +import java.io.StringWriter; +import java.util.HashMap; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.apache.camel.RuntimeCamelException; +import org.apache.camel.telemetry.Op; + +/* + * An interface used to represent a trace in a given format. + */ +public interface DevTraceFormat { + + String format(DevTrace trace); + +} + +/* + * Output regular Java toString(). + */ +class DevTraceFormatDefault implements DevTraceFormat { + + @Override + public String format(DevTrace trace) { + return trace.toString(); + } + +} + +/* + * Output basic Json representation. + */ +class DevTraceFormatJson implements DevTraceFormat { + + ObjectMapper mapper; + + DevTraceFormatJson() { + mapper = new ObjectMapper(); + mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + } + + @Override + public String format(DevTrace trace) { + try { + return mapper.writeValueAsString(trace); + } catch (JsonProcessingException e) { + throw new RuntimeCamelException(e); + } + } +} + +/* + * Output a basic tree visual structure. + */ +class DevTraceFormatTree implements DevTraceFormat { + + @Override + public String format(DevTrace trace) { + StringWriter sw = new StringWriter(); + sw.append("\n| " + trace.getTraceId() + "\n"); + HashMap<String, Integer> depths = new HashMap<>(); + int depth = 0; + for (int j = 0; j < trace.getSpans().size(); j++) { + DevSpanAdapter span = trace.getSpans().get(j); + String marker = getMarker(depths, span, j + 1 < trace.getSpans().size() ? trace.getSpans().get(j + 1) : null); + String actualParentSpan = span.getTag("parentSpan"); + if (depths.containsKey(actualParentSpan)) { + depth = depths.get(actualParentSpan); + } else if (actualParentSpan != null) { + depth++; + depths.put(actualParentSpan, depth); + } else { + depth = 0; + } + + if (depth > 0) { + sw.append(" "); + for (int i = 0; i < depth; i++) { + sw.append(" "); + } + if (depth == 0) { + + } + if (depth > 1) { + for (int i = 1; i < depth; i++) { + sw.append(" "); + } + } + } + sw.append(String.format("%s── %s\n", marker, formatSpan(span))); + } + return sw.toString(); + } + + private String formatSpan(DevSpanAdapter span) { + if (span.getTag("isDone") == null || !span.getTag("isDone").equals("true")) { + return String.format("[error: span %s not closed!]", span.getTag("component")); + } + if (span.getTag("initTimestamp") == null || span.getTag("endTimestamp") == null) { + return String.format("[error: span %s incomplete!]", span.getTag("component")); + } + String sentOrReceived = null; + if (span.getTag("op").equals(Op.EVENT_SENT.name())) { + sentOrReceived = "-->"; + } else if (span.getTag("op").equals(Op.EVENT_RECEIVED.name())) { + sentOrReceived = "<--"; + } else { + sentOrReceived = ""; + } + long nanos + = (Long.parseLong(span.getTag("endTimestamp")) - Long.parseLong(span.getTag("initTimestamp"))) / (1000 * 1000); + String component = span.getTag("component"); + String camelUri = span.getTag("camel.uri"); + return String.format( + "| %s (%s) [%d millis] %s", + camelUri == null ? "process" : camelUri, + component, + nanos, + sentOrReceived); + } + + private String getMarker(HashMap<String, Integer> depths, DevSpanAdapter span, DevSpanAdapter next) { + if (next == null) { + return "└"; + } + Integer thisDepth = depths.get(span.getTag("parentSpan")); + Integer nextDepth = depths.get(next.getTag("parentSpan")); + if (thisDepth == null && nextDepth == null) { + return "├"; + } + if (thisDepth != null && nextDepth != null && nextDepth >= thisDepth) { + return "├"; + } + return "└"; + } + +} diff --git a/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/InMemoryCollector.java b/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/InMemoryCollector.java new file mode 100644 index 00000000000..8baad8bf54d --- /dev/null +++ b/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/InMemoryCollector.java @@ -0,0 +1,54 @@ +/* + * 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.telemetrydev; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/* + * Basic inmemory implementation for an home made spans collector. + */ +public class InMemoryCollector { + + // traceid --> spanid --> Span + private Map<String, Map<String, DevSpanAdapter>> traceDB = new HashMap<>(); + + public synchronized void push(String traceId, DevSpanAdapter span) { + Map<String, DevSpanAdapter> spans = traceDB.get(traceId); + if (spans == null) { + spans = new HashMap<>(); + traceDB.put(traceId, spans); + } + spans.put(span.getTag("spanid"), span); + } + + public synchronized DevTrace get(String traceId) { + Map<String, DevSpanAdapter> spans = traceDB.get(traceId); + if (spans == null) { + return null; + } + for (DevSpanAdapter span : spans.values()) { + if (!"true".equals(span.getTag("isDone"))) { + // Still an active trace, not all spans are closed + return null; + } + } + traceDB.remove(traceId); + return new DevTrace(traceId, new ArrayList<>(spans.values())); + } +} diff --git a/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/TelemetryDevTracer.java b/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/TelemetryDevTracer.java new file mode 100644 index 00000000000..3107b177739 --- /dev/null +++ b/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/TelemetryDevTracer.java @@ -0,0 +1,164 @@ +/* + * 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.telemetrydev; + +import java.util.UUID; + +import org.apache.camel.api.management.ManagedAttribute; +import org.apache.camel.api.management.ManagedResource; +import org.apache.camel.spi.Configurer; +import org.apache.camel.spi.annotations.JdkService; +import org.apache.camel.telemetry.Span; +import org.apache.camel.telemetry.SpanContextPropagationExtractor; +import org.apache.camel.telemetry.SpanContextPropagationInjector; +import org.apache.camel.telemetry.SpanLifecycleManager; +import org.apache.camel.telemetry.Tracer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@JdkService("telemetry-dev-tracer") +@Configurer +@ManagedResource(description = "TelemetryDevTracer") +public class TelemetryDevTracer extends Tracer { + + private static final Logger LOG = LoggerFactory.getLogger(TelemetryDevTracer.class); + + private final Logger LOGTRACE = LoggerFactory.getLogger("LOG_TRACE"); + private String traceFormat; + + @ManagedAttribute(description = "The format of traces (default, tree, json)") + public String getTraceFormat() { + return traceFormat; + } + + public void setTraceFormat(String traceFormat) { + this.traceFormat = traceFormat; + } + + @Override + protected void initTracer() { + this.setSpanLifecycleManager(new DevSpanLifecycleManager(traceFormat)); + } + + @Override + protected void doStart() throws Exception { + super.doStart(); + LOG.warn( + "TelemetryDevTracer enabled. This is a development tracer and you should avoid using it in production workflows."); + } + + private class DevSpanLifecycleManager implements SpanLifecycleManager { + + private final InMemoryCollector inMemoryCollector = new InMemoryCollector(); + private DevTraceFormat stf; + + private DevSpanLifecycleManager(String traceFormat) { + if (traceFormat == null) { + traceFormat = "default"; + } + switch (traceFormat.toLowerCase()) { + case "tree": + this.stf = new DevTraceFormatTree(); + break; + case "json": + this.stf = new DevTraceFormatJson(); + break; + case "default": + this.stf = new DevTraceFormatDefault(); + break; + default: + LOG.warn("Unknown {} trace format. Fallback to default.", traceFormat); + break; + } + } + + @Override + public Span create(String spanName, Span parent, SpanContextPropagationExtractor extractor) { + Span span = DevSpanAdapter.buildSpan(spanName); + String traceId = UUID.randomUUID().toString().replaceAll("-", ""); + if (parent != null) { + traceId = spanTraceId(parent); + span.setTag("parentSpan", spanSpanId(parent)); + } else { + String upstreamTraceParent = (String) extractor.get("traceparent"); + if (upstreamTraceParent != null) { + String[] split = upstreamTraceParent.toString().split("-"); + if (split.length != 2) { + LOG.error("TRACE ERROR: wrong format, could not split traceparent {}", upstreamTraceParent); + span.setTag("traceid", traceId); + span.setTag("spanid", UUID.randomUUID().toString().replaceAll("-", "")); + return span; + } + traceId = split[0]; + String parentSpanId = split[1]; + span.setTag("parentSpan", parentSpanId); + } + } + span.setTag("traceid", traceId); + span.setTag("spanid", UUID.randomUUID().toString().replaceAll("-", "")); + return span; + } + + @Override + public void activate(Span span) { + span.setTag("initTimestamp", "" + System.nanoTime()); + DevSpanAdapter ssa = (DevSpanAdapter) span; + inMemoryCollector.push(ssa.getTag("traceid"), ssa); + } + + @Override + public void close(Span span) { + span.setTag("isDone", "true"); + DevSpanAdapter ssa = (DevSpanAdapter) span; + DevTrace trace = inMemoryCollector.get(ssa.getTag("traceid")); + if (trace != null) { + LOGTRACE.info("{}", stf.format(trace)); + } + } + + @Override + public void deactivate(Span span) { + span.setTag("endTimestamp", "" + System.nanoTime()); + } + + @Override + public void inject(Span span, SpanContextPropagationInjector injector) { + String[] split = span.toString().split("-"); + if (split.length < 2) { + LOG.error("TRACE ERROR: wrong format, could not split traceparent {}", span); + return; + } + injector.put("traceparent", split[0] + "-" + split[1]); + } + + } + + private static String spanTraceId(Span span) { + if (span == null) { + return ""; + } + return span.toString().split("-")[0]; + } + + private static String spanSpanId(Span span) { + if (span == null) { + return ""; + } + return span.toString().split("-")[1]; + } + +} diff --git a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncCXFTest.java b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/AsyncCXFTest.java similarity index 76% copy from components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncCXFTest.java copy to components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/AsyncCXFTest.java index a828aae2740..2ca776917e1 100644 --- a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncCXFTest.java +++ b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/AsyncCXFTest.java @@ -14,8 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.telemetry; +package org.apache.camel.telemetrydev; +import java.io.IOException; import java.util.List; import java.util.Map; @@ -24,11 +25,8 @@ import org.apache.camel.CamelContextAware; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; -import org.apache.camel.telemetry.mock.MockSpanAdapter; -import org.apache.camel.telemetry.mock.MockTrace; -import org.apache.camel.telemetry.mock.MockTracer; +import org.apache.camel.telemetry.Op; import org.apache.camel.test.AvailablePortFinder; -import org.apache.camel.test.junit5.ExchangeTestSupport; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -39,63 +37,59 @@ import static org.junit.jupiter.api.Assertions.assertNull; * AsyncCXFTest tests the execution of CXF async which was reported as a potential candidate to * inconsistent Span creation in async mode. */ -public class AsyncCXFTest extends ExchangeTestSupport { +public class AsyncCXFTest extends TelemetryDevTracerTestSupport { private static int cxfPort = AvailablePortFinder.getNextRandomAvailable(); - MockTracer mockTracer; - @Override protected CamelContext createCamelContext() throws Exception { + TelemetryDevTracer tst = new TelemetryDevTracer(); + tst.setTraceFormat("json"); CamelContext context = super.createCamelContext(); - this.mockTracer = new MockTracer(); - CamelContextAware.trySetCamelContext(mockTracer, context); - mockTracer.init(context); + CamelContextAware.trySetCamelContext(tst, context); + tst.init(context); return context; } @Test - void testRouteMultipleRequests() throws InterruptedException { + void testRouteMultipleRequests() throws InterruptedException, IOException { int j = 10; MockEndpoint mock = getMockEndpoint("mock:end"); mock.expectedMessageCount(j); + mock.setAssertPeriod(5000); for (int i = 0; i < j; i++) { context.createProducerTemplate().sendBody("direct:start", "Hello!"); } - // The wiretapped endpoint is delaying at least 2 seconds for each request so - // we must wait for the overall execution time (i*2 seconds) to complete before checking the results - mock.assertIsSatisfied(j * 2500); - Map<String, MockTrace> traces = mockTracer.traces(); + mock.assertIsSatisfied(1000); + Map<String, DevTrace> traces = tracesFromLog(); // Each trace should have a unique trace id. It is enough to assert that // the number of elements in the map is the same of the requests to prove // all traces have been generated uniquely. assertEquals(j, traces.size()); // Each trace should have the same structure - for (MockTrace trace : traces.values()) { + for (DevTrace trace : traces.values()) { checkTrace(trace, "Hello!"); } } - private void checkTrace(MockTrace trace, String expectedBody) { - List<Span> spans = trace.spans(); + private void checkTrace(DevTrace trace, String expectedBody) { + List<DevSpanAdapter> spans = trace.getSpans(); assertEquals(8, spans.size()); - // Cast to implementation object to be able to - // inspect the status of the Span. - MockSpanAdapter testProducer = SpanTestSupport.getSpan(spans, "direct://start", Op.EVENT_SENT); - MockSpanAdapter direct = SpanTestSupport.getSpan(spans, "direct://start", Op.EVENT_RECEIVED); - MockSpanAdapter directSendTo = SpanTestSupport.getSpan(spans, "direct://send", Op.EVENT_SENT); - MockSpanAdapter directSendFrom = SpanTestSupport.getSpan(spans, "direct://send", Op.EVENT_RECEIVED); - MockSpanAdapter cxfRs = SpanTestSupport.getSpan( + DevSpanAdapter testProducer = TelemetryDevTracerTestSupport.getSpan(spans, "direct://start", Op.EVENT_SENT); + DevSpanAdapter direct = TelemetryDevTracerTestSupport.getSpan(spans, "direct://start", Op.EVENT_RECEIVED); + DevSpanAdapter directSendTo = TelemetryDevTracerTestSupport.getSpan(spans, "direct://send", Op.EVENT_SENT); + DevSpanAdapter directSendFrom = TelemetryDevTracerTestSupport.getSpan(spans, "direct://send", Op.EVENT_RECEIVED); + DevSpanAdapter cxfRs = TelemetryDevTracerTestSupport.getSpan( spans, "cxfrs://http://localhost:" + cxfPort + "/rest/helloservice/sayHello?synchronous=false", Op.EVENT_SENT); - MockSpanAdapter rest = SpanTestSupport.getSpan( + DevSpanAdapter rest = TelemetryDevTracerTestSupport.getSpan( spans, "rest://post:/rest/helloservice:/sayHello?routeId=direct-hi", Op.EVENT_RECEIVED); - MockSpanAdapter log = SpanTestSupport.getSpan(spans, "log://hi", Op.EVENT_SENT); - MockSpanAdapter mock = SpanTestSupport.getSpan(spans, "mock://end", Op.EVENT_SENT); + DevSpanAdapter log = TelemetryDevTracerTestSupport.getSpan(spans, "log://hi", Op.EVENT_SENT); + DevSpanAdapter mock = TelemetryDevTracerTestSupport.getSpan(spans, "mock://end", Op.EVENT_SENT); // Validate span completion assertEquals("true", testProducer.getTag("isDone")); @@ -136,8 +130,8 @@ public class AsyncCXFTest extends ExchangeTestSupport { assertEquals(rest.getTag("spanid"), mock.getTag("parentSpan")); // Validate message logging - assertEquals("A direct message", directSendFrom.logEntries().get(0).fields().get("message")); - assertEquals("say-hi", rest.logEntries().get(0).fields().get("message")); + assertEquals("A direct message", directSendFrom.getLogEntries().get(0).getFields().get("message")); + assertEquals("say-hi", rest.getLogEntries().get(0).getFields().get("message")); } @Override diff --git a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncDirectTest.java b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/AsyncDirectTest.java similarity index 71% copy from components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncDirectTest.java copy to components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/AsyncDirectTest.java index 6fac52a24ff..d41399686f8 100644 --- a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncDirectTest.java +++ b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/AsyncDirectTest.java @@ -14,8 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.telemetry; +package org.apache.camel.telemetrydev; +import java.io.IOException; import java.util.List; import java.util.Map; @@ -24,63 +25,56 @@ import org.apache.camel.CamelContextAware; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; -import org.apache.camel.telemetry.mock.MockSpanAdapter; -import org.apache.camel.telemetry.mock.MockTrace; -import org.apache.camel.telemetry.mock.MockTracer; -import org.apache.camel.test.junit5.ExchangeTestSupport; +import org.apache.camel.telemetry.Op; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -public class AsyncDirectTest extends ExchangeTestSupport { - - MockTracer mockTracer; +public class AsyncDirectTest extends TelemetryDevTracerTestSupport { @Override protected CamelContext createCamelContext() throws Exception { + TelemetryDevTracer tst = new TelemetryDevTracer(); + tst.setTraceFormat("json"); CamelContext context = super.createCamelContext(); - this.mockTracer = new MockTracer(); - CamelContextAware.trySetCamelContext(mockTracer, context); - mockTracer.init(context); + CamelContextAware.trySetCamelContext(tst, context); + tst.init(context); return context; } @Test - void testRouteMultipleRequests() throws InterruptedException { + void testRouteMultipleRequests() throws InterruptedException, IOException { int j = 10; MockEndpoint mock = getMockEndpoint("mock:end"); mock.expectedMessageCount(j); + mock.setAssertPeriod(5000); for (int i = 0; i < j; i++) { context.createProducerTemplate().sendBody("direct:start", "Hello!"); } - // The wiretapped endpoint is delaying at least 2 seconds for each request so - // we must wait for the overall execution time (i*2 seconds) to complete before checking the results - mock.assertIsSatisfied(j * 2500); - Map<String, MockTrace> traces = mockTracer.traces(); + mock.assertIsSatisfied(1000); + Map<String, DevTrace> traces = tracesFromLog(); // Each trace should have a unique trace id. It is enough to assert that // the number of elements in the map is the same of the requests to prove // all traces have been generated uniquely. assertEquals(j, traces.size()); // Each trace should have the same structure - for (MockTrace trace : traces.values()) { + for (DevTrace trace : traces.values()) { checkTrace(trace, "Hello!"); } } - private void checkTrace(MockTrace trace, String expectedBody) { - List<Span> spans = trace.spans(); + private void checkTrace(DevTrace trace, String expectedBody) { + List<DevSpanAdapter> spans = trace.getSpans(); assertEquals(7, spans.size()); - // Cast to implementation object to be able to - // inspect the status of the Span. - MockSpanAdapter testProducer = SpanTestSupport.getSpan(spans, "direct://start", Op.EVENT_SENT); - MockSpanAdapter direct = SpanTestSupport.getSpan(spans, "direct://start", Op.EVENT_RECEIVED); - MockSpanAdapter newDirectTo = SpanTestSupport.getSpan(spans, "direct://new", Op.EVENT_SENT); - MockSpanAdapter log = SpanTestSupport.getSpan(spans, "log://info", Op.EVENT_SENT); - MockSpanAdapter newDirectFrom = SpanTestSupport.getSpan(spans, "direct://new", Op.EVENT_RECEIVED); - MockSpanAdapter newLog = SpanTestSupport.getSpan(spans, "log://new", Op.EVENT_SENT); - MockSpanAdapter newMock = SpanTestSupport.getSpan(spans, "mock://end", Op.EVENT_SENT); + DevSpanAdapter testProducer = TelemetryDevTracerTestSupport.getSpan(spans, "direct://start", Op.EVENT_SENT); + DevSpanAdapter direct = TelemetryDevTracerTestSupport.getSpan(spans, "direct://start", Op.EVENT_RECEIVED); + DevSpanAdapter newDirectTo = TelemetryDevTracerTestSupport.getSpan(spans, "direct://new", Op.EVENT_SENT); + DevSpanAdapter log = TelemetryDevTracerTestSupport.getSpan(spans, "log://info", Op.EVENT_SENT); + DevSpanAdapter newDirectFrom = TelemetryDevTracerTestSupport.getSpan(spans, "direct://new", Op.EVENT_RECEIVED); + DevSpanAdapter newLog = TelemetryDevTracerTestSupport.getSpan(spans, "log://new", Op.EVENT_SENT); + DevSpanAdapter newMock = TelemetryDevTracerTestSupport.getSpan(spans, "mock://end", Op.EVENT_SENT); // Validate span completion assertEquals("true", testProducer.getTag("isDone")); @@ -119,22 +113,22 @@ public class AsyncDirectTest extends ExchangeTestSupport { assertEquals(newDirectFrom.getTag("spanid"), newMock.getTag("parentSpan")); // Validate message logging - assertEquals("A direct message", direct.logEntries().get(0).fields().get("message")); - assertEquals("A new message", newDirectFrom.logEntries().get(0).fields().get("message")); + assertEquals("A direct message", direct.getLogEntries().get(0).getFields().get("message")); + assertEquals("A new message", newDirectFrom.getLogEntries().get(0).getFields().get("message")); if (expectedBody == null) { assertEquals( "Exchange[ExchangePattern: InOut, BodyType: null, Body: [Body is null]]", - log.logEntries().get(0).fields().get("message")); + log.getLogEntries().get(0).getFields().get("message")); assertEquals( "Exchange[ExchangePattern: InOut, BodyType: null, Body: [Body is null]]", - newLog.logEntries().get(0).fields().get("message")); + newLog.getLogEntries().get(0).getFields().get("message")); } else { assertEquals( "Exchange[ExchangePattern: InOnly, BodyType: String, Body: " + expectedBody + "]", - log.logEntries().get(0).fields().get("message")); + log.getLogEntries().get(0).getFields().get("message")); assertEquals( "Exchange[ExchangePattern: InOnly, BodyType: String, Body: " + expectedBody + "]", - newLog.logEntries().get(0).fields().get("message")); + newLog.getLogEntries().get(0).getFields().get("message")); } } diff --git a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncWiretapTest.java b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/AsyncWiretapTest.java similarity index 72% copy from components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncWiretapTest.java copy to components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/AsyncWiretapTest.java index 1c8f2a0fe03..67fd4384dda 100644 --- a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncWiretapTest.java +++ b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/AsyncWiretapTest.java @@ -14,8 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.telemetry; +package org.apache.camel.telemetrydev; +import java.io.IOException; import java.util.List; import java.util.Map; @@ -24,10 +25,7 @@ import org.apache.camel.CamelContextAware; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; -import org.apache.camel.telemetry.mock.MockSpanAdapter; -import org.apache.camel.telemetry.mock.MockTrace; -import org.apache.camel.telemetry.mock.MockTracer; -import org.apache.camel.test.junit5.ExchangeTestSupport; +import org.apache.camel.telemetry.Op; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -38,54 +36,51 @@ import static org.junit.jupiter.api.Assertions.assertNull; * WiretappedRouteTest tests the execution of a new spin off component which would create a new exchange, for example, * using the wiretap component. */ -public class AsyncWiretapTest extends ExchangeTestSupport { - - MockTracer mockTracer; +public class AsyncWiretapTest extends TelemetryDevTracerTestSupport { @Override protected CamelContext createCamelContext() throws Exception { + TelemetryDevTracer tst = new TelemetryDevTracer(); + tst.setTraceFormat("json"); CamelContext context = super.createCamelContext(); - this.mockTracer = new MockTracer(); - CamelContextAware.trySetCamelContext(mockTracer, context); - mockTracer.init(context); + CamelContextAware.trySetCamelContext(tst, context); + tst.init(context); return context; } @Test - void testRouteMultipleRequests() throws InterruptedException { + void testRouteMultipleRequests() throws InterruptedException, IOException { int j = 10; MockEndpoint mock = getMockEndpoint("mock:end"); mock.expectedMessageCount(j); + mock.setAssertPeriod(5000); for (int i = 0; i < j; i++) { context.createProducerTemplate().sendBody("direct:start", "Hello!"); } - // The wiretapped endpoint is delaying at least 2 seconds for each request so - // we must wait for the overall execution time (i*2 seconds) to complete before checking the results - mock.assertIsSatisfied(j * 2500); - Map<String, MockTrace> traces = mockTracer.traces(); + mock.assertIsSatisfied(1000); + Map<String, DevTrace> traces = tracesFromLog(); // Each trace should have a unique trace id. It is enough to assert that // the number of elements in the map is the same of the requests to prove // all traces have been generated uniquely. assertEquals(j, traces.size()); // Each trace should have the same structure - for (MockTrace trace : traces.values()) { + for (DevTrace trace : traces.values()) { checkTrace(trace, "Hello!"); } } - private void checkTrace(MockTrace trace, String expectedBody) { - List<Span> spans = trace.spans(); + private void checkTrace(DevTrace trace, String expectedBody) { + List<DevSpanAdapter> spans = trace.getSpans(); assertEquals(7, spans.size()); - // Cast to implementation object to be able to - // inspect the status of the Span. - MockSpanAdapter testProducer = SpanTestSupport.getSpan(spans, "direct://start", Op.EVENT_SENT); - MockSpanAdapter direct = SpanTestSupport.getSpan(spans, "direct://start", Op.EVENT_RECEIVED); - MockSpanAdapter wiretapDirectTo = SpanTestSupport.getSpan(spans, "direct://tap", Op.EVENT_SENT); - MockSpanAdapter wiretapDirectFrom = SpanTestSupport.getSpan(spans, "direct://tap", Op.EVENT_RECEIVED); - MockSpanAdapter log = SpanTestSupport.getSpan(spans, "log://info", Op.EVENT_SENT); - MockSpanAdapter wiretapLog = SpanTestSupport.getSpan(spans, "log://tapped", Op.EVENT_SENT); - MockSpanAdapter wiretapMock = SpanTestSupport.getSpan(spans, "mock://end", Op.EVENT_SENT); + DevSpanAdapter testProducer = TelemetryDevTracerTestSupport.getSpan(spans, "direct://start", Op.EVENT_SENT); + DevSpanAdapter direct = TelemetryDevTracerTestSupport.getSpan(spans, "direct://start", Op.EVENT_RECEIVED); + DevSpanAdapter wiretapDirectTo = TelemetryDevTracerTestSupport.getSpan(spans, "direct://tap", Op.EVENT_SENT); + DevSpanAdapter wiretapDirectFrom + = TelemetryDevTracerTestSupport.getSpan(spans, "direct://tap", Op.EVENT_RECEIVED); + DevSpanAdapter log = TelemetryDevTracerTestSupport.getSpan(spans, "log://info", Op.EVENT_SENT); + DevSpanAdapter wiretapLog = TelemetryDevTracerTestSupport.getSpan(spans, "log://tapped", Op.EVENT_SENT); + DevSpanAdapter wiretapMock = TelemetryDevTracerTestSupport.getSpan(spans, "mock://end", Op.EVENT_SENT); // Validate span completion assertEquals("true", testProducer.getTag("isDone")); @@ -122,22 +117,22 @@ public class AsyncWiretapTest extends ExchangeTestSupport { assertEquals(wiretapDirectFrom.getTag("spanid"), wiretapMock.getTag("parentSpan")); // Validate message logging - assertEquals("A direct message", direct.logEntries().get(0).fields().get("message")); - assertEquals("A tapped message", wiretapDirectFrom.logEntries().get(0).fields().get("message")); + assertEquals("A direct message", direct.getLogEntries().get(0).getFields().get("message")); + assertEquals("A tapped message", wiretapDirectFrom.getLogEntries().get(0).getFields().get("message")); if (expectedBody == null) { assertEquals( "Exchange[ExchangePattern: InOut, BodyType: null, Body: [Body is null]]", - log.logEntries().get(0).fields().get("message")); + log.getLogEntries().get(0).getFields().get("message")); assertEquals( "Exchange[ExchangePattern: InOut, BodyType: null, Body: [Body is null]]", - wiretapLog.logEntries().get(0).fields().get("message")); + wiretapLog.getLogEntries().get(0).getFields().get("message")); } else { assertEquals( "Exchange[ExchangePattern: InOnly, BodyType: String, Body: " + expectedBody + "]", - log.logEntries().get(0).fields().get("message")); + log.getLogEntries().get(0).getFields().get("message")); assertEquals( "Exchange[ExchangePattern: InOnly, BodyType: String, Body: " + expectedBody + "]", - wiretapLog.logEntries().get(0).fields().get("message")); + wiretapLog.getLogEntries().get(0).getFields().get("message")); } } diff --git a/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/DisableEndpointTest.java b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/DisableEndpointTest.java new file mode 100644 index 00000000000..b6cdf3f00e6 --- /dev/null +++ b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/DisableEndpointTest.java @@ -0,0 +1,85 @@ +/* + * 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.telemetrydev; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class DisableEndpointTest extends TelemetryDevTracerTestSupport { + + @Override + protected CamelContext createCamelContext() throws Exception { + TelemetryDevTracer tst = new TelemetryDevTracer(); + tst.setTraceFormat("json"); + tst.setTraceProcessors(true); + tst.setExcludePatterns("log*,to*"); + CamelContext context = super.createCamelContext(); + CamelContextAware.trySetCamelContext(tst, context); + tst.init(context); + return context; + } + + @Test + void testProcessorsTraceRequest() throws IOException { + template.sendBody("direct:start", "my-body"); + Map<String, DevTrace> traces = tracesFromLog(); + assertEquals(1, traces.size()); + checkTrace(traces.values().iterator().next()); + } + + private void checkTrace(DevTrace trace) { + List<DevSpanAdapter> spans = trace.getSpans(); + assertEquals(2, spans.size()); + DevSpanAdapter testProducer = spans.get(0); + DevSpanAdapter direct = spans.get(1); + + // Validate span completion + assertEquals("true", testProducer.getTag("isDone")); + assertEquals("true", direct.getTag("isDone")); + + // Validate same trace + assertEquals(testProducer.getTag("traceid"), direct.getTag("traceid")); + + // Validate hierarchy + assertNull(testProducer.getTag("parentSpan")); + assertEquals(testProducer.getTag("spanid"), direct.getTag("parentSpan")); + } + + @Override + protected RoutesBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:start") + .routeId("start") + .log("A message") + .to("log:info"); + } + }; + } + +} diff --git a/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/EnableProcessorsTest.java b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/EnableProcessorsTest.java new file mode 100644 index 00000000000..9c0a4b92263 --- /dev/null +++ b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/EnableProcessorsTest.java @@ -0,0 +1,114 @@ +/* + * 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.telemetrydev; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.telemetry.Op; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class EnableProcessorsTest extends TelemetryDevTracerTestSupport { + + @Override + protected CamelContext createCamelContext() throws Exception { + TelemetryDevTracer tst = new TelemetryDevTracer(); + tst.setTraceFormat("json"); + tst.setTraceProcessors(true); + CamelContext context = super.createCamelContext(); + CamelContextAware.trySetCamelContext(tst, context); + tst.init(context); + return context; + } + + @Test + void testProcessorsTraceRequest() throws IOException { + template.sendBody("direct:start", "my-body"); + Map<String, DevTrace> traces = tracesFromLog(); + assertEquals(1, traces.size()); + checkTrace(traces.values().iterator().next()); + } + + private void checkTrace(DevTrace trace) { + List<DevSpanAdapter> spans = trace.getSpans(); + assertEquals(6, spans.size()); + + DevSpanAdapter testProducer = spans.get(0); + DevSpanAdapter direct = spans.get(1); + DevSpanAdapter innerLog = spans.get(2); + DevSpanAdapter innerProcessor = spans.get(3); + DevSpanAdapter log = spans.get(4); + DevSpanAdapter innerToLog = spans.get(5); + + // Validate span completion + assertEquals("true", testProducer.getTag("isDone")); + assertEquals("true", direct.getTag("isDone")); + assertEquals("true", innerLog.getTag("isDone")); + assertEquals("true", innerProcessor.getTag("isDone")); + assertEquals("true", log.getTag("isDone")); + assertEquals("true", innerToLog.getTag("isDone")); + + // Validate same trace + assertEquals(testProducer.getTag("traceid"), direct.getTag("traceid")); + assertEquals(testProducer.getTag("traceid"), innerLog.getTag("traceid")); + assertEquals(testProducer.getTag("traceid"), innerProcessor.getTag("traceid")); + assertEquals(testProducer.getTag("traceid"), log.getTag("traceid")); + assertEquals(testProducer.getTag("traceid"), innerToLog.getTag("traceid")); + + // Validate op + assertEquals(Op.EVENT_RECEIVED.toString(), direct.getTag("op")); + assertEquals(Op.EVENT_PROCESS.toString(), innerProcessor.getTag("op")); + + // Validate hierarchy + assertNull(testProducer.getTag("parentSpan")); + assertEquals(testProducer.getTag("spanid"), direct.getTag("parentSpan")); + assertEquals(direct.getTag("spanid"), innerLog.getTag("parentSpan")); + assertEquals(direct.getTag("spanid"), innerProcessor.getTag("parentSpan")); + assertEquals(direct.getTag("spanid"), log.getTag("parentSpan")); + assertEquals(log.getTag("spanid"), innerToLog.getTag("parentSpan")); + } + + @Override + protected RoutesBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:start") + .routeId("start") + .log("A message") + .process(new Processor() { + @Override + public void process(Exchange exchange) throws Exception { + exchange.getIn().setHeader("operation", "fake"); + } + }) + .to("log:info"); + } + }; + } + +} diff --git a/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/SpanPropagationTest.java b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/SpanPropagationTest.java new file mode 100644 index 00000000000..4df5bbb70f9 --- /dev/null +++ b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/SpanPropagationTest.java @@ -0,0 +1,88 @@ +/* + * 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.telemetrydev; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SpanPropagationTest extends TelemetryDevTracerTestSupport { + + @Override + protected CamelContext createCamelContext() throws Exception { + TelemetryDevTracer tst = new TelemetryDevTracer(); + tst.setTraceFormat("json"); + CamelContext context = super.createCamelContext(); + CamelContextAware.trySetCamelContext(tst, context); + tst.init(context); + return context; + } + + @Test + void testPropagateUpstreamTraceRequest() throws IOException { + template.requestBodyAndHeader("direct:start", "sample body", + "traceparent", "123456789-123456"); + Map<String, DevTrace> traces = tracesFromLog(); + assertEquals(1, traces.size()); + checkTrace(traces.values().iterator().next()); + } + + private void checkTrace(DevTrace trace) { + List<DevSpanAdapter> spans = trace.getSpans(); + assertEquals(3, spans.size()); + DevSpanAdapter testProducer = spans.get(0); + DevSpanAdapter direct = spans.get(1); + DevSpanAdapter log = spans.get(2); + + // Validate span completion + assertEquals("true", testProducer.getTag("isDone")); + assertEquals("true", direct.getTag("isDone")); + assertEquals("true", log.getTag("isDone")); + + // Validate same trace + assertEquals("123456789", testProducer.getTag("traceid")); + assertEquals(testProducer.getTag("traceid"), direct.getTag("traceid")); + assertEquals(direct.getTag("traceid"), log.getTag("traceid")); + + // Validate hierarchy + assertEquals("123456", testProducer.getTag("parentSpan")); + assertEquals(testProducer.getTag("spanid"), direct.getTag("parentSpan")); + assertEquals(direct.getTag("spanid"), log.getTag("parentSpan")); + } + + @Override + protected RoutesBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:start") + .routeId("start") + .log("A message") + .to("log:info"); + } + }; + } + +} diff --git a/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/TelemetryDevTracerTest.java b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/TelemetryDevTracerTest.java new file mode 100644 index 00000000000..6fea7695553 --- /dev/null +++ b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/TelemetryDevTracerTest.java @@ -0,0 +1,126 @@ +/* + * 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.telemetrydev; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; +import org.apache.camel.Exchange; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.telemetry.Op; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class TelemetryDevTracerTest extends TelemetryDevTracerTestSupport { + + @Override + protected CamelContext createCamelContext() throws Exception { + TelemetryDevTracer tst = new TelemetryDevTracer(); + tst.setTraceFormat("json"); + CamelContext context = super.createCamelContext(); + CamelContextAware.trySetCamelContext(tst, context); + tst.init(context); + return context; + } + + @Test + void testRouteSingleRequest() throws IOException { + Exchange result = template.request("direct:start", null); + // Make sure the trace is propagated downstream + assertNotNull(result.getIn().getHeader("traceparent")); + Map<String, DevTrace> traces = tracesFromLog(); + assertEquals(1, traces.size()); + checkTrace(traces.values().iterator().next(), null); + } + + @Test + void testRouteMultipleRequests() throws IOException { + for (int i = 1; i <= 10; i++) { + context.createProducerTemplate().sendBody("direct:start", "Hello!"); + } + Map<String, DevTrace> traces = tracesFromLog(); + // Each trace should have a unique trace id. It is enough to assert that + // the number of elements in the map is the same of the requests to prove + // all traces have been generated uniquely. + assertEquals(10, traces.size()); + // Each trace should have the same structure + for (DevTrace trace : traces.values()) { + checkTrace(trace, "Hello!"); + } + + } + + private void checkTrace(DevTrace trace, String expectedBody) { + List<DevSpanAdapter> spans = trace.getSpans(); + assertEquals(3, spans.size()); + DevSpanAdapter testProducer = spans.get(0); + DevSpanAdapter direct = spans.get(1); + DevSpanAdapter log = spans.get(2); + + // Validate span completion + assertEquals("true", testProducer.getTag("isDone")); + assertEquals("true", direct.getTag("isDone")); + assertEquals("true", log.getTag("isDone")); + + // Validate same trace + assertEquals(testProducer.getTag("traceid"), direct.getTag("traceid")); + assertEquals(direct.getTag("traceid"), log.getTag("traceid")); + + // Validate hierarchy + assertNull(testProducer.getTag("parentSpan")); + assertEquals(testProducer.getTag("spanid"), direct.getTag("parentSpan")); + assertEquals(direct.getTag("spanid"), log.getTag("parentSpan")); + + // Validate operations + assertEquals(Op.EVENT_SENT.toString(), testProducer.getTag("op")); + assertEquals(Op.EVENT_RECEIVED.toString(), direct.getTag("op")); + + // Validate message logging + assertEquals("A message", direct.getLogEntries().get(0).getFields().get("message")); + if (expectedBody == null) { + assertEquals( + "Exchange[ExchangePattern: InOut, BodyType: null, Body: [Body is null]]", + log.getLogEntries().get(0).getFields().get("message")); + } else { + assertEquals( + "Exchange[ExchangePattern: InOnly, BodyType: String, Body: " + expectedBody + "]", + log.getLogEntries().get(0).getFields().get("message")); + } + + } + + @Override + protected RoutesBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:start") + .routeId("start") + .log("A message") + .to("log:info"); + } + }; + } + +} diff --git a/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/TelemetryDevTracerTestSupport.java b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/TelemetryDevTracerTestSupport.java new file mode 100644 index 00000000000..7ec4bdad962 --- /dev/null +++ b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/TelemetryDevTracerTestSupport.java @@ -0,0 +1,84 @@ +/* + * 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.telemetrydev; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.camel.telemetry.Op; +import org.apache.camel.telemetry.TagConstants; +import org.apache.camel.test.junit5.ExchangeTestSupport; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.appender.RollingFileAppender; +import org.junit.jupiter.api.AfterEach; + +public class TelemetryDevTracerTestSupport extends ExchangeTestSupport { + + private final ObjectMapper mapper = new ObjectMapper(); + + protected Map<String, DevTrace> tracesFromLog() throws IOException { + Map<String, DevTrace> answer = new HashMap<>(); + Path path = Paths.get("target/telemetry-traces.log"); + List<String> allTraces = Files.readAllLines(path); + for (String trace : allTraces) { + DevTrace st = mapper.readValue(trace, DevTrace.class); + if (answer.get(st.getTraceId()) != null) { + // Multiple traces exists for this traceId: this may happen + // when we deal with async events (like wiretap and the like) + DevTrace existing = answer.get(st.getTraceId()); + List<DevSpanAdapter> mergedSpans = st.getSpans(); + mergedSpans.addAll(existing.getSpans()); + st = new DevTrace(st.getTraceId(), mergedSpans); + } + answer.put(st.getTraceId(), st); + } + + return answer; + } + + /* + * This one is required to rollover the log traces database file and make sure each test has its own + * set of fresh data. + */ + @AfterEach + public synchronized void clearLogTraces() throws IOException { + final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); + RollingFileAppender appender = (RollingFileAppender) ctx.getConfiguration().getAppenders().get("file2"); + if (appender != null) { + appender.getManager().rollover(); + } + } + + protected static DevSpanAdapter getSpan(List<DevSpanAdapter> trace, String uri, Op op) { + for (DevSpanAdapter span : trace) { + if (span.getTag("camel.uri") != null && span.getTag("camel.uri").equals(uri)) { + if (span.getTag(TagConstants.OP).equals(op.toString())) { + return span; + } + } + } + throw new IllegalArgumentException("Trying to get a non existing span!"); + } + +} diff --git a/components/camel-telemetry/src/test/resources/log4j2.properties b/components/camel-telemetry-dev/src/test/resources/log4j2.properties similarity index 79% copy from components/camel-telemetry/src/test/resources/log4j2.properties copy to components/camel-telemetry-dev/src/test/resources/log4j2.properties index 5e292bb50f7..bd5fda232e0 100644 --- a/components/camel-telemetry/src/test/resources/log4j2.properties +++ b/components/camel-telemetry-dev/src/test/resources/log4j2.properties @@ -19,9 +19,6 @@ appender.console.name = console appender.console.layout.type = PatternLayout appender.console.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n -## MDC layout -### appender.console.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %X{camel.exchangeId} %X{camel.breadcrumbId} - %m%n - appender.file.type = File appender.file.name = file appender.file.fileName = target/camel-telemetry-test.log @@ -29,14 +26,16 @@ appender.file.append = true appender.file.layout.type = PatternLayout appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n -appender.file2.type = File +appender.file2.type=RollingFile +appender.file2.filePattern = target/telemetry-traces-%i.log appender.file2.name = file2 -appender.file2.fileName = target/custom-logger-test.log +appender.file2.fileName = target/telemetry-traces.log appender.file2.append = false appender.file2.layout.type = PatternLayout -appender.file2.layout.pattern = %-5p %c{1} %m%n +appender.file2.layout.pattern = %m%n +appender.file2.policies.type = Policies -logger.customlogger.name = org.apache.camel.customlogger +logger.customlogger.name = LOG_TRACE logger.customlogger.level = INFO logger.customlogger.appenderRef.file2.ref = file2 @@ -46,7 +45,4 @@ logger.file-cluster.level = INFO rootLogger.level = INFO rootLogger.appenderRef.file.ref = file -#rootLogger.appenderRef.console.ref = console -#logger.camel-core.name = org.apache.camel -#logger.camel-core.level = INFO diff --git a/components/camel-telemetry/pom.xml b/components/camel-telemetry/pom.xml index 6ec2bcf8403..adf8b12c8bf 100644 --- a/components/camel-telemetry/pom.xml +++ b/components/camel-telemetry/pom.xml @@ -30,6 +30,7 @@ <properties> <firstVersion>4.11.0</firstVersion> <label>monitoring,microservice</label> + <supportLevel>Preview</supportLevel> </properties> <artifactId>camel-telemetry</artifactId> diff --git a/components/camel-telemetry/src/main/docs/telemetry.adoc b/components/camel-telemetry/src/main/docs/telemetry.adoc index f1b5c6a4fb8..a40fa82196a 100644 --- a/components/camel-telemetry/src/main/docs/telemetry.adoc +++ b/components/camel-telemetry/src/main/docs/telemetry.adoc @@ -9,7 +9,9 @@ *Since Camel {since}* -This module is a common interface and API for distributed tracing/telemetry. It is not intended to be used directly by end users. It will be eventually implemented by some concrete component. +This module is a common interface and API for distributed tracing/telemetry. It is not intended to be used directly by end users. This component is implemented concretely in following dependencies: + +* camel-telemetry-dev Tracing an application in a distributed deployment is an important feature supported by Camel. As there are plenty of potential telemetry implementation out there we have created an abstract component whose goal is to define the structure and lifecycle of a trace from Camel point of view. This component acts as a trace manager, making sure that regardless of the concrete technology used, the traces are consistently treated by Camel applications. diff --git a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/TraceProcessorsInterceptStrategy.java b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/TraceProcessorsInterceptStrategy.java index ba04e387e59..b4b2aad5563 100644 --- a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/TraceProcessorsInterceptStrategy.java +++ b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/TraceProcessorsInterceptStrategy.java @@ -21,6 +21,7 @@ import org.apache.camel.Exchange; import org.apache.camel.NamedNode; import org.apache.camel.Processor; import org.apache.camel.spi.InterceptStrategy; +import org.apache.camel.support.processor.DelegateAsyncProcessor; public class TraceProcessorsInterceptStrategy implements InterceptStrategy { @@ -35,7 +36,8 @@ public class TraceProcessorsInterceptStrategy implements InterceptStrategy { CamelContext camelContext, NamedNode processorDefinition, Processor target, Processor nextTarget) throws Exception { - return new TraceProcessor(target, processorDefinition); + //return new TraceProcessor(target, processorDefinition); + return new DelegateAsyncProcessor(new TraceProcessor(target, processorDefinition)); } private class TraceProcessor implements Processor { diff --git a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java index 4f54dd8a5c3..7fae1a53d27 100644 --- a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java +++ b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java @@ -93,6 +93,10 @@ public abstract class Tracer extends ServiceSupport implements CamelTracingServi this.traceProcessors = traceProcessors; } + public SpanLifecycleManager getSpanLifecycleManager() { + return this.spanLifecycleManager; + } + public void setSpanLifecycleManager(SpanLifecycleManager spanLifecycleManager) { this.spanLifecycleManager = spanLifecycleManager; } diff --git a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncCXFTest.java b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncCXFTest.java index a828aae2740..8cab48604ae 100644 --- a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncCXFTest.java +++ b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncCXFTest.java @@ -59,12 +59,11 @@ public class AsyncCXFTest extends ExchangeTestSupport { int j = 10; MockEndpoint mock = getMockEndpoint("mock:end"); mock.expectedMessageCount(j); + mock.setAssertPeriod(5000); for (int i = 0; i < j; i++) { context.createProducerTemplate().sendBody("direct:start", "Hello!"); } - // The wiretapped endpoint is delaying at least 2 seconds for each request so - // we must wait for the overall execution time (i*2 seconds) to complete before checking the results - mock.assertIsSatisfied(j * 2500); + mock.assertIsSatisfied(1000); Map<String, MockTrace> traces = mockTracer.traces(); // Each trace should have a unique trace id. It is enough to assert that // the number of elements in the map is the same of the requests to prove diff --git a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncDirectTest.java b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncDirectTest.java index 6fac52a24ff..cebe375f01b 100644 --- a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncDirectTest.java +++ b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncDirectTest.java @@ -51,12 +51,11 @@ public class AsyncDirectTest extends ExchangeTestSupport { int j = 10; MockEndpoint mock = getMockEndpoint("mock:end"); mock.expectedMessageCount(j); + mock.setAssertPeriod(5000); for (int i = 0; i < j; i++) { context.createProducerTemplate().sendBody("direct:start", "Hello!"); } - // The wiretapped endpoint is delaying at least 2 seconds for each request so - // we must wait for the overall execution time (i*2 seconds) to complete before checking the results - mock.assertIsSatisfied(j * 2500); + mock.assertIsSatisfied(1000); Map<String, MockTrace> traces = mockTracer.traces(); // Each trace should have a unique trace id. It is enough to assert that // the number of elements in the map is the same of the requests to prove diff --git a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncWiretapTest.java b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncWiretapTest.java index 1c8f2a0fe03..b63ef386d16 100644 --- a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncWiretapTest.java +++ b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/AsyncWiretapTest.java @@ -56,12 +56,13 @@ public class AsyncWiretapTest extends ExchangeTestSupport { int j = 10; MockEndpoint mock = getMockEndpoint("mock:end"); mock.expectedMessageCount(j); + mock.setAssertPeriod(5000); for (int i = 0; i < j; i++) { context.createProducerTemplate().sendBody("direct:start", "Hello!"); } - // The wiretapped endpoint is delaying at least 2 seconds for each request so - // we must wait for the overall execution time (i*2 seconds) to complete before checking the results - mock.assertIsSatisfied(j * 2500); + mock.assertIsSatisfied(1000); + // We must wait a safe time to let the context completing the async writing to the log trace. + Thread.sleep(10000); Map<String, MockTrace> traces = mockTracer.traces(); // Each trace should have a unique trace id. It is enough to assert that // the number of elements in the map is the same of the requests to prove diff --git a/components/camel-telemetry/src/test/resources/log4j2.properties b/components/camel-telemetry/src/test/resources/log4j2.properties index 5e292bb50f7..c9d7df800c7 100644 --- a/components/camel-telemetry/src/test/resources/log4j2.properties +++ b/components/camel-telemetry/src/test/resources/log4j2.properties @@ -19,9 +19,6 @@ appender.console.name = console appender.console.layout.type = PatternLayout appender.console.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n -## MDC layout -### appender.console.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %X{camel.exchangeId} %X{camel.breadcrumbId} - %m%n - appender.file.type = File appender.file.name = file appender.file.fileName = target/camel-telemetry-test.log @@ -29,24 +26,5 @@ appender.file.append = true appender.file.layout.type = PatternLayout appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n -appender.file2.type = File -appender.file2.name = file2 -appender.file2.fileName = target/custom-logger-test.log -appender.file2.append = false -appender.file2.layout.type = PatternLayout -appender.file2.layout.pattern = %-5p %c{1} %m%n - -logger.customlogger.name = org.apache.camel.customlogger -logger.customlogger.level = INFO -logger.customlogger.appenderRef.file2.ref = file2 - -logger.file-cluster.name = org.apache.camel.component.file.cluster -logger.file-cluster.level = INFO - rootLogger.level = INFO - rootLogger.appenderRef.file.ref = file -#rootLogger.appenderRef.console.ref = console - -#logger.camel-core.name = org.apache.camel -#logger.camel-core.level = INFO diff --git a/components/pom.xml b/components/pom.xml index 3c2026793c5..1c9e053f9c0 100644 --- a/components/pom.xml +++ b/components/pom.xml @@ -307,6 +307,7 @@ <module>camel-tahu</module> <module>camel-tarfile</module> <module>camel-telegram</module> + <module>camel-telemetry-dev</module> <module>camel-threadpoolfactory-vertx</module> <module>camel-thrift</module> <module>camel-thymeleaf</module> diff --git a/core/camel-main/src/generated/java/org/apache/camel/main/TelemetryDevConfigurationPropertiesConfigurer.java b/core/camel-main/src/generated/java/org/apache/camel/main/TelemetryDevConfigurationPropertiesConfigurer.java new file mode 100644 index 00000000000..0c2cbf1ebd7 --- /dev/null +++ b/core/camel-main/src/generated/java/org/apache/camel/main/TelemetryDevConfigurationPropertiesConfigurer.java @@ -0,0 +1,81 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.main; + +import javax.annotation.processing.Generated; +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.TelemetryDevConfigurationProperties; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@Generated("org.apache.camel.maven.packaging.GenerateConfigurerMojo") +@SuppressWarnings("unchecked") +public class TelemetryDevConfigurationPropertiesConfigurer extends org.apache.camel.support.component.PropertyConfigurerSupport implements GeneratedPropertyConfigurer, ExtendedPropertyConfigurerGetter { + + private static final Map<String, Object> ALL_OPTIONS; + static { + Map<String, Object> map = new CaseInsensitiveMap(); + map.put("Enabled", boolean.class); + map.put("ExcludePatterns", java.lang.String.class); + map.put("TraceFormat", java.lang.String.class); + map.put("TraceProcessors", boolean.class); + ALL_OPTIONS = map; + } + + @Override + public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { + org.apache.camel.main.TelemetryDevConfigurationProperties target = (org.apache.camel.main.TelemetryDevConfigurationProperties) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "enabled": target.setEnabled(property(camelContext, boolean.class, value)); return true; + case "excludepatterns": + case "excludePatterns": target.setExcludePatterns(property(camelContext, java.lang.String.class, value)); return true; + case "traceformat": + case "traceFormat": target.setTraceFormat(property(camelContext, java.lang.String.class, value)); return true; + case "traceprocessors": + case "traceProcessors": target.setTraceProcessors(property(camelContext, boolean.class, value)); return true; + default: return false; + } + } + + @Override + public Map<String, Object> getAllOptions(Object target) { + return ALL_OPTIONS; + } + + @Override + public Class<?> getOptionType(String name, boolean ignoreCase) { + switch (ignoreCase ? name.toLowerCase() : name) { + case "enabled": return boolean.class; + case "excludepatterns": + case "excludePatterns": return java.lang.String.class; + case "traceformat": + case "traceFormat": return java.lang.String.class; + case "traceprocessors": + case "traceProcessors": return boolean.class; + default: return null; + } + } + + @Override + public Object getOptionValue(Object obj, String name, boolean ignoreCase) { + org.apache.camel.main.TelemetryDevConfigurationProperties target = (org.apache.camel.main.TelemetryDevConfigurationProperties) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "enabled": return target.isEnabled(); + case "excludepatterns": + case "excludePatterns": return target.getExcludePatterns(); + case "traceformat": + case "traceFormat": return target.getTraceFormat(); + case "traceprocessors": + case "traceProcessors": return target.isTraceProcessors(); + default: return null; + } + } +} + diff --git a/core/camel-main/src/generated/java/org/apache/camel/main/TelemetrySimpleConfigurationPropertiesConfigurer.java b/core/camel-main/src/generated/java/org/apache/camel/main/TelemetrySimpleConfigurationPropertiesConfigurer.java new file mode 100644 index 00000000000..57292947930 --- /dev/null +++ b/core/camel-main/src/generated/java/org/apache/camel/main/TelemetrySimpleConfigurationPropertiesConfigurer.java @@ -0,0 +1,81 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.main; + +import javax.annotation.processing.Generated; +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.TelemetryDevConfigurationProperties; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@Generated("org.apache.camel.maven.packaging.GenerateConfigurerMojo") +@SuppressWarnings("unchecked") +public class TelemetrySimpleConfigurationPropertiesConfigurer extends org.apache.camel.support.component.PropertyConfigurerSupport implements GeneratedPropertyConfigurer, ExtendedPropertyConfigurerGetter { + + private static final Map<String, Object> ALL_OPTIONS; + static { + Map<String, Object> map = new CaseInsensitiveMap(); + map.put("Enabled", boolean.class); + map.put("ExcludePatterns", java.lang.String.class); + map.put("TraceFormat", java.lang.String.class); + map.put("TraceProcessors", boolean.class); + ALL_OPTIONS = map; + } + + @Override + public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { + org.apache.camel.main.TelemetryDevConfigurationProperties target = (org.apache.camel.main.TelemetryDevConfigurationProperties) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "enabled": target.setEnabled(property(camelContext, boolean.class, value)); return true; + case "excludepatterns": + case "excludePatterns": target.setExcludePatterns(property(camelContext, java.lang.String.class, value)); return true; + case "traceformat": + case "traceFormat": target.setTraceFormat(property(camelContext, java.lang.String.class, value)); return true; + case "traceprocessors": + case "traceProcessors": target.setTraceProcessors(property(camelContext, boolean.class, value)); return true; + default: return false; + } + } + + @Override + public Map<String, Object> getAllOptions(Object target) { + return ALL_OPTIONS; + } + + @Override + public Class<?> getOptionType(String name, boolean ignoreCase) { + switch (ignoreCase ? name.toLowerCase() : name) { + case "enabled": return boolean.class; + case "excludepatterns": + case "excludePatterns": return java.lang.String.class; + case "traceformat": + case "traceFormat": return java.lang.String.class; + case "traceprocessors": + case "traceProcessors": return boolean.class; + default: return null; + } + } + + @Override + public Object getOptionValue(Object obj, String name, boolean ignoreCase) { + org.apache.camel.main.TelemetryDevConfigurationProperties target = (org.apache.camel.main.TelemetryDevConfigurationProperties) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "enabled": return target.isEnabled(); + case "excludepatterns": + case "excludePatterns": return target.getExcludePatterns(); + case "traceformat": + case "traceFormat": return target.getTraceFormat(); + case "traceprocessors": + case "traceProcessors": return target.isTraceProcessors(); + 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 71a1bfd7be6..dfd524df0fa 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 @@ -17,6 +17,7 @@ { "name": "camel.vault.kubernetescm", "description": "Camel Kubernetes Configmaps Vault configurations", "sourceType": "org.apache.camel.vault.KubernetesConfigMapVaultConfiguration" }, { "name": "camel.vault.hashicorp", "description": "Camel Hashicorp Vault configurations", "sourceType": "org.apache.camel.vault.HashicorpVaultConfiguration" }, { "name": "camel.opentelemetry", "description": "Camel OpenTelemetry configurations", "sourceType": "org.apache.camel.main.OtelConfigurationProperties" }, + { "name": "camel.telemetryDev", "description": "Camel Telemetry Dev configurations", "sourceType": "org.apache.camel.main.TelemetryDevConfigurationProperties" }, { "name": "camel.metrics", "description": "Camel Micrometer Metrics configurations", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties" }, { "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" }, @@ -318,6 +319,10 @@ { "name": "camel.startupcondition.interval", "description": "Interval in millis between checking conditions.", "sourceType": "org.apache.camel.main.StartupConditionConfigurationProperties", "type": "integer", "javaType": "int", "defaultValue": 500 }, { "name": "camel.startupcondition.onTimeout", "description": "What action, to do on timeout. fail = do not startup, and throw an exception causing camel to fail stop = do not startup, and stop camel ignore = log a WARN and continue to startup", "sourceType": "org.apache.camel.main.StartupConditionConfigurationProperties", "type": "string", "javaType": "java.lang.String", "defaultValue": "stop", "enum": [ "fail", "stop", "ignore" ] }, { "name": "camel.startupcondition.timeout", "description": "Total timeout (in millis) for all startup conditions.", "sourceType": "org.apache.camel.main.StartupConditionConfigurationProperties", "type": "integer", "javaType": "int", "defaultValue": 20000 }, + { "name": "camel.telemetryDev.enabled", "description": "To enable TelemetryDev", "sourceType": "org.apache.camel.main.TelemetryDevConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, + { "name": "camel.telemetryDev.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.TelemetryDevConfigurationProperties", "type": "string", "javaType": "java.lang.String" }, + { "name": "camel.telemetryDev.traceFormat", "description": "The output format for traces.", "sourceType": "org.apache.camel.main.TelemetryDevConfigurationProperties", "type": "string", "javaType": "java.lang.String" }, + { "name": "camel.telemetryDev.traceProcessors", "description": "Setting this to true will create new TelemetrySimple Spans for each Camel Processors. Use the excludePattern property to filter out Processors.", "sourceType": "org.apache.camel.main.TelemetryDevConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, { "name": "camel.threadpool.allowCoreThreadTimeOut", "description": "Sets default whether to allow core threads to timeout", "sourceType": "org.apache.camel.main.ThreadPoolConfigurationProperties", "type": "boolean", "javaType": "java.lang.Boolean", "defaultValue": "false" }, { "name": "camel.threadpool.config", "description": "Adds a configuration for a specific thread pool profile (inherits default values)", "sourceType": "org.apache.camel.main.ThreadPoolConfigurationProperties", "type": "object", "javaType": "java.util.Map" }, { "name": "camel.threadpool.keepAliveTime", "description": "Sets the default keep alive time for inactive threads", "sourceType": "org.apache.camel.main.ThreadPoolConfigurationProperties", "type": "integer", "javaType": "java.lang.Long" }, diff --git a/core/camel-main/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.main.TelemetryDevConfigurationProperties b/core/camel-main/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.main.TelemetryDevConfigurationProperties new file mode 100644 index 00000000000..7e66d483cb9 --- /dev/null +++ b/core/camel-main/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.main.TelemetryDevConfigurationProperties @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.main.TelemetryDevConfigurationPropertiesConfigurer diff --git a/core/camel-main/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.main.TelemetrySimpleConfigurationProperties b/core/camel-main/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.main.TelemetrySimpleConfigurationProperties new file mode 100644 index 00000000000..af4c3231824 --- /dev/null +++ b/core/camel-main/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.main.TelemetrySimpleConfigurationProperties @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.main.TelemetrySimpleConfigurationPropertiesConfigurer diff --git a/core/camel-main/src/main/docs/main.adoc b/core/camel-main/src/main/docs/main.adoc index 076867005fc..14a29bbd72f 100644 --- a/core/camel-main/src/main/docs/main.adoc +++ b/core/camel-main/src/main/docs/main.adoc @@ -481,6 +481,19 @@ The camel.opentelemetry supports 5 options, which are listed below. |=== +=== Camel Telemetry Dev configurations +The camel.telemetryDev supports 4 options, which are listed below. + +[width="100%",cols="2,5,^1,2",options="header"] +|=== +| Name | Description | Default | Type +| *camel.telemetryDev.enabled* | To enable TelemetryDev | false | boolean +| *camel.telemetryDev.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 +| *camel.telemetryDev.traceFormat* | The output format for traces. | | String +| *camel.telemetryDev.trace{zwsp}Processors* | Setting this to true will create new TelemetrySimple Spans for each Camel Processors. Use the excludePattern property to filter out Processors. | false | boolean +|=== + + === Camel Micrometer Metrics configurations The camel.metrics 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 57ac46692d9..c409ca4f2cc 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 @@ -135,7 +135,7 @@ public abstract class BaseMainSupport extends BaseService { private static final String[] GROUP_PREFIXES = new String[] { "camel.context.", "camel.resilience4j.", "camel.faulttolerance.", "camel.rest.", "camel.vault.", "camel.threadpool.", "camel.health.", - "camel.lra.", "camel.opentelemetry.", "camel.metrics.", "camel.routeTemplate", + "camel.lra.", "camel.opentelemetry.", "camel.telemetryDev.", "camel.metrics.", "camel.routeTemplate", "camel.devConsole.", "camel.variable.", "camel.beans.", "camel.globalOptions.", "camel.server.", "camel.ssl.", "camel.debug.", "camel.trace.", "camel.routeController." }; @@ -1244,6 +1244,7 @@ public abstract class BaseMainSupport extends BaseService { OrderedLocationProperties healthProperties = new OrderedLocationProperties(); OrderedLocationProperties lraProperties = new OrderedLocationProperties(); OrderedLocationProperties otelProperties = new OrderedLocationProperties(); + OrderedLocationProperties telemetryDevProperties = new OrderedLocationProperties(); OrderedLocationProperties metricsProperties = new OrderedLocationProperties(); OrderedLocationProperties routeTemplateProperties = new OrderedLocationProperties(); OrderedLocationProperties variableProperties = new OrderedLocationProperties(); @@ -1306,6 +1307,12 @@ public abstract class BaseMainSupport extends BaseService { String option = key.substring(20); validateOptionAndValue(key, option, value); otelProperties.put(loc, optionKey(option), value); + } else if (startsWithIgnoreCase(key, "camel.telemetryDev.")) { + // grab the value + String value = prop.getProperty(key); + String option = key.substring(19); + validateOptionAndValue(key, option, value); + telemetryDevProperties.put(loc, optionKey(option), value); } else if (startsWithIgnoreCase(key, "camel.metrics.")) { // grab the value String value = prop.getProperty(key); @@ -1441,6 +1448,14 @@ public abstract class BaseMainSupport extends BaseService { LOG.debug("Auto-configuring OpenTelemetry from loaded properties: {}", otelProperties.size()); setOtelProperties(camelContext, otelProperties, mainConfigurationProperties.isAutoConfigurationFailFast(), autoConfiguredProperties); + } else { + // Attempt to fallback to Telemetry simple only if no other tracing service is found + if (!telemetryDevProperties.isEmpty() || mainConfigurationProperties.hasTelemetryDevConfiguration()) { + LOG.debug("Auto-configuring TelemetryDev from loaded properties: {}", telemetryDevProperties.size()); + setTelemetryDevProperties(camelContext, telemetryDevProperties, + mainConfigurationProperties.isAutoConfigurationFailFast(), + autoConfiguredProperties); + } } if (!metricsProperties.isEmpty() || mainConfigurationProperties.hasMetricsConfiguration()) { LOG.debug("Auto-configuring Micrometer metrics from loaded properties: {}", metricsProperties.size()); @@ -1558,6 +1573,11 @@ public abstract class BaseMainSupport extends BaseService { LOG.warn("Property not auto-configured: camel.opentelemetry.{}={}", k, v); }); } + if (!telemetryDevProperties.isEmpty()) { + telemetryDevProperties.forEach((k, v) -> { + LOG.warn("Property not auto-configured: camel.telemetryDev.{}={}", k, v); + }); + } if (!httpServerProperties.isEmpty()) { httpServerProperties.forEach((k, v) -> { LOG.warn("Property not auto-configured: camel.server.{}={}", k, v); @@ -1736,6 +1756,29 @@ public abstract class BaseMainSupport extends BaseService { } } + private void setTelemetryDevProperties( + CamelContext camelContext, OrderedLocationProperties telemetryDevProperties, + boolean failIfNotSet, OrderedLocationProperties autoConfiguredProperties) + throws Exception { + + String loc = telemetryDevProperties.getLocation("enabled"); + Object obj = telemetryDevProperties.remove("enabled"); + if (ObjectHelper.isNotEmpty(obj)) { + autoConfiguredProperties.put(loc, "camel.telemetryDev.enabled", obj.toString()); + } + boolean enabled = obj != null ? CamelContextHelper.parseBoolean(camelContext, obj.toString()) : true; + if (enabled) { + CamelTracingService telemetryDev = resolveTelemetryDevService(camelContext); + setPropertiesOnTarget(camelContext, telemetryDev, telemetryDevProperties, "camel.telemetryDev.", + failIfNotSet, true, + autoConfiguredProperties); + if (camelContext.hasService(CamelTracingService.class) == null) { + // add as service so tracing can be active + camelContext.addService(telemetryDev, true, true); + } + } + } + private void setMetricsProperties( CamelContext camelContext, OrderedLocationProperties metricsProperties, boolean failIfNotSet, OrderedLocationProperties autoConfiguredProperties) @@ -2580,6 +2623,20 @@ public abstract class BaseMainSupport extends BaseService { return answer; } + private static CamelTracingService resolveTelemetryDevService(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("telemetry-dev-tracer", CamelTracingService.class) + .orElseThrow(() -> new IllegalArgumentException( + "Cannot find TelemetryDevTracer on classpath. Add camel-telemetry-dev to classpath.")); + } + return answer; + } + private static CamelMetricsService resolveMicrometerService(CamelContext camelContext) throws Exception { CamelMetricsService answer = camelContext.hasService(CamelMetricsService.class); if (answer == null) { 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 706eb369501..a83912e9060 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 @@ -60,6 +60,7 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties< private StartupConditionConfigurationProperties startupConditionConfigurationProperties; private LraConfigurationProperties lraConfigurationProperties; private OtelConfigurationProperties otelConfigurationProperties; + private TelemetryDevConfigurationProperties telemetryDevConfigurationProperties; private MetricsConfigurationProperties metricsConfigurationProperties; private ThreadPoolConfigurationProperties threadPoolProperties; private Resilience4jConfigurationProperties resilience4jConfigurationProperties; @@ -208,6 +209,13 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties< return otelConfigurationProperties != null; } + /** + * Whether there has been any TelemetryDev configuration specified + */ + public boolean hasTelemetryDevConfiguration() { + return telemetryDevConfigurationProperties != null; + } + /** * To configure Micrometer metrics. */ diff --git a/core/camel-main/src/main/java/org/apache/camel/main/TelemetryDevConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/TelemetryDevConfigurationProperties.java new file mode 100644 index 00000000000..5c01f4a0f12 --- /dev/null +++ b/core/camel-main/src/main/java/org/apache/camel/main/TelemetryDevConfigurationProperties.java @@ -0,0 +1,114 @@ +/* + * 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; + +/** + * Global configuration for TelemetryDev component + */ +@Configurer(extended = true) +public class TelemetryDevConfigurationProperties implements BootstrapCloseable { + + private MainConfigurationProperties parent; + + private boolean enabled; + private String excludePatterns; + private boolean traceProcessors; + private String traceFormat; + + public TelemetryDevConfigurationProperties(MainConfigurationProperties parent) { + this.parent = parent; + } + + public MainConfigurationProperties end() { + return parent; + } + + @Override + public void close() { + parent = null; + } + + public boolean isEnabled() { + return enabled; + } + + /** + * To enable TelemetryDev + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + 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; + } + + public boolean isTraceProcessors() { + return traceProcessors; + } + + /** + * Setting this to true will create new TelemetrySimple Spans for each Camel Processors. Use the excludePattern + * property to filter out Processors. + */ + public void setTraceProcessors(boolean traceProcessors) { + this.traceProcessors = traceProcessors; + } + + public String getTraceFormat() { + return traceFormat; + } + + /** + * The output format for traces. + */ + public void setTraceFormat(String traceFormat) { + this.traceFormat = traceFormat; + } + + public TelemetryDevConfigurationProperties withEnabled(boolean enabled) { + this.enabled = enabled; + return this; + } + + public TelemetryDevConfigurationProperties withExcludePatterns(String excludePatterns) { + this.excludePatterns = excludePatterns; + return this; + } + + public TelemetryDevConfigurationProperties withTraceProcessors(boolean traceProcessors) { + this.traceProcessors = traceProcessors; + return this; + } + + public TelemetryDevConfigurationProperties withTraceFromat(String traceFormat) { + this.traceFormat = traceFormat; + return this; + } + +} diff --git a/docs/components/modules/others/examples/json/telemetry-dev.json b/docs/components/modules/others/examples/json/telemetry-dev.json new file mode 120000 index 00000000000..76fc96f9455 --- /dev/null +++ b/docs/components/modules/others/examples/json/telemetry-dev.json @@ -0,0 +1 @@ +../../../../../../components/camel-telemetry-dev/src/generated/resources/telemetry-dev.json \ No newline at end of file diff --git a/docs/components/modules/others/nav.adoc b/docs/components/modules/others/nav.adoc index 79bf350173f..f33e430a10d 100644 --- a/docs/components/modules/others/nav.adoc +++ b/docs/components/modules/others/nav.adoc @@ -52,6 +52,7 @@ ** xref:spring-xml.adoc[Spring XML] ** xref:springdoc.adoc[Springdoc] ** xref:telemetry.adoc[Telemetry] +** xref:telemetry-dev.adoc[Temetry Dev] ** xref:test-junit5.adoc[Test JUnit5] ** xref:test-main-junit5.adoc[Test Main JUnit5] ** xref:test-spring-junit5.adoc[Test Spring JUnit5] diff --git a/docs/components/modules/others/pages/telemetry-dev.adoc b/docs/components/modules/others/pages/telemetry-dev.adoc new file mode 120000 index 00000000000..8e0905f9ad6 --- /dev/null +++ b/docs/components/modules/others/pages/telemetry-dev.adoc @@ -0,0 +1 @@ +../../../../../components/camel-telemetry-dev/src/main/docs/telemetry-dev.adoc \ No newline at end of file diff --git a/parent/pom.xml b/parent/pom.xml index e064e0fb996..76880db8f82 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -2440,6 +2440,11 @@ <artifactId>camel-telemetry</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-telemetry-dev</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-tensorflow-serving</artifactId> 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 3c94d764940..c21082592c6 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 @@ -219,6 +219,8 @@ public class PrepareCamelMainMojo extends AbstractGeneratorMojo { prefix = "camel.lra."; } else if (file.getName().contains("Otel")) { prefix = "camel.opentelemetry."; + } else if (file.getName().contains("TelemetryDev")) { + prefix = "camel.telemetryDev."; } else if (file.getName().contains("Metrics")) { prefix = "camel.metrics."; } else if (file.getName().contains("HttpServer")) { @@ -393,6 +395,9 @@ public class PrepareCamelMainMojo extends AbstractGeneratorMojo { model.getGroups().add(new MainGroupModel( "camel.opentelemetry", "Camel OpenTelemetry configurations", "org.apache.camel.main.OtelConfigurationProperties")); + model.getGroups().add(new MainGroupModel( + "camel.telemetryDev", "Camel Telemetry Dev configurations", + "org.apache.camel.main.TelemetryDevConfigurationProperties")); model.getGroups().add(new MainGroupModel( "camel.metrics", "Camel Micrometer Metrics configurations", "org.apache.camel.main.MetricsConfigurationProperties"));