blob: 08e215ac6d1d5599a5e2431641bd7dbe2a967d88 [file] [log] [blame]
/*
* 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;
}
}
}