1 /* 2 * Copyright 2017, OpenCensus Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.opencensus.trace; 18 19 import io.grpc.Context; 20 import io.opencensus.common.Scope; 21 import io.opencensus.trace.unsafe.ContextUtils; 22 import java.util.concurrent.Callable; 23 import javax.annotation.Nullable; 24 25 /** Util methods/functionality to interact with the {@link Span} in the {@link io.grpc.Context}. */ 26 final class CurrentSpanUtils { 27 // No instance of this class. CurrentSpanUtils()28 private CurrentSpanUtils() {} 29 30 /** 31 * Returns The {@link Span} from the current context. 32 * 33 * @return The {@code Span} from the current context. 34 */ 35 @Nullable getCurrentSpan()36 static Span getCurrentSpan() { 37 return ContextUtils.CONTEXT_SPAN_KEY.get(); 38 } 39 40 /** 41 * Enters the scope of code where the given {@link Span} is in the current context, and returns an 42 * object that represents that scope. The scope is exited when the returned object is closed. 43 * 44 * <p>Supports try-with-resource idiom. 45 * 46 * @param span The {@code Span} to be set to the current context. 47 * @param endSpan if {@code true} the returned {@code Scope} will close the {@code Span}. 48 * @return An object that defines a scope where the given {@code Span} is set to the current 49 * context. 50 */ withSpan(Span span, boolean endSpan)51 static Scope withSpan(Span span, boolean endSpan) { 52 return new ScopeInSpan(span, endSpan); 53 } 54 55 /** 56 * Wraps a {@link Runnable} so that it executes with the {@code span} as the current {@code Span}. 57 * 58 * @param span the {@code Span} to be set as current. 59 * @param endSpan if {@code true} the returned {@code Runnable} will close the {@code Span}. 60 * @param runnable the {@code Runnable} to run in the {@code Span}. 61 * @return the wrapped {@code Runnable}. 62 */ withSpan(Span span, boolean endSpan, Runnable runnable)63 static Runnable withSpan(Span span, boolean endSpan, Runnable runnable) { 64 return new RunnableInSpan(span, runnable, endSpan); 65 } 66 67 /** 68 * Wraps a {@link Callable} so that it executes with the {@code span} as the current {@code Span}. 69 * 70 * @param span the {@code Span} to be set as current. 71 * @param endSpan if {@code true} the returned {@code Runnable} will close the {@code Span}. 72 * @param callable the {@code Callable} to run in the {@code Span}. 73 * @return the wrapped {@code Callable}. 74 */ withSpan(Span span, boolean endSpan, Callable<C> callable)75 static <C> Callable<C> withSpan(Span span, boolean endSpan, Callable<C> callable) { 76 return new CallableInSpan<C>(span, callable, endSpan); 77 } 78 79 // Defines an arbitrary scope of code as a traceable operation. Supports try-with-resources idiom. 80 private static final class ScopeInSpan implements Scope { 81 private final Context origContext; 82 private final Span span; 83 private final boolean endSpan; 84 85 /** 86 * Constructs a new {@link ScopeInSpan}. 87 * 88 * @param span is the {@code Span} to be added to the current {@code io.grpc.Context}. 89 */ ScopeInSpan(Span span, boolean endSpan)90 private ScopeInSpan(Span span, boolean endSpan) { 91 this.span = span; 92 this.endSpan = endSpan; 93 origContext = Context.current().withValue(ContextUtils.CONTEXT_SPAN_KEY, span).attach(); 94 } 95 96 @Override close()97 public void close() { 98 Context.current().detach(origContext); 99 if (endSpan) { 100 span.end(); 101 } 102 } 103 } 104 105 private static final class RunnableInSpan implements Runnable { 106 // TODO(bdrutu): Investigate if the extra private visibility increases the generated bytecode. 107 private final Span span; 108 private final Runnable runnable; 109 private final boolean endSpan; 110 RunnableInSpan(Span span, Runnable runnable, boolean endSpan)111 private RunnableInSpan(Span span, Runnable runnable, boolean endSpan) { 112 this.span = span; 113 this.runnable = runnable; 114 this.endSpan = endSpan; 115 } 116 117 @Override run()118 public void run() { 119 Context origContext = 120 Context.current().withValue(ContextUtils.CONTEXT_SPAN_KEY, span).attach(); 121 try { 122 runnable.run(); 123 } catch (Throwable t) { 124 setErrorStatus(span, t); 125 if (t instanceof RuntimeException) { 126 throw (RuntimeException) t; 127 } else if (t instanceof Error) { 128 throw (Error) t; 129 } 130 throw new RuntimeException("unexpected", t); 131 } finally { 132 Context.current().detach(origContext); 133 if (endSpan) { 134 span.end(); 135 } 136 } 137 } 138 } 139 140 private static final class CallableInSpan<V> implements Callable<V> { 141 private final Span span; 142 private final Callable<V> callable; 143 private final boolean endSpan; 144 CallableInSpan(Span span, Callable<V> callable, boolean endSpan)145 private CallableInSpan(Span span, Callable<V> callable, boolean endSpan) { 146 this.span = span; 147 this.callable = callable; 148 this.endSpan = endSpan; 149 } 150 151 @Override call()152 public V call() throws Exception { 153 Context origContext = 154 Context.current().withValue(ContextUtils.CONTEXT_SPAN_KEY, span).attach(); 155 try { 156 return callable.call(); 157 } catch (Exception e) { 158 setErrorStatus(span, e); 159 throw e; 160 } catch (Throwable t) { 161 setErrorStatus(span, t); 162 if (t instanceof Error) { 163 throw (Error) t; 164 } 165 throw new RuntimeException("unexpected", t); 166 } finally { 167 Context.current().detach(origContext); 168 if (endSpan) { 169 span.end(); 170 } 171 } 172 } 173 } 174 setErrorStatus(Span span, Throwable t)175 private static void setErrorStatus(Span span, Throwable t) { 176 span.setStatus( 177 Status.UNKNOWN.withDescription( 178 t.getMessage() == null ? t.getClass().getSimpleName() : t.getMessage())); 179 } 180 } 181