apache camel 2.20.x instrumentation (#1397)

* apache camel 2.20.x instrumentation

* removed instrumentation advice from helper classes

* code review #1

* code review #2 / experimental server name update

* code review changes / new tests, improved direct, new license header

* rebase changes

* code review changes

* code review changes, REST tests added

* changes after rebase to newest master

* code review changes

* code review

* code review

* rebase to master
This commit is contained in:
Jakub Wach 2020-11-03 10:42:33 +01:00 committed by GitHub
parent a528daee52
commit 049358e81b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 2370 additions and 1 deletions

View File

@ -3,7 +3,7 @@ apply plugin: 'com.diffplug.spotless'
spotless {
java {
googleJavaFormat()
licenseHeaderFile rootProject.file('gradle/enforcement/spotless.license.java'), '(package|import|public)'
licenseHeaderFile rootProject.file('gradle/enforcement/spotless.license.java'), '(package|import|public|// Includes work from:)'
target 'src/**/*.java'
}
groovy {

View File

@ -0,0 +1,35 @@
ext {
minJavaVersionForTests = JavaVersion.VERSION_1_8
}
apply from: "$rootDir/gradle/instrumentation.gradle"
muzzle {
pass {
group = "org.apache.camel"
module = "camel-core"
versions = "[2.20.1,3)"
}
}
dependencies {
library group: 'org.apache.camel', name: 'camel-core', version: '2.20.1'
testImplementation project(':instrumentation:apache-httpclient:apache-httpclient-2.0')
testImplementation project(':instrumentation:servlet:servlet-3.0')
testImplementation group: 'org.spockframework', name: 'spock-spring', version: "$versions.spock"
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '1.5.17.RELEASE'
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter', version: '1.5.17.RELEASE'
testImplementation group: 'org.apache.camel', name: 'camel-spring-boot-starter', version: '2.20.1'
testImplementation group: 'org.apache.camel', name: 'camel-jetty-starter', version: '2.20.1'
testImplementation group: 'org.apache.camel', name: 'camel-http-starter', version: '2.20.1'
testImplementation group: 'org.apache.camel', name: 'camel-jaxb-starter', version: '2.20.1'
testImplementation group: 'org.apache.camel', name: 'camel-undertow', version: '2.20.1'
testImplementation 'javax.xml.bind:jaxb-api:2.3.1'
latestDepTestLibrary group: 'org.apache.camel', name: 'camel-core', version: '2.+'
}

View File

@ -0,0 +1,121 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import org.apache.camel.Exchange;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Utility class for managing active spans as a stack associated with an exchange. */
class ActiveSpanManager {
private static final String ACTIVE_SPAN_PROPERTY = "OpenTelemetry.activeSpan";
private static final Logger LOG = LoggerFactory.getLogger(ActiveSpanManager.class);
private ActiveSpanManager() {}
public static Span getSpan(Exchange exchange) {
SpanWithScope spanWithScope = exchange.getProperty(ACTIVE_SPAN_PROPERTY, SpanWithScope.class);
if (spanWithScope != null) {
return spanWithScope.getSpan();
}
return null;
}
/**
* This method activates the supplied span for the supplied exchange. If an existing span is found
* for the exchange, this will be pushed onto a stack.
*
* @param exchange The exchange
* @param span The span
*/
public static void activate(Exchange exchange, Span span) {
SpanWithScope parent = exchange.getProperty(ACTIVE_SPAN_PROPERTY, SpanWithScope.class);
SpanWithScope spanWithScope = SpanWithScope.activate(span, parent);
exchange.setProperty(ACTIVE_SPAN_PROPERTY, spanWithScope);
if (LOG.isTraceEnabled()) {
LOG.trace("Activated a span: " + spanWithScope);
}
}
/**
* This method deactivates an existing active span associated with the supplied exchange. Once
* deactivated, if a parent span is found associated with the stack for the exchange, it will be
* restored as the current span for that exchange.
*
* @param exchange The exchange
*/
public static void deactivate(Exchange exchange) {
SpanWithScope spanWithScope = exchange.getProperty(ACTIVE_SPAN_PROPERTY, SpanWithScope.class);
if (spanWithScope != null) {
spanWithScope.deactivate();
exchange.setProperty(ACTIVE_SPAN_PROPERTY, spanWithScope.getParent());
if (LOG.isTraceEnabled()) {
LOG.trace("Deactivated span: " + spanWithScope);
}
}
}
public static class SpanWithScope {
@Nullable private final SpanWithScope parent;
private final Span span;
private final Scope scope;
public SpanWithScope(SpanWithScope parent, Span span, Scope scope) {
this.parent = parent;
this.span = span;
this.scope = scope;
}
public static SpanWithScope activate(Span span, SpanWithScope parent) {
Scope scope = CamelTracer.TRACER.startScope(span);
return new SpanWithScope(parent, span, scope);
}
public SpanWithScope getParent() {
return parent;
}
public Span getSpan() {
return span;
}
public void deactivate() {
span.end();
scope.close();
}
@Override
public String toString() {
return "SpanWithScope [span=" + span + ", scope=" + scope + "]";
}
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.apachecamel;
import static io.opentelemetry.javaagent.tooling.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.implementsInterface;
import static net.bytebuddy.matcher.ElementMatchers.isAbstract;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.tooling.Instrumenter;
import java.util.Collections;
import java.util.Map;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.camel.CamelContext;
@AutoService(Instrumenter.class)
public class CamelContextInstrumentation extends Instrumenter.Default {
public CamelContextInstrumentation() {
super("apachecamel", "apache-camel");
}
@Override
public ElementMatcher<ClassLoader> classLoaderMatcher() {
// Optimization for expensive typeMatcher.
return hasClassesNamed("org.apache.camel.CamelContext");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return not(isAbstract()).and(implementsInterface(named("org.apache.camel.CamelContext")));
}
@Override
public String[] helperClassNames() {
return new String[] {
"io.opentelemetry.javaagent.instrumentation.apachecamel.CamelDirection",
"io.opentelemetry.javaagent.instrumentation.apachecamel.SpanDecorator",
"io.opentelemetry.javaagent.instrumentation.apachecamel.decorators.BaseSpanDecorator",
"io.opentelemetry.javaagent.instrumentation.apachecamel.decorators.DbSpanDecorator",
"io.opentelemetry.javaagent.instrumentation.apachecamel.decorators.MessagingSpanDecorator",
"io.opentelemetry.javaagent.instrumentation.apachecamel.decorators.HttpSpanDecorator",
"io.opentelemetry.javaagent.instrumentation.apachecamel.decorators.InternalSpanDecorator",
"io.opentelemetry.javaagent.instrumentation.apachecamel.decorators.KafkaSpanDecorator",
"io.opentelemetry.javaagent.instrumentation.apachecamel.decorators.LogSpanDecorator",
"io.opentelemetry.javaagent.instrumentation.apachecamel.decorators.RestSpanDecorator",
"io.opentelemetry.javaagent.instrumentation.apachecamel.decorators.TimerSpanDecorator",
"io.opentelemetry.javaagent.instrumentation.apachecamel.decorators.DecoratorRegistry",
"io.opentelemetry.javaagent.instrumentation.apachecamel.ActiveSpanManager",
"io.opentelemetry.javaagent.instrumentation.apachecamel.ActiveSpanManager$SpanWithScope",
"io.opentelemetry.javaagent.instrumentation.apachecamel.CamelPropagationUtil",
"io.opentelemetry.javaagent.instrumentation.apachecamel.CamelPropagationUtil$MapGetter",
"io.opentelemetry.javaagent.instrumentation.apachecamel.CamelPropagationUtil$MapSetter",
"io.opentelemetry.javaagent.instrumentation.apachecamel.CamelTracer",
"io.opentelemetry.javaagent.instrumentation.apachecamel.CamelEventNotifier",
"io.opentelemetry.javaagent.instrumentation.apachecamel.CamelRoutePolicy",
"io.opentelemetry.javaagent.instrumentation.apachecamel.CamelTracingService"
};
}
@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
return Collections.singletonMap(
named("start").and(isPublic()).and(takesArguments(0)),
CamelContextInstrumentation.class.getName() + "$ContextAdvice");
}
public static class ContextAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onContextStart(@Advice.This final CamelContext context) throws Exception {
if (context.hasService(CamelTracingService.class) == null) {
// start this service eager so we init before Camel is starting up
context.addService(new CamelTracingService(context), true, true);
}
}
}
}

View File

@ -0,0 +1,11 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.apachecamel;
public enum CamelDirection {
INBOUND,
OUTBOUND;
}

View File

@ -0,0 +1,101 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import java.util.EventObject;
import org.apache.camel.management.event.ExchangeSendingEvent;
import org.apache.camel.management.event.ExchangeSentEvent;
import org.apache.camel.support.EventNotifierSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
final class CamelEventNotifier extends EventNotifierSupport {
private static final Logger LOG = LoggerFactory.getLogger(CamelEventNotifier.class);
@Override
public void notify(EventObject event) {
try {
if (event instanceof ExchangeSendingEvent) {
onExchangeSending((ExchangeSendingEvent) event);
} else if (event instanceof ExchangeSentEvent) {
onExchangeSent((ExchangeSentEvent) event);
}
} catch (Throwable t) {
LOG.warn("Failed to capture tracing data", t);
}
}
/** Camel about to send (outbound). */
private void onExchangeSending(ExchangeSendingEvent ese) {
SpanDecorator sd = CamelTracer.TRACER.getSpanDecorator(ese.getEndpoint());
if (!sd.shouldStartNewSpan()) {
return;
}
String name =
sd.getOperationName(ese.getExchange(), ese.getEndpoint(), CamelDirection.OUTBOUND);
Span span = CamelTracer.TRACER.startSpan(name, sd.getInitiatorSpanKind());
sd.pre(span, ese.getExchange(), ese.getEndpoint(), CamelDirection.OUTBOUND);
CamelPropagationUtil.injectParent(Context.current(), ese.getExchange().getIn().getHeaders());
ActiveSpanManager.activate(ese.getExchange(), span);
if (LOG.isTraceEnabled()) {
LOG.trace("[Exchange sending] Initiator span started " + span);
}
}
/** Camel finished sending (outbound). Finish span and remove it from CAMEL holder. */
private void onExchangeSent(ExchangeSentEvent event) {
ExchangeSentEvent ese = event;
SpanDecorator sd = CamelTracer.TRACER.getSpanDecorator(ese.getEndpoint());
if (!sd.shouldStartNewSpan()) {
return;
}
Span span = ActiveSpanManager.getSpan(ese.getExchange());
if (span != null) {
if (LOG.isTraceEnabled()) {
LOG.trace("[Exchange sent] Initiator span finished " + span);
}
sd.post(span, ese.getExchange(), ese.getEndpoint());
ActiveSpanManager.deactivate(ese.getExchange());
} else {
LOG.warn("Could not find managed span for exchange " + ese.getExchange());
}
}
@Override
public boolean isEnabled(EventObject event) {
return event instanceof ExchangeSendingEvent || event instanceof ExchangeSentEvent;
}
@Override
public String toString() {
return "OpenTelemetryCamelEventNotifier";
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.apachecamel;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator.Getter;
import io.opentelemetry.context.propagation.TextMapPropagator.Setter;
import java.util.Map;
final class CamelPropagationUtil {
private CamelPropagationUtil() {}
static Context extractParent(final Map<String, Object> exchangeHeaders) {
return OpenTelemetry.getGlobalPropagators()
.getTextMapPropagator()
.extract(Context.current(), exchangeHeaders, MapGetter.INSTANCE);
}
static void injectParent(Context context, final Map<String, Object> exchangeHeaders) {
OpenTelemetry.getGlobalPropagators()
.getTextMapPropagator()
.inject(context, exchangeHeaders, MapSetter.INSTANCE);
}
private static class MapGetter implements Getter<Map<String, Object>> {
private static final MapGetter INSTANCE = new MapGetter();
@Override
public String get(Map<String, Object> map, String s) {
return (map.containsKey(s) ? map.get(s).toString() : null);
}
}
private static class MapSetter implements Setter<Map<String, Object>> {
private static final MapSetter INSTANCE = new MapSetter();
@Override
public void set(Map<String, Object> carrier, String key, String value) {
// Camel keys are internal ones
if (!key.startsWith("Camel")) {
carrier.put(key, value);
}
}
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import org.apache.camel.Exchange;
import org.apache.camel.Route;
import org.apache.camel.support.RoutePolicySupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
final class CamelRoutePolicy extends RoutePolicySupport {
private static final Logger LOG = LoggerFactory.getLogger(CamelRoutePolicy.class);
private Span spanOnExchangeBegin(Route route, Exchange exchange, SpanDecorator sd) {
Span activeSpan = CamelTracer.TRACER.getCurrentSpan();
String name = sd.getOperationName(exchange, route.getEndpoint(), CamelDirection.INBOUND);
Span.Builder builder = CamelTracer.TRACER.spanBuilder(name);
if (!activeSpan.getSpanContext().isValid()) {
// root operation, set kind, otherwise - INTERNAL
builder.setSpanKind(sd.getReceiverSpanKind());
Context parentContext = CamelPropagationUtil.extractParent(exchange.getIn().getHeaders());
if (parentContext != null) {
builder.setParent(parentContext);
}
}
return builder.startSpan();
}
/**
* Route exchange started, ie request could have been already captured by upper layer
* instrumentation.
*/
@Override
public void onExchangeBegin(Route route, Exchange exchange) {
try {
SpanDecorator sd = CamelTracer.TRACER.getSpanDecorator(route.getEndpoint());
Span span = spanOnExchangeBegin(route, exchange, sd);
sd.pre(span, exchange, route.getEndpoint(), CamelDirection.INBOUND);
ActiveSpanManager.activate(exchange, span);
if (LOG.isTraceEnabled()) {
LOG.trace("[Route start] Receiver span started " + span);
}
} catch (Throwable t) {
LOG.warn("Failed to capture tracing data", t);
}
}
/** Route exchange done. Get active CAMEL span, finish, remove from CAMEL holder. */
@Override
public void onExchangeDone(Route route, Exchange exchange) {
try {
Span span = ActiveSpanManager.getSpan(exchange);
if (span != null) {
if (LOG.isTraceEnabled()) {
LOG.trace("[Route finished] Receiver span finished " + span);
}
SpanDecorator sd = CamelTracer.TRACER.getSpanDecorator(route.getEndpoint());
sd.post(span, exchange, route.getEndpoint());
ActiveSpanManager.deactivate(exchange);
} else {
LOG.warn("Could not find managed span for exchange=" + exchange);
}
} catch (Throwable t) {
LOG.warn("Failed to capture tracing data", t);
}
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.javaagent.instrumentation.apachecamel.decorators.DecoratorRegistry;
import org.apache.camel.Endpoint;
import org.apache.camel.util.StringHelper;
class CamelTracer extends BaseTracer {
public static final CamelTracer TRACER = new CamelTracer();
private final DecoratorRegistry registry = new DecoratorRegistry();
@Override
protected String getInstrumentationName() {
return "io.opentelemetry.auto.apache-camel-2.20";
}
public Span.Builder spanBuilder(String name) {
return tracer.spanBuilder(name);
}
public SpanDecorator getSpanDecorator(Endpoint endpoint) {
String component = "";
String uri = endpoint.getEndpointUri();
String splitURI[] = StringHelper.splitOnCharacter(uri, ":", 2);
if (splitURI[1] != null) {
component = splitURI[0];
}
return registry.forComponent(component);
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel;
import org.apache.camel.CamelContext;
import org.apache.camel.StaticService;
import org.apache.camel.model.RouteDefinition;
import org.apache.camel.spi.RoutePolicy;
import org.apache.camel.spi.RoutePolicyFactory;
import org.apache.camel.support.ServiceSupport;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.ServiceHelper;
public class CamelTracingService extends ServiceSupport
implements RoutePolicyFactory, StaticService {
private final CamelContext camelContext;
private final CamelEventNotifier eventNotifier = new CamelEventNotifier();
private final CamelRoutePolicy routePolicy = new CamelRoutePolicy();
public CamelTracingService(CamelContext camelContext) {
ObjectHelper.notNull(camelContext, "CamelContext", this);
this.camelContext = camelContext;
}
@Override
protected void doStart() throws Exception {
camelContext.getManagementStrategy().addEventNotifier(eventNotifier);
if (!camelContext.getRoutePolicyFactories().contains(this)) {
camelContext.addRoutePolicyFactory(this);
}
ServiceHelper.startServices(eventNotifier);
}
@Override
protected void doStop() throws Exception {
// stop event notifier
camelContext.getManagementStrategy().removeEventNotifier(eventNotifier);
ServiceHelper.stopService(eventNotifier);
// remove route policy
camelContext.getRoutePolicyFactories().remove(this);
}
@Override
public RoutePolicy createRoutePolicy(
CamelContext camelContext, String routeId, RouteDefinition route) {
return routePolicy;
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel;
import io.opentelemetry.api.trace.Span;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
/** This interface represents a decorator specific to the component/endpoint being instrumented. */
public interface SpanDecorator {
/**
* This method indicates whether the component associated with the SpanDecorator should result in
* a new span being created.
*
* @return Whether a new span should be created
*/
boolean shouldStartNewSpan();
/**
* This method returns the operation name to use with the Span representing this exchange and
* endpoint.
*
* @param exchange The exchange
* @param endpoint The endpoint
* @return The operation name
*/
String getOperationName(Exchange exchange, Endpoint endpoint, CamelDirection camelDirection);
/**
* This method adds appropriate details (tags/logs) to the supplied span based on the pre
* processing of the exchange.
*
* @param span The span
* @param exchange The exchange
* @param endpoint The endpoint
*/
void pre(Span span, Exchange exchange, Endpoint endpoint, CamelDirection camelDirection);
/**
* This method adds appropriate details (tags/logs) to the supplied span based on the post
* processing of the exchange.
*
* @param span The span
* @param exchange The exchange
* @param endpoint The endpoint
*/
void post(Span span, Exchange exchange, Endpoint endpoint);
/**
* This method returns the 'span.kind' value for use when the component is initiating a
* communication.
*
* @return The kind
*/
Span.Kind getInitiatorSpanKind();
/**
* This method returns the 'span.kind' value for use when the component is receiving a
* communication.
*
* @return The kind
*/
Span.Kind getReceiverSpanKind();
}

View File

@ -0,0 +1,117 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel.decorators;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Span.Kind;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.javaagent.instrumentation.apachecamel.CamelDirection;
import io.opentelemetry.javaagent.instrumentation.apachecamel.SpanDecorator;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
import org.apache.camel.util.StringHelper;
import org.apache.camel.util.URISupport;
/** An abstract base implementation of the {@link SpanDecorator} interface. */
class BaseSpanDecorator implements SpanDecorator {
static final String DEFAULT_OPERATION_NAME = "CamelOperation";
/**
* This method removes the scheme, any leading slash characters and options from the supplied URI.
* This is intended to extract a meaningful name from the URI that can be used in situations, such
* as the operation name.
*
* @param endpoint The endpoint
* @return The stripped value from the URI
*/
public static String stripSchemeAndOptions(Endpoint endpoint) {
int start = endpoint.getEndpointUri().indexOf(':');
start++;
// Remove any leading '/'
while (endpoint.getEndpointUri().charAt(start) == '/') {
start++;
}
int end = endpoint.getEndpointUri().indexOf('?');
return end == -1
? endpoint.getEndpointUri().substring(start)
: endpoint.getEndpointUri().substring(start, end);
}
public static Map<String, String> toQueryParameters(String uri) {
int index = uri.indexOf('?');
if (index != -1) {
String queryString = uri.substring(index + 1);
Map<String, String> map = new HashMap<>();
for (String param : queryString.split("&")) {
String[] parts = param.split("=");
if (parts.length == 2) {
map.put(parts[0], parts[1]);
}
}
return map;
}
return Collections.emptyMap();
}
@Override
public boolean shouldStartNewSpan() {
return true;
}
@Override
public String getOperationName(
Exchange exchange, Endpoint endpoint, CamelDirection camelDirection) {
String[] splitURI = StringHelper.splitOnCharacter(endpoint.getEndpointUri(), ":", 2);
return (splitURI.length > 0 ? splitURI[0] : DEFAULT_OPERATION_NAME);
}
@Override
public void pre(Span span, Exchange exchange, Endpoint endpoint, CamelDirection camelDirection) {
span.setAttribute("camel.uri", URISupport.sanitizeUri(endpoint.getEndpointUri()));
}
@Override
public void post(Span span, Exchange exchange, Endpoint endpoint) {
if (exchange.isFailed()) {
span.setStatus(StatusCode.ERROR);
if (exchange.getException() != null) {
span.recordException(exchange.getException());
}
}
}
@Override
public Span.Kind getInitiatorSpanKind() {
return Kind.CLIENT;
}
@Override
public Span.Kind getReceiverSpanKind() {
return Kind.SERVER;
}
}

View File

@ -0,0 +1,138 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel.decorators;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.attributes.SemanticAttributes;
import io.opentelemetry.javaagent.instrumentation.apachecamel.CamelDirection;
import java.net.URI;
import java.util.Map;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
class DbSpanDecorator extends BaseSpanDecorator {
private final String component;
private final String system;
DbSpanDecorator(String component, String system) {
this.component = component;
this.system = system;
}
@Override
public String getOperationName(
Exchange exchange, Endpoint endpoint, CamelDirection camelDirection) {
switch (component) {
case "mongodb":
case "elasticsearch":
{
Map<String, String> queryParameters = toQueryParameters(endpoint.getEndpointUri());
if (queryParameters.containsKey("operation")) {
return queryParameters.get("operation");
}
}
}
return super.getOperationName(exchange, endpoint, camelDirection);
}
private String getStatement(Exchange exchange, Endpoint endpoint) {
switch (component) {
case "mongodb":
{
Map<String, String> queryParameters = toQueryParameters(endpoint.getEndpointUri());
return queryParameters.toString();
}
case "cql":
{
Object cql = exchange.getIn().getHeader("CamelCqlQuery");
if (cql != null) {
return cql.toString();
} else {
Map<String, String> queryParameters = toQueryParameters(endpoint.getEndpointUri());
if (queryParameters.containsKey("cql")) {
return queryParameters.get("cql");
}
}
}
case "jdbc":
{
Object body = exchange.getIn().getBody();
if (body instanceof String) {
return (String) body;
}
}
case "sql":
{
Object sqlquery = exchange.getIn().getHeader("CamelSqlQuery");
if (sqlquery instanceof String) {
return (String) sqlquery;
}
}
}
return null;
}
private String getDbName(Endpoint endpoint) {
switch (component) {
case "mongodb":
{
Map<String, String> queryParameters = toQueryParameters(endpoint.getEndpointUri());
return queryParameters.get("database");
}
case "cql":
{
URI uri = URI.create(endpoint.getEndpointUri());
if (uri.getPath() != null && uri.getPath().length() > 0) {
// Strip leading '/' from path
return uri.getPath().substring(1);
}
}
case "elasticsearch":
{
Map<String, String> queryParameters = toQueryParameters(endpoint.getEndpointUri());
if (queryParameters.containsKey("indexName")) {
return queryParameters.get("indexName");
}
}
}
return null;
}
@Override
public void pre(Span span, Exchange exchange, Endpoint endpoint, CamelDirection camelDirection) {
super.pre(span, exchange, endpoint, camelDirection);
span.setAttribute(SemanticAttributes.DB_SYSTEM, system);
String statement = getStatement(exchange, endpoint);
if (statement != null) {
span.setAttribute(SemanticAttributes.DB_STATEMENT, statement);
}
String dbName = getDbName(endpoint);
if (dbName != null) {
span.setAttribute(SemanticAttributes.DB_NAME, dbName);
}
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.apachecamel.decorators;
import io.opentelemetry.javaagent.instrumentation.apachecamel.SpanDecorator;
import io.opentelemetry.javaagent.instrumentation.api.db.DbSystem;
import java.util.HashMap;
import java.util.Map;
public class DecoratorRegistry {
private static final SpanDecorator DEFAULT = new BaseSpanDecorator();
private static final Map<String, SpanDecorator> DECORATORS = loadDecorators();
private static Map<String, SpanDecorator> loadDecorators() {
Map<String, SpanDecorator> result = new HashMap<>();
result.put("ahc", new HttpSpanDecorator());
result.put("ampq", new MessagingSpanDecorator("ampq"));
result.put("aws-sns", new MessagingSpanDecorator("aws-sns"));
result.put("aws-sqs", new MessagingSpanDecorator("aws-sqs"));
result.put("cometd", new MessagingSpanDecorator("cometd"));
result.put("cometds", new MessagingSpanDecorator("cometds"));
result.put("cql", new DbSpanDecorator("cql", DbSystem.CASSANDRA));
result.put("direct", new InternalSpanDecorator());
result.put("direct-vm", new InternalSpanDecorator());
result.put("disruptor", new InternalSpanDecorator());
result.put("disruptor-vm", new InternalSpanDecorator());
result.put("elasticsearch", new DbSpanDecorator("elasticsearch", "elasticsearch"));
result.put("http4", new HttpSpanDecorator());
result.put("http", new HttpSpanDecorator());
result.put("ironmq", new MessagingSpanDecorator("ironmq"));
result.put("jdbc", new DbSpanDecorator("jdbc", DbSystem.OTHER_SQL));
result.put("jetty", new HttpSpanDecorator());
result.put("jms", new MessagingSpanDecorator("jms"));
result.put("kafka", new KafkaSpanDecorator());
result.put("log", new LogSpanDecorator());
result.put("mongodb", new DbSpanDecorator("mongodb", DbSystem.MONGODB));
result.put("mqtt", new MessagingSpanDecorator("mqtt"));
result.put("netty-http4", new HttpSpanDecorator());
result.put("netty-http", new HttpSpanDecorator());
result.put("paho", new MessagingSpanDecorator("paho"));
result.put("rabbitmq", new MessagingSpanDecorator("rabbitmq"));
result.put("restlet", new HttpSpanDecorator());
result.put("rest", new RestSpanDecorator());
result.put("seda", new InternalSpanDecorator());
result.put("servlet", new HttpSpanDecorator());
result.put("sjms", new MessagingSpanDecorator("sjms"));
result.put("sql", new DbSpanDecorator("sql", DbSystem.OTHER_SQL));
result.put("stomp", new MessagingSpanDecorator("stomp"));
result.put("timer", new TimerSpanDecorator());
result.put("undertow", new HttpSpanDecorator());
result.put("vm", new InternalSpanDecorator());
return result;
}
public SpanDecorator forComponent(final String component) {
return DECORATORS.getOrDefault(component, DEFAULT);
}
}

View File

@ -0,0 +1,153 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel.decorators;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.attributes.SemanticAttributes;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.javaagent.instrumentation.apachecamel.CamelDirection;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
import org.checkerframework.checker.nullness.qual.Nullable;
class HttpSpanDecorator extends BaseSpanDecorator {
private static final String POST_METHOD = "POST";
private static final String GET_METHOD = "GET";
protected static String getHttpMethod(Exchange exchange, Endpoint endpoint) {
// 1. Use method provided in header.
Object method = exchange.getIn().getHeader(Exchange.HTTP_METHOD);
if (method instanceof String) {
return (String) method;
}
// 2. GET if query string is provided in header.
if (exchange.getIn().getHeader(Exchange.HTTP_QUERY) != null) {
return GET_METHOD;
}
// 3. GET if endpoint is configured with a query string.
if (endpoint.getEndpointUri().indexOf('?') != -1) {
return GET_METHOD;
}
// 4. POST if there is data to send (body is not null).
if (exchange.getIn().getBody() != null) {
return POST_METHOD;
}
// 5. GET otherwise.
return GET_METHOD;
}
@Override
public String getOperationName(
Exchange exchange, Endpoint endpoint, CamelDirection camelDirection) {
// Based on HTTP component documentation:
String spanName = null;
if (shouldSetPathAsName(camelDirection)) {
spanName = getPath(exchange, endpoint);
}
return (spanName == null ? getHttpMethod(exchange, endpoint) : spanName);
}
@Override
public void pre(Span span, Exchange exchange, Endpoint endpoint, CamelDirection camelDirection) {
super.pre(span, exchange, endpoint, camelDirection);
String httpUrl = getHttpURL(exchange, endpoint);
if (httpUrl != null) {
span.setAttribute(SemanticAttributes.HTTP_URL, httpUrl);
}
span.setAttribute(SemanticAttributes.HTTP_METHOD, getHttpMethod(exchange, endpoint));
Span serverSpan = Context.current().get(BaseTracer.CONTEXT_SERVER_SPAN_KEY);
if (shouldUpdateServerSpanName(serverSpan, camelDirection)) {
updateServerSpanName(serverSpan, exchange, endpoint);
}
}
private boolean shouldSetPathAsName(CamelDirection camelDirection) {
return CamelDirection.INBOUND.equals(camelDirection);
}
@Nullable
protected String getPath(Exchange exchange, Endpoint endpoint) {
String httpUrl = getHttpURL(exchange, endpoint);
try {
URL url = new URL(httpUrl);
return url.getPath();
} catch (MalformedURLException e) {
return null;
}
}
private boolean shouldUpdateServerSpanName(Span serverSpan, CamelDirection camelDirection) {
return (serverSpan != null && shouldSetPathAsName(camelDirection));
}
private void updateServerSpanName(Span serverSpan, Exchange exchange, Endpoint endpoint) {
String path = getPath(exchange, endpoint);
if (path != null) {
serverSpan.updateName(path);
}
}
protected String getHttpURL(Exchange exchange, Endpoint endpoint) {
Object url = exchange.getIn().getHeader(Exchange.HTTP_URL);
if (url instanceof String) {
return (String) url;
} else {
Object uri = exchange.getIn().getHeader(Exchange.HTTP_URI);
if (uri instanceof String) {
return (String) uri;
} else {
// Try to obtain from endpoint
int index = endpoint.getEndpointUri().lastIndexOf("http:");
if (index != -1) {
return endpoint.getEndpointUri().substring(index);
}
}
}
return null;
}
@Override
public void post(Span span, Exchange exchange, Endpoint endpoint) {
super.post(span, exchange, endpoint);
if (exchange.hasOut()) {
Object responseCode = exchange.getOut().getHeader(Exchange.HTTP_RESPONSE_CODE);
if (responseCode instanceof Integer) {
span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, (Integer) responseCode);
}
}
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel.decorators;
import io.opentelemetry.api.trace.Span.Kind;
import io.opentelemetry.javaagent.instrumentation.apachecamel.CamelDirection;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
class InternalSpanDecorator extends BaseSpanDecorator {
@Override
public String getOperationName(
Exchange exchange, Endpoint endpoint, CamelDirection camelDirection) {
// Internal communications use descriptive names, so suitable
// as an operation name, but need to strip the scheme and any options
return stripSchemeAndOptions(endpoint);
}
@Override
public boolean shouldStartNewSpan() {
return false;
}
@Override
public Kind getReceiverSpanKind() {
return Kind.INTERNAL;
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel.decorators;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.attributes.SemanticAttributes;
import io.opentelemetry.javaagent.instrumentation.apachecamel.CamelDirection;
import java.util.Map;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
class KafkaSpanDecorator extends MessagingSpanDecorator {
private static final String PARTITION_KEY = "kafka.PARTITION_KEY";
private static final String PARTITION = "kafka.PARTITION";
private static final String KEY = "kafka.KEY";
private static final String TOPIC = "kafka.TOPIC";
private static final String OFFSET = "kafka.OFFSET";
public KafkaSpanDecorator() {
super("kafka");
}
@Override
public String getDestination(Exchange exchange, Endpoint endpoint) {
String topic = (String) exchange.getIn().getHeader(TOPIC);
if (topic == null) {
Map<String, String> queryParameters = toQueryParameters(endpoint.getEndpointUri());
topic = queryParameters.get("topic");
}
return topic != null ? topic : super.getDestination(exchange, endpoint);
}
@Override
public void pre(Span span, Exchange exchange, Endpoint endpoint, CamelDirection camelDirection) {
super.pre(span, exchange, endpoint, camelDirection);
span.setAttribute(SemanticAttributes.MESSAGING_OPERATION, "process");
span.setAttribute(SemanticAttributes.MESSAGING_DESTINATION_KIND, "topic");
String partition = getValue(exchange, PARTITION, Integer.class);
if (partition != null) {
span.setAttribute("partition", partition);
}
String partitionKey = (String) exchange.getIn().getHeader(PARTITION_KEY);
if (partitionKey != null) {
span.setAttribute("partitionKey", partitionKey);
}
String key = (String) exchange.getIn().getHeader(KEY);
if (key != null) {
span.setAttribute("key", key);
}
String offset = getValue(exchange, OFFSET, Long.class);
if (offset != null) {
span.setAttribute("offset", offset);
}
}
/**
* Extracts header value from the exchange for given header
*
* @param exchange the {@link Exchange}
* @param header the header name
* @param type the class type of the exchange header
* @return
*/
private <T> String getValue(final Exchange exchange, final String header, Class<T> type) {
T value = exchange.getIn().getHeader(header, type);
return value != null ? String.valueOf(value) : exchange.getIn().getHeader(header, String.class);
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel.decorators;
class LogSpanDecorator extends BaseSpanDecorator {
@Override
public boolean shouldStartNewSpan() {
return false;
}
}

View File

@ -0,0 +1,127 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel.decorators;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Span.Kind;
import io.opentelemetry.api.trace.attributes.SemanticAttributes;
import io.opentelemetry.javaagent.instrumentation.apachecamel.CamelDirection;
import java.net.URI;
import java.util.Map;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
class MessagingSpanDecorator extends BaseSpanDecorator {
private final String component;
public MessagingSpanDecorator(String component) {
this.component = component;
}
@Override
public String getOperationName(
Exchange exchange, Endpoint endpoint, CamelDirection camelDirection) {
switch (component) {
case "mqtt":
return stripSchemeAndOptions(endpoint);
}
return getDestination(exchange, endpoint);
}
@Override
public void pre(Span span, Exchange exchange, Endpoint endpoint, CamelDirection camelDirection) {
super.pre(span, exchange, endpoint, camelDirection);
span.setAttribute(SemanticAttributes.MESSAGING_DESTINATION, getDestination(exchange, endpoint));
String messageId = getMessageId(exchange);
if (messageId != null) {
span.setAttribute(SemanticAttributes.MESSAGING_MESSAGE_ID, messageId);
}
}
/**
* This method identifies the destination from the supplied exchange and/or endpoint.
*
* @param exchange The exchange
* @param endpoint The endpoint
* @return The message bus destination
*/
protected String getDestination(Exchange exchange, Endpoint endpoint) {
switch (component) {
case "cometds":
case "cometd":
return URI.create(endpoint.getEndpointUri()).getPath().substring(1);
case "rabbitmq":
return (String) exchange.getIn().getHeader("rabbitmq.EXCHANGE_NAME");
case "stomp":
{
String destination = stripSchemeAndOptions(endpoint);
if (destination.startsWith("queue:")) {
destination = destination.substring("queue:".length());
}
return destination;
}
case "mqtt":
{
Map<String, String> queryParameters = toQueryParameters(endpoint.getEndpointUri());
return (queryParameters.containsKey("subscribeTopicNames")
? queryParameters.get("subscribeTopicNames")
: queryParameters.get("publishTopicName"));
}
}
return stripSchemeAndOptions(endpoint);
}
@Override
public Span.Kind getInitiatorSpanKind() {
return Kind.PRODUCER;
}
@Override
public Span.Kind getReceiverSpanKind() {
return Kind.CONSUMER;
}
/**
* This method identifies the message id for the messaging exchange.
*
* @return The message id, or null if no id exists for the exchange
*/
protected String getMessageId(Exchange exchange) {
switch (component) {
case "aws-sns":
return (String) exchange.getIn().getHeader("CamelAwsSnsMessageId");
case "aws-sqs":
return (String) exchange.getIn().getHeader("CamelAwsSqsMessageId");
case "ironmq":
return (String) exchange.getIn().getHeader("CamelIronMQMessageId");
case "jms":
return (String) exchange.getIn().getHeader("JMSMessageID");
}
return null;
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel.decorators;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class RestSpanDecorator extends HttpSpanDecorator {
private static final Logger LOG = LoggerFactory.getLogger(RestSpanDecorator.class);
@Override
protected String getPath(Exchange exchange, Endpoint endpoint) {
String endpointUri = endpoint.getEndpointUri();
// Obtain the 'path' part of the URI format: rest://method:path[:uriTemplate]?[options]
String path = null;
int index = endpointUri.indexOf(':');
if (index != -1) {
index = endpointUri.indexOf(':', index + 1);
if (index != -1) {
path = endpointUri.substring(index + 1);
index = path.indexOf('?');
if (index != -1) {
path = path.substring(0, index);
}
path = path.replaceAll(":", "");
try {
path = URLDecoder.decode(path, "UTF-8");
} catch (UnsupportedEncodingException e) {
LOG.debug("Failed to decode URL path '" + path + "', ignoring exception", e);
}
}
}
return path;
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Apache Camel Opentracing Component
*
* 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 io.opentelemetry.javaagent.instrumentation.apachecamel.decorators;
import io.opentelemetry.javaagent.instrumentation.apachecamel.CamelDirection;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
class TimerSpanDecorator extends BaseSpanDecorator {
@Override
public String getOperationName(
Exchange exchange, Endpoint endpoint, CamelDirection camelDirection) {
Object name = exchange.getProperty(Exchange.TIMER_NAME);
if (name instanceof String) {
return (String) name;
}
return super.getOperationName(exchange, endpoint, camelDirection);
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package test
import static io.opentelemetry.api.trace.Span.Kind.INTERNAL
import io.opentelemetry.instrumentation.test.AgentTestRunner
import org.apache.camel.CamelContext
import org.apache.camel.ProducerTemplate
import org.springframework.boot.SpringApplication
import org.springframework.context.ConfigurableApplicationContext
import spock.lang.Shared
class DirectCamelTest extends AgentTestRunner {
@Shared
ConfigurableApplicationContext server
def setupSpec() {
def app = new SpringApplication(DirectConfig)
server = app.run()
}
def cleanupSpec() {
if (server != null) {
server.close()
server = null
}
}
def "simple direct to a single services"() {
setup:
def camelContext = server.getBean(CamelContext)
ProducerTemplate template = camelContext.createProducerTemplate()
when:
template.sendBody("direct:input", "Example request")
then:
assertTraces(1) {
trace(0, 2) {
def parent = it
it.span(0) {
name "input"
kind INTERNAL
hasNoParent()
attributes {
"camel.uri" "direct://input"
}
}
it.span(1) {
name "receiver"
kind INTERNAL
parentSpanId parent.span(0).spanId
attributes {
"camel.uri" "direct://receiver"
}
}
}
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package test
import org.apache.camel.LoggingLevel
import org.apache.camel.builder.RouteBuilder
import org.springframework.boot.SpringBootConfiguration
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.context.annotation.Bean
@SpringBootConfiguration
@EnableAutoConfiguration
class DirectConfig {
@Bean
RouteBuilder receiverRoute() {
return new RouteBuilder() {
@Override
void configure() throws Exception {
from("direct:receiver")
.log(LoggingLevel.INFO, "test","RECEIVER got: \${body}")
.delay(simple("2000"))
.setBody(constant("result"))
}
}
}
@Bean
RouteBuilder clientRoute() {
return new RouteBuilder() {
@Override
void configure() throws Exception {
from("direct:input")
.log(LoggingLevel.INFO, "test","SENDING request \${body}")
.to("direct:receiver")
.log(LoggingLevel.INFO, "test","RECEIVED response \${body}")
}
}
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package test
import org.apache.camel.LoggingLevel
import org.apache.camel.builder.RouteBuilder
import org.springframework.boot.SpringBootConfiguration
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.context.annotation.Bean
@SpringBootConfiguration
@EnableAutoConfiguration
class MulticastConfig {
@Bean
RouteBuilder firstServiceRoute() {
return new RouteBuilder() {
@Override
void configure() throws Exception {
from("direct:first")
.log(LoggingLevel.INFO, "test","FIRST request: \${body}")
.delay(simple("1000"))
.setBody(constant("first"))
}
}
}
@Bean
RouteBuilder secondServiceRoute() {
return new RouteBuilder() {
@Override
void configure() throws Exception {
from("direct:second")
.log(LoggingLevel.INFO, "test","SECOND request: \${body}")
.delay(simple("2000"))
.setBody(constant("second"))
}
}
}
@Bean
RouteBuilder clientServiceRoute() {
return new RouteBuilder() {
@Override
void configure() throws Exception {
from("direct:input")
.log(LoggingLevel.INFO, "test","SENDING request \${body}")
.multicast()
.parallelProcessing()
.to("direct:first", "direct:second")
.end()
.log(LoggingLevel.INFO, "test","RECEIVED response \${body}")
}
}
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package test
import static io.opentelemetry.api.trace.Span.Kind.INTERNAL
import io.opentelemetry.instrumentation.test.AgentTestRunner
import org.apache.camel.CamelContext
import org.apache.camel.ProducerTemplate
import org.springframework.boot.SpringApplication
import org.springframework.context.ConfigurableApplicationContext
import spock.lang.Shared
class MulticastDirectCamelTest extends AgentTestRunner {
@Shared
ConfigurableApplicationContext server
def setupSpec() {
def app = new SpringApplication(MulticastConfig)
server = app.run()
}
def cleanupSpec() {
if (server != null) {
server.close()
server = null
}
}
def "parallel multicast to two child services"() {
setup:
def camelContext = server.getBean(CamelContext)
ProducerTemplate template = camelContext.createProducerTemplate()
when:
template.sendBody("direct:input", "Example request")
then:
assertTraces(1) {
trace(0, 3) {
def parent = it
it.span(0) {
name "input"
kind INTERNAL
hasNoParent()
attributes {
"camel.uri" "direct://input"
}
}
it.span(1) {
name "second"
kind INTERNAL
parentSpanId parent.span(0).spanId
attributes {
"camel.uri" "direct://second"
}
}
it.span(2) {
name "first"
kind INTERNAL
parentSpanId parent.span(0).spanId
attributes {
"camel.uri" "direct://first"
}
}
}
}
}
}

View File

@ -0,0 +1,117 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package test
import static io.opentelemetry.api.trace.Span.Kind.CLIENT
import static io.opentelemetry.api.trace.Span.Kind.INTERNAL
import static io.opentelemetry.api.trace.Span.Kind.SERVER
import com.google.common.collect.ImmutableMap
import io.opentelemetry.instrumentation.test.AgentTestRunner
import io.opentelemetry.instrumentation.test.utils.PortUtils
import io.opentelemetry.api.trace.attributes.SemanticAttributes
import org.apache.camel.CamelContext
import org.apache.camel.ProducerTemplate
import org.springframework.boot.SpringApplication
import org.springframework.context.ConfigurableApplicationContext
import spock.lang.Shared
class RestCamelTest extends AgentTestRunner {
@Shared
ConfigurableApplicationContext server
@Shared
int port
def setupSpec() {
withRetryOnAddressAlreadyInUse({
setupSpecUnderRetry()
})
}
def setupSpecUnderRetry() {
port = PortUtils.randomOpenPort()
def app = new SpringApplication(RestConfig)
app.setDefaultProperties(ImmutableMap.of("restServer.port", port))
server = app.run()
println getClass().name + " http server started at: http://localhost:$port/"
}
def cleanupSpec() {
if (server != null) {
server.close()
server = null
}
}
def "rest component - server and client call with jetty backend"() {
setup:
def camelContext = server.getBean(CamelContext)
ProducerTemplate template = camelContext.createProducerTemplate()
when:
// run client and server in separate threads to simulate "real" rest client/server call
new Thread(new Runnable() {
@Override
void run() {
template.sendBodyAndHeaders("direct:start", null, ImmutableMap.of("module", "firstModule", "unitId", "unitOne"))
}
}
).start()
then:
assertTraces(1) {
trace(0, 5) {
it.span(0) {
name "start"
kind INTERNAL
attributes {
"camel.uri" "direct://start"
}
}
it.span(1) {
name "GET"
kind CLIENT
attributes {
"$SemanticAttributes.HTTP_METHOD.key" "GET"
"$SemanticAttributes.HTTP_STATUS_CODE.key" 200
"camel.uri" "rest://get:api/%7Bmodule%7D/unit/%7BunitId%7D"
}
}
it.span(2) {
name "/api/{module}/unit/{unitId}"
kind SERVER
attributes {
"$SemanticAttributes.HTTP_URL.key" "http://localhost:$port/api/firstModule/unit/unitOne"
"$SemanticAttributes.HTTP_STATUS_CODE.key" 200
"$SemanticAttributes.HTTP_CLIENT_IP.key" "127.0.0.1"
"$SemanticAttributes.HTTP_USER_AGENT.key" "Jetty/9.3.21.v20170918"
"$SemanticAttributes.HTTP_FLAVOR.key" "HTTP/1.1"
"$SemanticAttributes.HTTP_METHOD.key" "GET"
"$SemanticAttributes.NET_PEER_IP.key" "127.0.0.1"
"$SemanticAttributes.NET_PEER_PORT.key" Long
}
}
it.span(3) {
name "/api/{module}/unit/{unitId}"
kind INTERNAL
attributes {
"$SemanticAttributes.HTTP_METHOD.key" "GET"
"$SemanticAttributes.HTTP_URL.key" "http://localhost:$port/api/firstModule/unit/unitOne"
"camel.uri" String
}
}
it.span(4) {
name "moduleUnit"
kind INTERNAL
attributes {
"camel.uri" "direct://moduleUnit"
}
}
}
}
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package test
import org.apache.camel.LoggingLevel
import org.apache.camel.builder.RouteBuilder
import org.apache.camel.model.rest.RestBindingMode
import org.springframework.boot.SpringBootConfiguration
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.context.annotation.Bean
@SpringBootConfiguration
@EnableAutoConfiguration
class RestConfig {
@Bean
RouteBuilder routes() {
return new RouteBuilder() {
@Override
void configure() throws Exception {
restConfiguration()
.component("jetty")
.bindingMode(RestBindingMode.auto)
.host("localhost")
.port("{{restServer.port}}")
rest("/api")
.get("/{module}/unit/{unitId}")
.to("direct:moduleUnit")
from("direct:moduleUnit")
.transform().simple("\${header.unitId} of \${header.module}")
// producer - client route
from("direct:start")
.log(LoggingLevel.INFO, "test","SENDING request")
.to("rest:get:api/{module}/unit/{unitId}")
.log(LoggingLevel.INFO, "test","RECEIVED response: '\${body}'")
}
}
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package test
import static io.opentelemetry.api.trace.Span.Kind.SERVER
import com.google.common.collect.ImmutableMap
import io.opentelemetry.instrumentation.test.AgentTestRunner
import io.opentelemetry.instrumentation.test.utils.OkHttpUtils
import io.opentelemetry.instrumentation.test.utils.PortUtils
import io.opentelemetry.api.trace.attributes.SemanticAttributes
import okhttp3.FormBody
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import org.springframework.boot.SpringApplication
import org.springframework.context.ConfigurableApplicationContext
import spock.lang.Shared
class SingleServiceCamelTest extends AgentTestRunner {
@Shared
ConfigurableApplicationContext server
@Shared
OkHttpClient client = OkHttpUtils.client()
@Shared
int port
@Shared
URI address
def setupSpec() {
withRetryOnAddressAlreadyInUse({
setupSpecUnderRetry()
})
}
def setupSpecUnderRetry() {
port = PortUtils.randomOpenPort()
address = new URI("http://localhost:$port/")
def app = new SpringApplication(SingleServiceConfig)
app.setDefaultProperties(ImmutableMap.of("camelService.port", port))
server = app.run()
println getClass().name + " http server started at: http://localhost:$port/"
}
def cleanupSpec() {
if (server != null) {
server.close()
server = null
}
}
def "single camel service span"() {
setup:
def requestUrl = address.resolve("/camelService")
def url = HttpUrl.get(requestUrl)
def request = new Request.Builder()
.url(url)
.method("POST",
new FormBody.Builder().add("", "testContent").build())
.build()
when:
client.newCall(request).execute()
then:
assertTraces(1) {
trace(0, 1) {
span(0) {
kind SERVER
name "/camelService"
attributes {
"$SemanticAttributes.HTTP_METHOD.key" "POST"
"$SemanticAttributes.HTTP_URL.key" "${address.resolve("/camelService")}"
"camel.uri" "${address.resolve("/camelService")}".replace("localhost", "0.0.0.0")
}
}
}
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package test
import org.apache.camel.LoggingLevel
import org.apache.camel.builder.RouteBuilder
import org.springframework.boot.SpringBootConfiguration
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.context.annotation.Bean
@SpringBootConfiguration
@EnableAutoConfiguration
class SingleServiceConfig {
@Bean
RouteBuilder serviceRoute() {
return new RouteBuilder() {
@Override
void configure() throws Exception {
from("undertow:http://0.0.0.0:{{camelService.port}}/camelService")
.routeId("camelService")
.streamCaching()
.log("CamelService request: \${body}")
.delay(simple("\${random(1000, 2000)}"))
.transform(simple("CamelService-\${body}"))
.log(LoggingLevel.INFO, "test", "CamelService response: \${body}")
}
}
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package test
import org.apache.camel.builder.RouteBuilder
import org.springframework.boot.SpringBootConfiguration
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.context.annotation.Bean
@SpringBootConfiguration
@EnableAutoConfiguration
class TwoServicesConfig {
@Bean
RouteBuilder serviceOneRoute() {
return new RouteBuilder() {
@Override
void configure() throws Exception {
from("undertow:http://0.0.0.0:{{service.one.port}}/serviceOne")
.routeId("serviceOne")
.streamCaching()
.removeHeaders("CamelHttp*")
.log("Service One request: \${body}")
.delay(simple("\${random(1000,2000)}"))
.transform(simple("Service-One-\${body}"))
.to("http://0.0.0.0:{{service.two.port}}/serviceTwo")
.log("Service One response: \${body}")
}
}
}
@Bean
RouteBuilder serviceTwoRoute() {
return new RouteBuilder() {
@Override
void configure() throws Exception {
from("jetty:http://0.0.0.0:{{service.two.port}}/serviceTwo?arg=value")
.routeId("serviceTwo")
.streamCaching()
.log("Service Two request: \${body}")
.delay(simple("\${random(1000, 2000)}"))
.transform(simple("Service-Two-\${body}"))
.log("Service Two response: \${body}")
}
}
}
}

View File

@ -0,0 +1,171 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package test
import static io.opentelemetry.api.trace.Span.Kind.CLIENT
import static io.opentelemetry.api.trace.Span.Kind.INTERNAL
import static io.opentelemetry.api.trace.Span.Kind.SERVER
import com.google.common.collect.ImmutableMap
import io.opentelemetry.instrumentation.test.AgentTestRunner
import io.opentelemetry.instrumentation.test.utils.PortUtils
import io.opentelemetry.api.trace.attributes.SemanticAttributes
import org.apache.camel.CamelContext
import org.apache.camel.ProducerTemplate
import org.apache.camel.builder.RouteBuilder
import org.apache.camel.impl.DefaultCamelContext
import org.springframework.boot.SpringApplication
import org.springframework.context.ConfigurableApplicationContext
import spock.lang.Shared
class TwoServicesWithDirectClientCamelTest extends AgentTestRunner {
@Shared
int portOne
@Shared
int portTwo
@Shared
ConfigurableApplicationContext server
@Shared
CamelContext clientContext
def setupSpec() {
withRetryOnAddressAlreadyInUse({
setupSpecUnderRetry()
})
}
def setupSpecUnderRetry() {
portOne = PortUtils.randomOpenPort()
portTwo = PortUtils.randomOpenPort()
def app = new SpringApplication(TwoServicesConfig)
app.setDefaultProperties(ImmutableMap.of("service.one.port", portOne, "service.two.port", portTwo))
server = app.run()
}
def createAndStartClient() {
clientContext = new DefaultCamelContext()
clientContext.addRoutes(new RouteBuilder() {
void configure() {
from("direct:input")
.log("SENT Client request")
.to("http://localhost:$portOne/serviceOne")
.log("RECEIVED Client response")
}
})
clientContext.start()
}
def cleanupSpec() {
if (server != null) {
server.close()
server = null
}
}
def "two camel service spans"() {
setup:
createAndStartClient()
ProducerTemplate template = clientContext.createProducerTemplate()
when:
template.sendBody("direct:input", "Example request")
then:
assertTraces(1) {
trace(0, 8) {
it.span(0) {
name "input"
kind INTERNAL
attributes {
"camel.uri" "direct://input"
}
}
it.span(1) {
name "POST"
kind CLIENT
attributes {
"$SemanticAttributes.HTTP_METHOD.key" "POST"
"$SemanticAttributes.HTTP_URL.key" "http://localhost:$portOne/serviceOne"
"$SemanticAttributes.HTTP_STATUS_CODE.key" 200
"camel.uri" "http://localhost:$portOne/serviceOne"
}
}
it.span(2) {
name "HTTP POST"
kind CLIENT
attributes {
"$SemanticAttributes.HTTP_METHOD.key" "POST"
"$SemanticAttributes.HTTP_URL.key" "http://localhost:$portOne/serviceOne"
"$SemanticAttributes.HTTP_STATUS_CODE.key" 200
"$SemanticAttributes.NET_PEER_NAME.key" "localhost"
"$SemanticAttributes.NET_PEER_PORT.key" portOne
"$SemanticAttributes.NET_TRANSPORT.key" "IP.TCP"
"$SemanticAttributes.HTTP_FLAVOR.key" "1.1"
}
}
it.span(3) {
name "/serviceOne"
kind SERVER
attributes {
"$SemanticAttributes.HTTP_METHOD.key" "POST"
"$SemanticAttributes.HTTP_URL.key" "http://localhost:$portOne/serviceOne"
"$SemanticAttributes.HTTP_STATUS_CODE.key" 200
"camel.uri" "http://0.0.0.0:$portOne/serviceOne"
}
}
it.span(4) {
name "POST"
kind CLIENT
attributes {
"$SemanticAttributes.HTTP_METHOD.key" "POST"
"$SemanticAttributes.HTTP_URL.key" "http://0.0.0.0:$portTwo/serviceTwo"
"$SemanticAttributes.HTTP_STATUS_CODE.key" 200
"camel.uri" "http://0.0.0.0:$portTwo/serviceTwo"
}
}
it.span(5) {
name "HTTP POST"
kind CLIENT
attributes {
"$SemanticAttributes.HTTP_METHOD.key" "POST"
"$SemanticAttributes.HTTP_URL.key" "http://0.0.0.0:$portTwo/serviceTwo"
"$SemanticAttributes.HTTP_STATUS_CODE.key" 200
"$SemanticAttributes.NET_PEER_NAME.key" "0.0.0.0"
"$SemanticAttributes.NET_PEER_PORT.key" portTwo
"$SemanticAttributes.NET_TRANSPORT.key" "IP.TCP"
"$SemanticAttributes.HTTP_FLAVOR.key" "1.1"
"$SemanticAttributes.HTTP_USER_AGENT.key" "Jakarta Commons-HttpClient/3.1"
}
}
it.span(6) {
name "/serviceTwo"
kind SERVER
attributes {
"$SemanticAttributes.HTTP_METHOD.key" "POST"
"$SemanticAttributes.HTTP_STATUS_CODE.key" 200
"$SemanticAttributes.HTTP_URL.key" "http://0.0.0.0:$portTwo/serviceTwo"
"$SemanticAttributes.NET_PEER_PORT.key" Number
"$SemanticAttributes.NET_PEER_IP.key" InetAddress.getLocalHost().getHostAddress().toString()
"$SemanticAttributes.HTTP_USER_AGENT.key" "Jakarta Commons-HttpClient/3.1"
"$SemanticAttributes.HTTP_FLAVOR.key" "HTTP/1.1"
"$SemanticAttributes.HTTP_CLIENT_IP.key" InetAddress.getLocalHost().getHostAddress().toString()
}
}
it.span(7) {
name "/serviceTwo"
kind INTERNAL
attributes {
"$SemanticAttributes.HTTP_METHOD.key" "POST"
"$SemanticAttributes.HTTP_URL.key" "http://0.0.0.0:$portTwo/serviceTwo"
"camel.uri" "jetty:http://0.0.0.0:$portTwo/serviceTwo?arg=value"
}
}
}
}
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</Pattern>
</layout>
</appender>
<root level="WARN">
<appender-ref ref="console"/>
</root>
<logger name="io.opentelemetry.javaagent.instrumentation.apachecamel" level="trace"/>
<logger name="org.apache.camel" level="debug" />
<logger name="test" level="trace"/>
</configuration>

View File

@ -55,6 +55,7 @@ include ':smoke-tests'
include ':instrumentation:akka-context-propagation-2.5'
include ':instrumentation:akka-http-10.0'
include ':instrumentation:apache-camel-2.20'
include ':instrumentation:apache-httpasyncclient-4.0'
include ':instrumentation:apache-httpclient:apache-httpclient-2.0'
include ':instrumentation:apache-httpclient:apache-httpclient-4.0'