All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.helidon.tracing.providers.opentelemetry.OpenTelemetryTracer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2022, 2024 Oracle and/or its affiliates.
 *
 * Licensed 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
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.helidon.tracing.providers.opentelemetry;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;

import io.helidon.common.HelidonServiceLoader;
import io.helidon.common.LazyValue;
import io.helidon.common.config.Config;
import io.helidon.tracing.HeaderConsumer;
import io.helidon.tracing.HeaderProvider;
import io.helidon.tracing.Span;
import io.helidon.tracing.SpanContext;
import io.helidon.tracing.SpanListener;
import io.helidon.tracing.Tracer;
import io.helidon.tracing.TracerBuilder;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapGetter;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.context.propagation.TextMapSetter;

class OpenTelemetryTracer implements Tracer {

    private static final TextMapGetter GETTER = new Getter();
    private static final TextMapSetter SETTER = new Setter();

    private static final LazyValue> SPAN_LISTENERS =
            LazyValue.create(() -> HelidonServiceLoader.create(ServiceLoader.load(SpanListener.class)).asList());

    private final OpenTelemetry telemetry;
    private final io.opentelemetry.api.trace.Tracer delegate;
    private final boolean enabled;
    private final TextMapPropagator propagator;
    private final Map tags;
    private final List spanListeners = new ArrayList<>(SPAN_LISTENERS.get());

    OpenTelemetryTracer(OpenTelemetry telemetry, io.opentelemetry.api.trace.Tracer tracer, Map tags) {
        this.telemetry = telemetry;
        this.delegate = tracer;
        this.enabled = !tracer.getClass().getSimpleName().equals("DefaultTracer");
        this.propagator = telemetry.getPropagators().getTextMapPropagator();
        this.tags = tags;
    }

    static Builder builder() {
        return new Builder();
    }

    @Override
    public boolean enabled() {
        return enabled;
    }

    @Override
    public Span.Builder spanBuilder(String name) {
        OpenTelemetrySpanBuilder builder = new OpenTelemetrySpanBuilder(delegate.spanBuilder(name),
                                                                        spanListeners);
        Span.current().map(Span::context).ifPresent(builder::parent);
        tags.forEach(builder::tag);
        return builder;
    }

    @Override
    public Optional extract(HeaderProvider headersProvider) {
        Context context = propagator.extract(Context.current(), headersProvider, GETTER);

        return Optional.ofNullable(context)
                .map(OpenTelemetrySpanContext::new);
    }

    @Override
    public void inject(SpanContext spanContext, HeaderProvider inboundHeadersProvider, HeaderConsumer outboundHeadersConsumer) {
        propagator.inject(((OpenTelemetrySpanContext) spanContext).openTelemetry(), outboundHeadersConsumer, SETTER);
    }

    @Override
    public  T unwrap(Class tracerClass) {
        if (tracerClass.isAssignableFrom(delegate.getClass())) {
            return tracerClass.cast(delegate);
        }
        if (tracerClass.isInstance(this)) {
            return tracerClass.cast(this);
        }

        throw new IllegalArgumentException("Cannot provide an instance of " + tracerClass.getName()
                                                   + ", telemetry tracer is: " + delegate.getClass().getName());
    }

    @Override
    public Tracer register(SpanListener listener) {
        spanListeners.add(listener);
        return this;
    }

    List spanListeners() {
        return Collections.unmodifiableList(spanListeners);
    }

    static class Builder implements TracerBuilder {
        private OpenTelemetry ot;
        private String serviceName = "helidon-service";
        private boolean registerGlobal;

        @Override
        public Tracer build() {
            if (ot == null) {
                ot = GlobalOpenTelemetry.get();
            }
            io.opentelemetry.api.trace.Tracer tracer = ot.getTracer(serviceName);
            Tracer result = new OpenTelemetryTracer(ot, tracer, Map.of());
            if (registerGlobal) {
                Tracer.global(result);
            }
            return result;
        }

        Builder openTelemetry(OpenTelemetry ot) {
            this.ot = ot;
            return this;
        }

        @Override
        public Builder serviceName(String name) {
            this.serviceName = name;
            return this;
        }

        @Override
        public Builder collectorProtocol(String protocol) {
            return this;
        }

        @Override
        public Builder collectorPort(int port) {
            return this;
        }

        @Override
        public Builder collectorHost(String host) {
            return this;
        }

        @Override
        public Builder collectorPath(String path) {
            return this;
        }

        @Override
        public Builder addTracerTag(String key, String value) {
            return this;
        }

        @Override
        public Builder addTracerTag(String key, Number value) {
            return this;
        }

        @Override
        public Builder addTracerTag(String key, boolean value) {
            return this;
        }

        @Override
        public Builder config(Config config) {
            return this;
        }

        @Override
        public Builder enabled(boolean enabled) {
            return this;
        }

        @Override
        public Builder registerGlobal(boolean global) {
            this.registerGlobal = global;
            return this;
        }

        @Override
        public  B unwrap(Class builderClass) {
            if (builderClass.isAssignableFrom(getClass())) {
                return builderClass.cast(this);
            }
            throw new IllegalArgumentException("Cannot unwrap " + builderClass + " from Opentelmetry tracer builder.");
        }
    }

    private static class Getter implements TextMapGetter {
        @Override
        public Iterable keys(HeaderProvider headerProvider) {
            return headerProvider.keys();
        }

        @Override
        public String get(HeaderProvider headerProvider, String s) {
            return headerProvider.get(s).orElse(null);
        }
    }

    private static class Setter implements TextMapSetter {
        @Override
        public void set(HeaderConsumer carrier, String key, String value) {
            carrier.set(key, value);
        }
    }
}