|
| 1 | +/* |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more |
| 3 | + * contributor license agreements. See the NOTICE file distributed with |
| 4 | + * this work for additional information regarding copyright ownership. |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 |
| 6 | + * (the "License"); you may not use this file except in compliance with |
| 7 | + * the License. You may obtain a copy of the License at |
| 8 | + * |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | + * |
| 11 | + * Unless required by applicable law or agreed to in writing, software |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | + * See the License for the specific language governing permissions and |
| 15 | + * limitations under the License. |
| 16 | + */ |
| 17 | +package org.apache.camel.opentelemetry2; |
| 18 | + |
| 19 | +import io.opentelemetry.api.baggage.Baggage; |
| 20 | +import io.opentelemetry.api.trace.SpanBuilder; |
| 21 | +import io.opentelemetry.api.trace.SpanKind; |
| 22 | +import io.opentelemetry.api.trace.Tracer; |
| 23 | +import io.opentelemetry.context.Context; |
| 24 | +import io.opentelemetry.context.propagation.ContextPropagators; |
| 25 | +import io.opentelemetry.context.propagation.TextMapGetter; |
| 26 | +import org.apache.camel.Endpoint; |
| 27 | +import org.apache.camel.Exchange; |
| 28 | +import org.apache.camel.telemetry.Op; |
| 29 | +import org.apache.camel.telemetry.SpanContextPropagationExtractor; |
| 30 | +import org.apache.camel.telemetry.SpanContextPropagationInjector; |
| 31 | +import org.apache.camel.telemetry.SpanLifecycleManager; |
| 32 | +import org.apache.camel.telemetry.decorators.PlatformHttpSpanDecorator; |
| 33 | +import org.apache.camel.telemetry.decorators.ServletSpanDecorator; |
| 34 | + |
| 35 | +public class CamelQuarkusOpenTelemetry2Tracer extends OpenTelemetryTracer { |
| 36 | + |
| 37 | + private final ThreadLocal<Op> currentOp = new ThreadLocal<>(); |
| 38 | + private final ThreadLocal<Endpoint> currentEndpoint = new ThreadLocal<>(); |
| 39 | + |
| 40 | + private Tracer otelTracer; |
| 41 | + private ContextPropagators contextPropagators; |
| 42 | + private org.apache.camel.telemetry.SpanDecoratorManager spanDecoratorManager; |
| 43 | + |
| 44 | + @Override |
| 45 | + public void setTracer(Tracer tracer) { |
| 46 | + super.setTracer(tracer); |
| 47 | + this.otelTracer = tracer; |
| 48 | + } |
| 49 | + |
| 50 | + @Override |
| 51 | + public void setContextPropagators(ContextPropagators contextPropagators) { |
| 52 | + super.setContextPropagators(contextPropagators); |
| 53 | + this.contextPropagators = contextPropagators; |
| 54 | + } |
| 55 | + |
| 56 | + @Override |
| 57 | + protected void beginEventSpan(Exchange exchange, Endpoint endpoint, Op op) throws Exception { |
| 58 | + currentOp.set(op); |
| 59 | + currentEndpoint.set(endpoint); |
| 60 | + try { |
| 61 | + super.beginEventSpan(exchange, endpoint, op); |
| 62 | + } finally { |
| 63 | + currentOp.remove(); |
| 64 | + currentEndpoint.remove(); |
| 65 | + } |
| 66 | + } |
| 67 | + |
| 68 | + @Override |
| 69 | + protected void initTracer() { |
| 70 | + super.initTracer(); |
| 71 | + setSpanLifecycleManager(new QuarkusSpanLifecycleManager()); |
| 72 | + // Use reflection to get the private spanDecoratorManager from Tracer |
| 73 | + try { |
| 74 | + java.lang.reflect.Field field = org.apache.camel.telemetry.Tracer.class.getDeclaredField("spanDecoratorManager"); |
| 75 | + field.setAccessible(true); |
| 76 | + this.spanDecoratorManager = (org.apache.camel.telemetry.SpanDecoratorManager) field.get(this); |
| 77 | + } catch (Exception e) { |
| 78 | + throw new RuntimeException("Failed to access spanDecoratorManager from Tracer", e); |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + private org.apache.camel.telemetry.SpanDecoratorManager getSpanDecoratorManager() { |
| 83 | + return spanDecoratorManager; |
| 84 | + } |
| 85 | + |
| 86 | + private class QuarkusSpanLifecycleManager implements SpanLifecycleManager { |
| 87 | + |
| 88 | + @Override |
| 89 | + public org.apache.camel.telemetry.Span create(String spanName, org.apache.camel.telemetry.Span parent, |
| 90 | + SpanContextPropagationExtractor extractor) { |
| 91 | + Endpoint endpoint = currentEndpoint.get(); |
| 92 | + if (endpoint != null) { |
| 93 | + org.apache.camel.telemetry.SpanDecorator sd = CamelQuarkusOpenTelemetry2Tracer.this.getSpanDecoratorManager() |
| 94 | + .get(endpoint); |
| 95 | + if (sd instanceof PlatformHttpSpanDecorator || sd instanceof ServletSpanDecorator) { |
| 96 | + io.opentelemetry.api.trace.Span span = io.opentelemetry.api.trace.Span.fromContext(Context.current()); |
| 97 | + Baggage baggage = Baggage.fromContext(Context.current()); |
| 98 | + if (span != null && span.getSpanContext().isValid()) { |
| 99 | + return new OpenTelemetrySpanAdapter(span, baggage); |
| 100 | + } |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + SpanBuilder builder = otelTracer.spanBuilder(spanName); |
| 105 | + Baggage baggage = null; |
| 106 | + if (parent != null) { |
| 107 | + OpenTelemetrySpanAdapter otelParent = (OpenTelemetrySpanAdapter) parent; |
| 108 | + builder.setParent(Context.current().with(otelParent.getSpan())); |
| 109 | + baggage = otelParent.getBaggage(); |
| 110 | + } else { |
| 111 | + Context extractedContext = contextPropagators.getTextMapPropagator().extract(Context.root(), extractor, |
| 112 | + new TextMapGetter<SpanContextPropagationExtractor>() { |
| 113 | + @Override |
| 114 | + public Iterable<String> keys(SpanContextPropagationExtractor carrier) { |
| 115 | + return carrier.keys(); |
| 116 | + } |
| 117 | + |
| 118 | + @Override |
| 119 | + public String get(SpanContextPropagationExtractor carrier, String key) { |
| 120 | + return (String) carrier.get(key); |
| 121 | + } |
| 122 | + }); |
| 123 | + builder.setParent(extractedContext); |
| 124 | + baggage = Baggage.fromContext(extractedContext); |
| 125 | + } |
| 126 | + |
| 127 | + Op op = currentOp.get(); |
| 128 | + if (op != null) { |
| 129 | + builder.setSpanKind(mapOpToKind(op)); |
| 130 | + } |
| 131 | + |
| 132 | + return new OpenTelemetrySpanAdapter(builder.startSpan(), baggage); |
| 133 | + } |
| 134 | + |
| 135 | + private SpanKind mapOpToKind(Op op) { |
| 136 | + if (op == null) { |
| 137 | + return SpanKind.INTERNAL; |
| 138 | + } |
| 139 | + switch (op) { |
| 140 | + case EVENT_SENT: |
| 141 | + return SpanKind.CLIENT; |
| 142 | + case EVENT_RECEIVED: |
| 143 | + return SpanKind.SERVER; |
| 144 | + default: |
| 145 | + return SpanKind.INTERNAL; |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | + @Override |
| 150 | + public void activate(org.apache.camel.telemetry.Span span) { |
| 151 | + ((OpenTelemetrySpanAdapter) span).makeCurrent(); |
| 152 | + } |
| 153 | + |
| 154 | + @Override |
| 155 | + public void deactivate(org.apache.camel.telemetry.Span span) { |
| 156 | + ((OpenTelemetrySpanAdapter) span).end(); |
| 157 | + } |
| 158 | + |
| 159 | + @Override |
| 160 | + public void close(org.apache.camel.telemetry.Span span) { |
| 161 | + ((OpenTelemetrySpanAdapter) span).close(); |
| 162 | + } |
| 163 | + |
| 164 | + @Override |
| 165 | + public void inject(org.apache.camel.telemetry.Span span, SpanContextPropagationInjector injector, |
| 166 | + boolean includeTracing) { |
| 167 | + OpenTelemetrySpanAdapter otelSpan = (OpenTelemetrySpanAdapter) span; |
| 168 | + Context ctx = Context.current().with(otelSpan.getSpan()); |
| 169 | + if (otelSpan.getBaggage() != null) { |
| 170 | + ctx = ctx.with(otelSpan.getBaggage()); |
| 171 | + } |
| 172 | + contextPropagators.getTextMapPropagator().inject(ctx, injector, (carrier, key, value) -> carrier.put(key, value)); |
| 173 | + |
| 174 | + if (includeTracing) { |
| 175 | + injector.put("CAMEL_TRACE_ID", otelSpan.getSpan().getSpanContext().getTraceId()); |
| 176 | + injector.put("CAMEL_SPAN_ID", otelSpan.getSpan().getSpanContext().getSpanId()); |
| 177 | + } |
| 178 | + } |
| 179 | + } |
| 180 | +} |
0 commit comments