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

io.datarouter.opencensus.adapter.DatarouterOpencensusTraceExporter Maven / Gradle / Ivy

/*
 * Copyright © 2009 HotPads ([email protected])
 *
 * 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.datarouter.opencensus.adapter;

import java.time.Instant;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.datarouter.instrumentation.trace.TraceBundleAndHttpRequestRecordDto;
import io.datarouter.instrumentation.trace.TraceBundleDto;
import io.datarouter.instrumentation.trace.TraceSpanDto;
import io.datarouter.instrumentation.trace.TraceSpanGroupType;
import io.datarouter.instrumentation.trace.Traceparent;
import io.datarouter.scanner.Scanner;
import io.datarouter.trace.conveyor.TraceBuffers;
import io.opencensus.common.Timestamp;
import io.opencensus.trace.AttributeValue;
import io.opencensus.trace.SpanId;
import io.opencensus.trace.export.SpanData;
import io.opencensus.trace.export.SpanExporter.Handler;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;

@Singleton
public class DatarouterOpencensusTraceExporter extends Handler{
	private static final Logger logger = LoggerFactory.getLogger(DatarouterOpencensusTraceExporter.class);

	@Inject
	private TraceBuffers traceBuffers;

	@Override
	public void export(Collection spanDataList){
		logger.info("DatarouterOpencensusTraceExporter starts exporting spanData from opencensus");
		Map bindingSpans = Scanner.of(spanDataList)
				.include(spanData -> spanData.getAttributes().getAttributeMap()
						.containsKey(DatarouterOpencensusTool.TRACEPARENT_ATTRIBUTE_KEY))
				.toMap(spanData -> spanData.getContext().getSpanId());
		if(bindingSpans.isEmpty()){
			logger.info("no binding spans found.");
			return;
		}
		Map parents = Scanner.of(spanDataList)
				.exclude(span -> span.getParentSpanId() == null)
				.toMap(span -> span.getContext().getSpanId(), SpanData::getParentSpanId);
		Map> sequenceByThreadIdByTraceparent = new HashMap<>();
		Map sequenceBySpanId = new HashMap<>();
		Scanner.of(spanDataList)
				.exclude(spanData -> bindingSpans.containsKey(spanData.getContext().getSpanId()))
				.map(spanData -> {
					SpanData bindingSpan = bindingSpans.get(findBindingSpan(spanData.getContext().getSpanId(),
							parents));
					if(bindingSpan == null){
						logger.info("No binding span for: {}", spanData);
						return null;
					}
					Map attributes = bindingSpan.getAttributes().getAttributeMap();
					Traceparent traceparent = Traceparent.parseIfValid(getInnerValue(attributes.get(
							DatarouterOpencensusTool.TRACEPARENT_ATTRIBUTE_KEY))).get();
					Long threadId = getInnerValue(attributes.get(DatarouterOpencensusTool.THREAD_ID_ATTRIBUTE_KEY));
					Function nextSequenceGenerator = $ -> sequenceByThreadIdByTraceparent
							.computeIfAbsent(traceparent, $$ -> new HashMap<>())
							.compute(threadId, ($$, old) -> old == null ? 10000 : old + 1);
					Integer sequenceParent;
					if(bindingSpan.getContext().getSpanId().equals(spanData.getParentSpanId())){
						sequenceParent = getIntegerValue(attributes.get(
								DatarouterOpencensusTool.SEQUENCE_PARENT_ATTRIBUTE_KEY));
					}else{
						sequenceParent = sequenceBySpanId.computeIfAbsent(spanData.getParentSpanId(),
								nextSequenceGenerator);
					}
					Integer sequence = sequenceBySpanId.computeIfAbsent(spanData.getContext().getSpanId(),
							nextSequenceGenerator);
					return new TraceSpanDto(
							traceparent,
							threadId,
							sequence,
							sequenceParent,
							spanData.getName(),
							TraceSpanGroupType.DATABASE,
							null,
							toNanoTimestamp(toInstant(spanData.getStartTimestamp())),
							toNanoTimestamp(toInstant(spanData.getEndTimestamp())));
				})
				.include(Objects::nonNull)
				.groupBy(TraceSpanDto::getTraceparent)
				.values()
				.stream()
				.map(spans -> new TraceBundleDto(null, List.of(), spans))
				.map(bundleDto -> new TraceBundleAndHttpRequestRecordDto(bundleDto, null))
				.forEach(traceBuffers::offer);
	}

	private static int getIntegerValue(AttributeValue attributeValue){
		return DatarouterOpencensusTraceExporter.getInnerValue(attributeValue).intValue();
	}

	@SuppressWarnings("unchecked")
	private static  T getInnerValue(AttributeValue attributeValue){
		return (T) attributeValue.match(x -> x, x -> x, x -> x, x -> x, x -> x);
	}

	private static Instant toInstant(Timestamp timestamp){
		return Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanos());
	}

	private static SpanId findBindingSpan(SpanId spanId, Map parents){
		SpanId bindingSpan = spanId;
		while(parents.containsKey(bindingSpan)){
			bindingSpan = parents.get(bindingSpan);
		}
		return bindingSpan;
	}

	private static Long toNanoTimestamp(Instant instant){
		return instant.getEpochSecond() * 1_000_000_000 + instant.getNano();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy