-
Notifications
You must be signed in to change notification settings - Fork 199
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
Showing
33 changed files
with
2,370 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
instrumentation/apache-camel-2.20/apache-camel-2.20.gradle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.+' | ||
} |
121 changes: 121 additions & 0 deletions
121
...c/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/ActiveSpanManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 + "]"; | ||
} | ||
} | ||
} |
91 changes: 91 additions & 0 deletions
91
...a/io/opentelemetry/javaagent/instrumentation/apachecamel/CamelContextInstrumentation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
.../src/main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/CamelDirection.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
101 changes: 101 additions & 0 deletions
101
.../main/java/io/opentelemetry/javaagent/instrumentation/apachecamel/CamelEventNotifier.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"; | ||
} | ||
} |
Oops, something went wrong.