| /* |
| * Copyright 2018, OpenCensus 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 |
| * |
| * 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.opencensus.contrib.spring.sleuth.v1x; |
| |
| import io.opencensus.common.ExperimentalApi; |
| import java.util.Random; |
| import java.util.concurrent.Callable; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.springframework.cloud.sleuth.Sampler; |
| import org.springframework.cloud.sleuth.Span; |
| import org.springframework.cloud.sleuth.SpanNamer; |
| import org.springframework.cloud.sleuth.SpanReporter; |
| import org.springframework.cloud.sleuth.TraceKeys; |
| import org.springframework.cloud.sleuth.Tracer; |
| import org.springframework.cloud.sleuth.instrument.async.SpanContinuingTraceCallable; |
| import org.springframework.cloud.sleuth.instrument.async.SpanContinuingTraceRunnable; |
| import org.springframework.cloud.sleuth.log.SpanLogger; |
| import org.springframework.cloud.sleuth.util.ExceptionUtils; |
| import org.springframework.cloud.sleuth.util.SpanNameUtil; |
| |
| /*>>> |
| import org.checkerframework.checker.nullness.qual.Nullable; |
| */ |
| |
| /** |
| * Sleuth Tracer that keeps a synchronized OpenCensus Span. This class is based on Sleuth's {@code |
| * DefaultTracer}. |
| * |
| * @since 0.16 |
| */ |
| @ExperimentalApi |
| public class OpenCensusSleuthTracer implements Tracer { |
| private static final Log log = LogFactory.getLog(OpenCensusSleuthTracer.class); |
| private final Sampler defaultSampler; |
| private final Random random; |
| private final SpanNamer spanNamer; |
| private final SpanLogger spanLogger; |
| private final SpanReporter spanReporter; |
| private final TraceKeys traceKeys; |
| private final boolean traceId128; |
| |
| /** Basic constructor holding components for implementing Sleuth's {@link Tracer} interface. */ |
| public OpenCensusSleuthTracer( |
| Sampler defaultSampler, |
| Random random, |
| SpanNamer spanNamer, |
| SpanLogger spanLogger, |
| SpanReporter spanReporter, |
| TraceKeys traceKeys) { |
| this( |
| defaultSampler, |
| random, |
| spanNamer, |
| spanLogger, |
| spanReporter, |
| traceKeys, |
| /* traceId128= */ false); |
| } |
| |
| /** Basic constructor holding components for implementing Sleuth's {@link Tracer} interface. */ |
| public OpenCensusSleuthTracer( |
| Sampler defaultSampler, |
| Random random, |
| SpanNamer spanNamer, |
| SpanLogger spanLogger, |
| SpanReporter spanReporter, |
| TraceKeys traceKeys, |
| boolean traceId128) { |
| this.defaultSampler = defaultSampler; |
| this.random = random; |
| this.spanNamer = spanNamer; |
| this.spanLogger = spanLogger; |
| this.spanReporter = spanReporter; |
| this.traceId128 = traceId128; |
| this.traceKeys = traceKeys != null ? traceKeys : new TraceKeys(); |
| } |
| |
| @Override |
| @javax.annotation.Nullable |
| public Span createSpan(String name, /*@Nullable*/ Span parent) { |
| if (parent == null) { |
| return createSpan(name); |
| } |
| return continueSpan(createChild(parent, name)); |
| } |
| |
| @Override |
| @javax.annotation.Nullable |
| public Span createSpan(String name) { |
| return this.createSpan(name, this.defaultSampler); |
| } |
| |
| @Override |
| @javax.annotation.Nullable |
| public Span createSpan(String name, /*@Nullable*/ Sampler sampler) { |
| String shortenedName = SpanNameUtil.shorten(name); |
| Span span; |
| if (isTracing()) { |
| span = createChild(getCurrentSpan(), shortenedName); |
| } else { |
| long id = createId(); |
| span = |
| Span.builder() |
| .name(shortenedName) |
| .traceIdHigh(this.traceId128 ? createTraceIdHigh() : 0L) |
| .traceId(id) |
| .spanId(id) |
| .build(); |
| if (sampler == null) { |
| sampler = this.defaultSampler; |
| } |
| span = sampledSpan(span, sampler); |
| this.spanLogger.logStartedSpan(null, span); |
| } |
| return continueSpan(span); |
| } |
| |
| @Override |
| @javax.annotation.Nullable |
| public Span detach(/*@Nullable*/ Span span) { |
| if (span == null) { |
| return null; |
| } |
| Span current = OpenCensusSleuthSpanContextHolder.getCurrentSpan(); |
| if (current == null) { |
| if (log.isTraceEnabled()) { |
| log.trace( |
| "Span in the context is null so something has already detached the span. " |
| + "Won't do anything about it"); |
| } |
| return null; |
| } |
| if (!span.equals(current)) { |
| ExceptionUtils.warn( |
| "Tried to detach trace span but " |
| + "it is not the current span: " |
| + span |
| + ". You may have forgotten to close or detach " |
| + current); |
| } else { |
| OpenCensusSleuthSpanContextHolder.removeCurrentSpan(); |
| } |
| return span.getSavedSpan(); |
| } |
| |
| @Override |
| @javax.annotation.Nullable |
| public Span close(/*@Nullable*/ Span span) { |
| if (span == null) { |
| return null; |
| } |
| final Span savedSpan = span.getSavedSpan(); |
| Span current = OpenCensusSleuthSpanContextHolder.getCurrentSpan(); |
| if (current == null || !span.equals(current)) { |
| ExceptionUtils.warn( |
| "Tried to close span but it is not the current span: " |
| + span |
| + ". You may have forgotten to close or detach " |
| + current); |
| } else { |
| span.stop(); |
| if (savedSpan != null && span.getParents().contains(savedSpan.getSpanId())) { |
| this.spanReporter.report(span); |
| this.spanLogger.logStoppedSpan(savedSpan, span); |
| } else { |
| if (!span.isRemote()) { |
| this.spanReporter.report(span); |
| this.spanLogger.logStoppedSpan(null, span); |
| } |
| } |
| OpenCensusSleuthSpanContextHolder.close( |
| new OpenCensusSleuthSpanContextHolder.SpanFunction() { |
| @Override |
| public void apply(Span closedSpan) { |
| // Note: hasn't this already been done? |
| OpenCensusSleuthTracer.this.spanLogger.logStoppedSpan(savedSpan, closedSpan); |
| } |
| }); |
| } |
| return savedSpan; |
| } |
| |
| Span createChild(/*@Nullable*/ Span parent, String name) { |
| String shortenedName = SpanNameUtil.shorten(name); |
| long id = createId(); |
| if (parent == null) { |
| Span span = |
| Span.builder() |
| .name(shortenedName) |
| .traceIdHigh(this.traceId128 ? createTraceIdHigh() : 0L) |
| .traceId(id) |
| .spanId(id) |
| .build(); |
| span = sampledSpan(span, this.defaultSampler); |
| this.spanLogger.logStartedSpan(null, span); |
| return span; |
| } else { |
| if (!isTracing()) { |
| OpenCensusSleuthSpanContextHolder.push(parent, /* autoClose= */ true); |
| } |
| Span span = |
| Span.builder() |
| .name(shortenedName) |
| .traceIdHigh(parent.getTraceIdHigh()) |
| .traceId(parent.getTraceId()) |
| .parent(parent.getSpanId()) |
| .spanId(id) |
| .processId(parent.getProcessId()) |
| .savedSpan(parent) |
| .exportable(parent.isExportable()) |
| .baggage(parent.getBaggage()) |
| .build(); |
| this.spanLogger.logStartedSpan(parent, span); |
| return span; |
| } |
| } |
| |
| private static Span sampledSpan(Span span, Sampler sampler) { |
| if (!sampler.isSampled(span)) { |
| // Copy everything, except set exportable to false |
| return Span.builder() |
| .begin(span.getBegin()) |
| .traceIdHigh(span.getTraceIdHigh()) |
| .traceId(span.getTraceId()) |
| .spanId(span.getSpanId()) |
| .name(span.getName()) |
| .exportable(false) |
| .build(); |
| } |
| return span; |
| } |
| |
| // Encodes a timestamp into the upper 32-bits, so that it can be converted to an Amazon trace ID. |
| // For example, an Amazon trace ID is composed of the following: |
| // |-- 32 bits for epoch seconds -- | -- 96 bits for random data -- | |
| // |
| // To support this, Span#getTraceIdHigh() holds the epoch seconds and first 32 random bits: and |
| // Span#getTraceId() holds the remaining 64 random bits. |
| private long createTraceIdHigh() { |
| long epochSeconds = System.currentTimeMillis() / 1000; |
| int random = this.random.nextInt(); |
| return (epochSeconds & 0xffffffffL) << 32 | (random & 0xffffffffL); |
| } |
| |
| private long createId() { |
| return this.random.nextLong(); |
| } |
| |
| @Override |
| @javax.annotation.Nullable |
| public Span continueSpan(/*@Nullable*/ Span span) { |
| if (span != null) { |
| this.spanLogger.logContinuedSpan(span); |
| } else { |
| return null; |
| } |
| Span newSpan = createContinuedSpan(span, OpenCensusSleuthSpanContextHolder.getCurrentSpan()); |
| OpenCensusSleuthSpanContextHolder.setCurrentSpan(newSpan); |
| return newSpan; |
| } |
| |
| @SuppressWarnings("deprecation") |
| private static Span createContinuedSpan(Span span, /*@Nullable*/ Span saved) { |
| if (saved == null && span.getSavedSpan() != null) { |
| saved = span.getSavedSpan(); |
| } |
| return new Span(span, saved); |
| } |
| |
| @Override |
| @javax.annotation.Nullable |
| public Span getCurrentSpan() { |
| return OpenCensusSleuthSpanContextHolder.getCurrentSpan(); |
| } |
| |
| @Override |
| public boolean isTracing() { |
| return OpenCensusSleuthSpanContextHolder.isTracing(); |
| } |
| |
| @Override |
| public void addTag(String key, String value) { |
| Span s = getCurrentSpan(); |
| if (s != null && s.isExportable()) { |
| s.tag(key, value); |
| } |
| } |
| |
| /** |
| * Wrap the callable in a TraceCallable, if tracing. |
| * |
| * @return The callable provided, wrapped if tracing, 'callable' if not. |
| */ |
| @Override |
| public <V> Callable<V> wrap(Callable<V> callable) { |
| if (isTracing()) { |
| return new SpanContinuingTraceCallable<V>(this, this.traceKeys, this.spanNamer, callable); |
| } |
| return callable; |
| } |
| |
| /** |
| * Wrap the runnable in a TraceRunnable, if tracing. |
| * |
| * @return The runnable provided, wrapped if tracing, 'runnable' if not. |
| */ |
| @Override |
| public Runnable wrap(Runnable runnable) { |
| if (isTracing()) { |
| return new SpanContinuingTraceRunnable(this, this.traceKeys, this.spanNamer, runnable); |
| } |
| return runnable; |
| } |
| } |