Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

apache camel 2.20.x instrumentation #1397

Merged
merged 13 commits into from Nov 3, 2020
2 changes: 1 addition & 1 deletion gradle/spotless.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
35 changes: 35 additions & 0 deletions instrumentation/apache-camel-2.20/apache-camel-2.20.gradle
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')
This conversation was marked as resolved.
Show resolved Hide resolved

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.+'
}
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) {

This conversation was marked as resolved.
Show resolved Hide resolved
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 conversation was marked as resolved.
Show resolved Hide resolved
}

/**
* 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 + "]";
}
}
}
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);
}
}
}
}
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;
}
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);
}
}
This conversation was marked as resolved.
Show resolved Hide resolved

/** 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";
}
}
Loading