| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.htrace; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.htrace.impl.MilliSpan; |
| import org.apache.htrace.impl.TrueIfTracingSampler; |
| import org.apache.htrace.wrappers.TraceCallable; |
| import org.apache.htrace.wrappers.TraceRunnable; |
| |
| import java.util.concurrent.Callable; |
| |
| /** |
| * The Trace class is the primary way to interact with the library. It provides |
| * methods to create and manipulate spans. |
| * |
| * A 'Span' represents a length of time. It has many other attributes such as a |
| * description, ID, and even potentially a set of key/value strings attached to |
| * it. |
| * |
| * Each thread in your application has a single currently active currentSpan |
| * associated with it. When this is non-null, it represents the current |
| * operation that the thread is doing. Spans are NOT thread-safe, and must |
| * never be used by multiple threads at once. With care, it is possible to |
| * safely pass a Span object between threads, but in most cases this is not |
| * necessary. |
| * |
| * A 'TraceScope' can either be empty, or contain a Span. TraceScope objects |
| * implement the Java's Closeable interface. Similar to file descriptors, they |
| * must be closed after they are created. When a TraceScope contains a Span, |
| * this span is closed when the scope is closed. |
| * |
| * The 'startSpan' methods in this class do a few things: |
| * <ul> |
| * <li>Create a new Span which has this thread's currentSpan as one of its parents.</li> |
| * <li>Set currentSpan to the new Span.</li> |
| * <li>Create a TraceSpan object to manage the new Span.</li> |
| * </ul> |
| * |
| * Closing a TraceScope does a few things: |
| * <ul> |
| * <li>It closes the span which the scope was managing.</li> |
| * <li>Set currentSpan to the previous currentSpan (which may be null).</li> |
| * </ul> |
| */ |
| public class Trace { |
| private static final Log LOG = LogFactory.getLog(Trace.class); |
| |
| /** |
| * Creates a new trace scope. |
| * |
| * If this thread has a currently active trace span, the trace scope we create |
| * here will contain a new span descending from the currently active span. |
| * If there is no currently active trace span, the trace scope we create will |
| * be empty. |
| * |
| * @param description The description field for the new span to create. |
| */ |
| public static TraceScope startSpan(String description) { |
| return startSpan(description, TrueIfTracingSampler.INSTANCE); |
| } |
| |
| public static TraceScope startSpan(String description, TraceInfo tinfo) { |
| if (tinfo == null) return continueSpan(null); |
| Span newSpan = new MilliSpan.Builder(). |
| begin(System.currentTimeMillis()). |
| end(0). |
| description(description). |
| traceId(tinfo.traceId). |
| parents(new long[] { tinfo.spanId }). |
| processId(Tracer.getProcessId()). |
| build(); |
| return continueSpan(newSpan); |
| } |
| |
| /** |
| * Creates a new trace scope. |
| * |
| * If this thread has a currently active trace span, it must be the 'parent' |
| * span that you pass in here as a parameter. The trace scope we create here |
| * will contain a new span which is a child of 'parent'. |
| * |
| * @param description The description field for the new span to create. |
| */ |
| public static TraceScope startSpan(String description, Span parent) { |
| if (parent == null) { |
| return startSpan(description); |
| } |
| Span currentSpan = currentSpan(); |
| if ((currentSpan != null) && (currentSpan != parent)) { |
| Tracer.clientError("HTrace client error: thread " + |
| Thread.currentThread().getName() + " tried to start a new Span " + |
| "with parent " + parent.toString() + ", but there is already a " + |
| "currentSpan " + currentSpan); |
| } |
| return continueSpan(parent.child(description)); |
| } |
| |
| public static <T> TraceScope startSpan(String description, Sampler<T> s) { |
| return startSpan(description, s, null); |
| } |
| |
| public static <T> TraceScope startSpan(String description, Sampler<T> s, T info) { |
| Span span = null; |
| if (isTracing() || s.next(info)) { |
| span = Tracer.getInstance().createNew(description); |
| } |
| return continueSpan(span); |
| } |
| |
| /** |
| * Pick up an existing span from another thread. |
| */ |
| public static TraceScope continueSpan(Span s) { |
| // Return an empty TraceScope that does nothing on close |
| if (s == null) return NullScope.INSTANCE; |
| return Tracer.getInstance().continueSpan(s); |
| } |
| |
| /** |
| * Set the processId to be used for all Spans created by this Tracer. |
| * |
| * @see Span |
| */ |
| public static void setProcessId(String processId) { |
| Tracer.processId = processId; |
| } |
| |
| /** |
| * Removes the given SpanReceiver from the list of SpanReceivers. |
| */ |
| public static void removeReceiver(SpanReceiver rcvr) { |
| Tracer.getInstance().removeReceiver(rcvr); |
| } |
| |
| /** |
| * Adds the given SpanReceiver to the current Tracer instance's list of |
| * SpanReceivers. |
| */ |
| public static void addReceiver(SpanReceiver rcvr) { |
| Tracer.getInstance().addReceiver(rcvr); |
| } |
| |
| /** |
| * Adds a data annotation to the current span if tracing is currently on. |
| */ |
| public static void addKVAnnotation(byte[] key, byte[] value) { |
| Span s = currentSpan(); |
| if (s != null) { |
| s.addKVAnnotation(key, value); |
| } |
| } |
| |
| /** |
| * Annotate the current span with the given message. |
| */ |
| public static void addTimelineAnnotation(String msg) { |
| Span s = currentSpan(); |
| if (s != null) { |
| s.addTimelineAnnotation(msg); |
| } |
| } |
| |
| /** |
| * Returns true if the current thread is a part of a trace, false otherwise. |
| */ |
| public static boolean isTracing() { |
| return Tracer.getInstance().isTracing(); |
| } |
| |
| /** |
| * If we are tracing, return the current span, else null |
| * |
| * @return Span representing the current trace, or null if not tracing. |
| */ |
| public static Span currentSpan() { |
| return Tracer.getInstance().currentSpan(); |
| } |
| |
| /** |
| * Wrap the callable in a TraceCallable, if tracing. |
| * |
| * @return The callable provided, wrapped if tracing, 'callable' if not. |
| */ |
| public static <V> Callable<V> wrap(Callable<V> callable) { |
| if (isTracing()) { |
| return new TraceCallable<V>(Trace.currentSpan(), callable); |
| } else { |
| return callable; |
| } |
| } |
| |
| /** |
| * Wrap the runnable in a TraceRunnable, if tracing |
| * |
| * @return The runnable provided, wrapped if tracing, 'runnable' if not. |
| */ |
| public static Runnable wrap(Runnable runnable) { |
| if (isTracing()) { |
| return new TraceRunnable(Trace.currentSpan(), runnable); |
| } else { |
| return runnable; |
| } |
| } |
| |
| /** |
| * Wrap the runnable in a TraceRunnable, if tracing |
| * |
| * @param description name of the span to be created. |
| * @param runnable The runnable that will have tracing info associated with it if tracing. |
| * @return The runnable provided, wrapped if tracing, 'runnable' if not. |
| */ |
| public static Runnable wrap(String description, Runnable runnable) { |
| if (isTracing()) { |
| return new TraceRunnable(Trace.currentSpan(), runnable, description); |
| } else { |
| return runnable; |
| } |
| } |
| } |