zipkin2.reporter.brave.ZipkinSpanHandler Maven / Gradle / Ivy
Show all versions of zipkin-reporter-brave Show documentation
/*
 * Copyright The OpenZipkin Authors
 * SPDX-License-Identifier: Apache-2.0
 */
package zipkin2.reporter.brave;
import brave.Tag;
import brave.Tags;
import brave.handler.MutableSpan;
import brave.handler.SpanHandler;
import brave.propagation.TraceContext;
import java.io.Closeable;
import zipkin2.Span;
import zipkin2.reporter.Reporter;
import zipkin2.reporter.SpanBytesEncoder;
/**
 * This allows you to send spans recorded by Brave to a pre-configured {@linkplain Reporter Zipkin
 * reporter}.
 *
 * If you can't use {@link AsyncZipkinSpanHandler} because you are using an old format,
 * construct the span handler like this:
 * 
{@code
 * reporter = AsyncReporter.builder(URLConnectionSender.create("http://localhost:9411/api/v1/spans"))
 *                         .build(SpanBytesEncoder.JSON_V1);
 * tracingBuilder.addSpanHandler(ZipkinSpanHandler.create(reporter));
 * }
 *
 * Note: Use {@link AsyncZipkinSpanHandler} if you are using {@link
 * SpanBytesEncoder#JSON_V2} format. This handler has to convert {@link MutableSpan} into {@link
 * Span}, and that conversion happens inline (during production requests) upon {@link
 * brave.Span#finish()} or {@link brave.Span#flush()}.
 *
 * @see AsyncZipkinSpanHandler
 * @see brave.Tracing.Builder#addSpanHandler(SpanHandler)
 * @since 2.13
 */
public class ZipkinSpanHandler extends SpanHandler implements Closeable {
  /** @since 2.13 */
  // SpanHandler not ZipkinSpanHandler as it can coerce to NOOP
  public static SpanHandler create(Reporter spanReporter) {
    return newBuilder(spanReporter).build();
  }
  /** @since 2.13 */
  public static Builder newBuilder(Reporter spanReporter) {
    if (spanReporter == null) throw new NullPointerException("spanReporter == null");
    return new ConvertingZipkinSpanHandler.Builder(spanReporter);
  }
  /**
   * Allows this instance to be reconfigured, for example {@link Builder#alwaysReportSpans(boolean)}.
   *
   * Note: Call {@link #close()} if you no longer need this instance, as otherwise it
   * can leak resources.
   *
   * @since 2.15
   */
  public Builder toBuilder() {
    // For testing, this is easier than making the type abstract: It is package sealed anyway!
    throw new UnsupportedOperationException();
  }
  /**
   * Implementations that throw exceptions on close have bugs. This may result in log warnings,
   * though.
   *
   * @since 2.15
   */
  @Override public void close() {
  }
  public static abstract class Builder {
    Tag errorTag = Tags.ERROR;
    boolean alwaysReportSpans;
    Builder() { // sealed
    }
    /**
     * Sets the "error" tag when absent and {@link MutableSpan#error()} is present.
     *
     * Note: Zipkin format uses the {@linkplain Tags#ERROR "error" tag}, but
     * alternative formats may have a different tag name or a field entirely. Hence, we only create
     * the "error" tag here, and only if not previously set.
     *
     * @since 2.13
     */
    public Builder errorTag(Tag errorTag) {
      if (errorTag == null) throw new NullPointerException("errorTag == null");
      this.errorTag = errorTag;
      return this;
    }
    /**
     * When true, all spans {@link TraceContext#sampledLocal() sampled locally} are reported to the
     * span reporter, even if they aren't sampled remotely. Defaults to {@code false}.
     *
     * The primary use case is to implement a sampling
     * overlay, such as boosting the sample rate for a subset of the network depending on the
     * value of a baggage field. This means that data will report when either the trace is normally
     * sampled, or secondarily sampled via a custom header.
     *
     * 
This is simpler than a custom {@link SpanHandler}, because you don't have to
     * duplicate transport mechanics already implemented in the {@link Reporter span reporter}.
     * However, this assumes your backend can properly process the partial traces implied when using
     * conditional sampling. For example, if your sampling condition is not consistent on a call
     * tree, the resulting data could appear broken.
     *
     * @see TraceContext#sampledLocal()
     * @since 2.13
     */
    public Builder alwaysReportSpans(boolean alwaysReportSpans) {
      this.alwaysReportSpans = alwaysReportSpans;
      return this;
    }
    public abstract SpanHandler build();
  }
  final Reporter spanReporter;
  final Tag errorTag; // for toBuilder()
  final boolean alwaysReportSpans;
  ZipkinSpanHandler(Reporter spanReporter, Tag errorTag,
      boolean alwaysReportSpans) {
    this.spanReporter = spanReporter;
    this.errorTag = errorTag;
    this.alwaysReportSpans = alwaysReportSpans;
  }
  @Override public boolean end(TraceContext context, MutableSpan span, Cause cause) {
    if (!alwaysReportSpans && !Boolean.TRUE.equals(context.sampled())) return true;
    spanReporter.report(span);
    return true;
  }
  @Override public String toString() {
    return spanReporter.toString();
  }
  /**
   * Overridden to avoid duplicates when added via {@link brave.Tracing.Builder#addSpanHandler(SpanHandler)}
   */
  @Override public final boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof ZipkinSpanHandler)) return false;
    return spanReporter.equals(((ZipkinSpanHandler) o).spanReporter);
  }
  /**
   * Overridden to avoid duplicates when added via {@link brave.Tracing.Builder#addSpanHandler(SpanHandler)}
   */
  @Override public final int hashCode() {
    return spanReporter.hashCode();
  }
}