Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.micrometer.tracing.otel.bridge.OtelBaggageManager Maven / Gradle / Ivy
/**
* Copyright 2022 the original author or authors.
*
* 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
*
* https://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.micrometer.tracing.otel.bridge;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import io.micrometer.common.lang.Nullable;
import io.micrometer.tracing.BaggageInScope;
import io.micrometer.tracing.BaggageManager;
import io.micrometer.tracing.CurrentTraceContext;
import io.micrometer.tracing.TraceContext;
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.baggage.BaggageBuilder;
import io.opentelemetry.api.baggage.BaggageEntry;
import io.opentelemetry.api.baggage.BaggageEntryMetadata;
import io.opentelemetry.context.Context;
import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.unmodifiableMap;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
/**
* OpenTelemetry implementation of a {@link BaggageManager}.
*
* @author Marcin Grzejszczak
* @since 1.0.0
*/
public class OtelBaggageManager implements BaggageManager {
/**
* Taken from one of the W3C OTel tests. Can't find it in a spec.
*/
private static final String PROPAGATION_UNLIMITED = "propagation=unlimited";
private final CurrentTraceContext currentTraceContext;
private final List remoteFields;
private final List tagFields;
/**
* Creates a new instance of {@link OtelBaggageManager}.
* @param currentTraceContext current trace context
* @param remoteFields remote fields names
* @param tagFields tag fields names
*/
public OtelBaggageManager(CurrentTraceContext currentTraceContext, List remoteFields,
List tagFields) {
this.currentTraceContext = currentTraceContext;
this.remoteFields = remoteFields;
this.tagFields = tagFields;
}
@Override
public Map getAllBaggage() {
return toMap(currentBaggage());
}
private Map toMap(CompositeBaggage compositeBaggage) {
Map baggage = new HashMap<>();
compositeBaggage.getEntries().forEach(entry -> baggage.put(entry.getKey(), entry.getValue()));
return baggage;
}
@Override
public Map getAllBaggage(TraceContext traceContext) {
if (traceContext == null) {
return getAllBaggage();
}
return toMap(baggage((OtelTraceContext) traceContext));
}
CompositeBaggage currentBaggage() {
return baggage((OtelTraceContext) currentTraceContext.context());
}
private CompositeBaggage baggage(OtelTraceContext traceContext) {
Context context = Context.current();
Deque stack = new ArrayDeque<>();
stack.addFirst(context);
if (traceContext != null && traceContext.context() != null) {
stack.addFirst(traceContext.context());
}
return new CompositeBaggage(stack);
}
@Override
public io.micrometer.tracing.Baggage getBaggage(String name) {
Entry entry = getBaggage(name, currentBaggage());
return createNewEntryIfMissing(name, entry);
}
io.micrometer.tracing.Baggage createNewEntryIfMissing(String name, Entry entry) {
if (entry == null) {
return createBaggage(name);
}
return otelBaggage(entry);
}
private Entry getBaggage(String name, io.opentelemetry.api.baggage.Baggage baggage) {
return entryForName(name, baggage);
}
@Override
public io.micrometer.tracing.Baggage getBaggage(TraceContext traceContext, String name) {
OtelTraceContext context = (OtelTraceContext) traceContext;
LinkedList stack = new LinkedList<>();
Context current = Context.current();
Context traceContextContext = context.context();
stack.addFirst(current);
if (!Objects.equals(current, traceContextContext)) {
stack.addFirst(traceContextContext);
}
Context ctx = removeFirst(stack);
Entry entry = null;
while (ctx != null && entry == null) {
entry = getBaggage(name, Baggage.fromContext(ctx));
ctx = removeFirst(stack);
}
if (entry != null) {
return otelBaggage(context, entry);
}
return null;
}
Entry getEntry(OtelTraceContext traceContext, String name) {
OtelTraceContext context = traceContext;
Context ctx = context.context();
return getBaggage(name, Baggage.fromContext(ctx));
}
Context removeFirst(Deque stack) {
return stack.isEmpty() ? null : stack.removeFirst();
}
private Entry entryForName(String name, io.opentelemetry.api.baggage.Baggage baggage) {
return Entry.fromBaggage(baggage)
.stream()
.filter(e -> e.getKey().equalsIgnoreCase(name))
.findFirst()
.orElse(null);
}
private io.micrometer.tracing.Baggage otelBaggage(Entry entry) {
return new OtelBaggageInScope(this, this.currentTraceContext, this.tagFields, entry);
}
private io.micrometer.tracing.Baggage otelBaggage(OtelTraceContext otelTraceContext, Entry entry) {
return new OtelBaggageInScope(this, this.currentTraceContext, otelTraceContext, this.tagFields, entry);
}
@Override
@Deprecated
public io.micrometer.tracing.Baggage createBaggage(String name) {
return createBaggage(name, null);
}
@Override
@Deprecated
public io.micrometer.tracing.Baggage createBaggage(String name, String value) {
io.micrometer.tracing.Baggage baggage = baggageWithValue(name, value);
return baggage.set(value);
}
@Override
public BaggageInScope createBaggageInScope(String name, String value) {
return baggageWithValue(name, value).makeCurrent();
}
@Override
public BaggageInScope createBaggageInScope(TraceContext traceContext, String name, String value) {
return baggageWithValue(name, value).makeCurrent(traceContext, value);
}
private io.micrometer.tracing.Baggage baggageWithValue(String name, @Nullable String value) {
boolean remoteField = this.remoteFields.stream()
.map(String::toLowerCase)
.anyMatch(s -> s.equals(name.toLowerCase()));
BaggageEntryMetadata entryMetadata = BaggageEntryMetadata.create(propagationString(remoteField));
Entry entry = new Entry(name, value, entryMetadata);
return new OtelBaggageInScope(this, this.currentTraceContext, this.tagFields, entry);
}
private String propagationString(boolean remoteField) {
String propagation = "";
if (remoteField) {
propagation = PROPAGATION_UNLIMITED;
}
return propagation;
}
}
class CompositeBaggage implements io.opentelemetry.api.baggage.Baggage {
private final Collection entries;
private final Map baggageEntries;
CompositeBaggage(Deque stack) {
this.entries = unmodifiableCollection(createEntries(stack));
this.baggageEntries = unmodifiableMap(this.entries.stream().collect(toMap(Entry::getKey, identity())));
}
private Collection createEntries(Deque stack) {
// parent baggage foo=bar
// child baggage foo=baz - we want the last one to override the previous one
Map map = new HashMap<>();
Iterator iterator = stack.descendingIterator();
while (iterator.hasNext()) {
Context next = iterator.next();
Baggage baggage = Baggage.fromContext(next);
baggage.forEach((key, value) -> map.put(key, new Entry(key, value.getValue(), value.getMetadata())));
}
return map.values();
}
Collection getEntries() {
return this.entries;
}
@Override
public int size() {
return this.entries.size();
}
@Override
public void forEach(BiConsumer super String, ? super BaggageEntry> consumer) {
this.entries.forEach(entry -> consumer.accept(entry.getKey(), entry));
}
@Override
public Map asMap() {
return this.baggageEntries;
}
@Override
public String getEntryValue(String entryKey) {
return this.entries.stream()
.filter(entry -> entryKey.equals(entry.getKey()))
.map(Entry::getValue)
.findFirst()
.orElse(null);
}
@Override
public BaggageBuilder toBuilder() {
return Baggage.builder();
}
}
class Entry implements BaggageEntry {
final String key;
final String value;
final BaggageEntryMetadata entryMetadata;
Entry(String key, String value, BaggageEntryMetadata entryMetadata) {
this.key = key;
this.value = value;
this.entryMetadata = entryMetadata;
}
static List fromBaggage(Baggage baggage) {
List list = new ArrayList<>(baggage.size());
baggage.forEach((key, value) -> list.add(new Entry(key, value.getValue(), value.getMetadata())));
return list;
}
public String getKey() {
return this.key;
}
@Override
public String getValue() {
return this.value;
}
@Override
public BaggageEntryMetadata getMetadata() {
return this.entryMetadata;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Entry entry = (Entry) o;
return Objects.equals(this.key, entry.key) && Objects.equals(this.value, entry.value)
&& Objects.equals(this.entryMetadata, entry.entryMetadata);
}
@Override
public int hashCode() {
return Objects.hash(this.key, this.value, this.entryMetadata);
}
}