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:
parent
a528daee52
commit
049358e81b
|
@ -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 {
|
||||
|
|
|
@ -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.+'
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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'
|
||||
|
|
Loading…
Reference in New Issue