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.opencensus.common.Scope; 20 import io.opencensus.trace.unsafe.ContextHandleUtils; 21 import java.util.concurrent.Callable; 22 import javax.annotation.Nullable; 23 24 /** Util methods/functionality to interact with the {@link Span} in the {@link io.grpc.Context}. */ 25 final class CurrentSpanUtils { 26 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 ContextHandleUtils.getValue(ContextHandleUtils.currentContext()); 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 82 private final ContextHandle origContext; 83 private final Span span; 84 private final boolean endSpan; 85 86 /** 87 * Constructs a new {@link ScopeInSpan}. 88 * 89 * @param span is the {@code Span} to be added to the current {@code io.grpc.Context}. 90 */ ScopeInSpan(Span span, boolean endSpan)91 private ScopeInSpan(Span span, boolean endSpan) { 92 this.span = span; 93 this.endSpan = endSpan; 94 origContext = 95 ContextHandleUtils.withValue(ContextHandleUtils.currentContext(), span).attach(); 96 } 97 98 @Override close()99 public void close() { 100 ContextHandleUtils.currentContext().detach(origContext); 101 if (endSpan) { 102 span.end(); 103 } 104 } 105 } 106 107 private static final class RunnableInSpan implements Runnable { 108 109 // TODO(bdrutu): Investigate if the extra private visibility increases the generated bytecode. 110 private final Span span; 111 private final Runnable runnable; 112 private final boolean endSpan; 113 RunnableInSpan(Span span, Runnable runnable, boolean endSpan)114 private RunnableInSpan(Span span, Runnable runnable, boolean endSpan) { 115 this.span = span; 116 this.runnable = runnable; 117 this.endSpan = endSpan; 118 } 119 120 @Override run()121 public void run() { 122 ContextHandle origContext = 123 ContextHandleUtils.withValue(ContextHandleUtils.currentContext(), span).attach(); 124 try { 125 runnable.run(); 126 } catch (Throwable t) { 127 setErrorStatus(span, t); 128 if (t instanceof RuntimeException) { 129 throw (RuntimeException) t; 130 } else if (t instanceof Error) { 131 throw (Error) t; 132 } 133 throw new RuntimeException("unexpected", t); 134 } finally { 135 ContextHandleUtils.currentContext().detach(origContext); 136 if (endSpan) { 137 span.end(); 138 } 139 } 140 } 141 } 142 143 private static final class CallableInSpan<V> implements Callable<V> { 144 145 private final Span span; 146 private final Callable<V> callable; 147 private final boolean endSpan; 148 CallableInSpan(Span span, Callable<V> callable, boolean endSpan)149 private CallableInSpan(Span span, Callable<V> callable, boolean endSpan) { 150 this.span = span; 151 this.callable = callable; 152 this.endSpan = endSpan; 153 } 154 155 @Override call()156 public V call() throws Exception { 157 ContextHandle origContext = 158 ContextHandleUtils.withValue(ContextHandleUtils.currentContext(), span).attach(); 159 try { 160 return callable.call(); 161 } catch (Exception e) { 162 setErrorStatus(span, e); 163 throw e; 164 } catch (Throwable t) { 165 setErrorStatus(span, t); 166 if (t instanceof Error) { 167 throw (Error) t; 168 } 169 throw new RuntimeException("unexpected", t); 170 } finally { 171 ContextHandleUtils.currentContext().detach(origContext); 172 if (endSpan) { 173 span.end(); 174 } 175 } 176 } 177 } 178 setErrorStatus(Span span, Throwable t)179 private static void setErrorStatus(Span span, Throwable t) { 180 span.setStatus( 181 Status.UNKNOWN.withDescription( 182 t.getMessage() == null ? t.getClass().getSimpleName() : t.getMessage())); 183 } 184 } 185