zipkin2.reporter.AsyncReporter Maven / Gradle / Ivy
/*
* Copyright The OpenZipkin Authors
* SPDX-License-Identifier: Apache-2.0
*/
package zipkin2.reporter;
import java.io.Closeable;
import java.io.Flushable;
import java.util.List;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
* As spans are reported, they are encoded and added to a pending queue. The task of sending spans
* happens on a separate thread which calls {@link #flush()}. By doing so, callers are protected
* from latency or exceptions possible when exporting spans out of process.
*
* Spans are bundled into messages based on size in bytes or a timeout, whichever happens first.
*
*
The thread that sends flushes spans to the {@linkplain BytesMessageSender} does so in a synchronous loop.
* This means that even asynchronous transports will wait for an ack before sending a next message.
* We do this so that a surge of spans doesn't overrun memory or bandwidth via hundreds or
* thousands of in-flight messages. The downside of this is that reporting is limited in speed to
* what a single thread can clear. When a thread cannot clear the backlog, new spans are dropped.
*
* @param type of the span, usually {@link zipkin2.Span}
*/
// This is effectively, but not explicitly final as it was not final in version 2.x.
public class AsyncReporter extends Component implements Reporter, Closeable, Flushable {
/** @deprecated Since 3.2, use {@link #create(BytesMessageSender)} */
@Deprecated public static AsyncReporter create(Sender sender) {
return create((BytesMessageSender) sender);
}
/**
* Builds a json reporter for Zipkin V2. If http,
* the endpoint of the sender is usually "http://zipkinhost:9411/api/v2/spans".
*
* After a certain threshold, spans are drained and {@link BytesMessageSender#send(List) sent}
* to Zipkin collectors.
*
* @since 3.2
*/
public static AsyncReporter create(BytesMessageSender sender) {
return new Builder(sender).build();
}
/** @deprecated Since 3.2, use {@link #builder(BytesMessageSender)} */
@Deprecated public static Builder builder(Sender sender) {
return builder((BytesMessageSender) sender);
}
/** Like {@link #create(BytesMessageSender)}, except you can configure settings such as the timeout. */
public static Builder builder(BytesMessageSender sender) {
return new Builder(sender);
}
final zipkin2.reporter.internal.AsyncReporter delegate;
AsyncReporter(zipkin2.reporter.internal.AsyncReporter delegate) {
this.delegate = delegate;
}
@Override public void report(S span) {
delegate.report(span);
}
/**
* Calling this will flush any pending spans to the transport on the current thread.
*
* Note: If you set {@link Builder#messageTimeout(long, TimeUnit) message timeout} to zero, you
* must call this externally as otherwise spans will never be sent.
*
* @throws IllegalStateException if closed
*/
@Override public void flush() {
delegate.flush();
}
/** Shuts down the sender thread, and increments drop metrics if there were any unsent spans. */
@Override public void close() {
delegate.close();
}
@Override public String toString() {
return delegate.toString();
}
public static final class Builder {
final zipkin2.reporter.internal.AsyncReporter.Builder delegate;
final Encoding encoding;
Builder(BytesMessageSender sender) {
this.delegate = zipkin2.reporter.internal.AsyncReporter.newBuilder(sender);
this.encoding = sender.encoding();
}
/**
* Launches the flush thread when {@link #messageTimeout} is greater than zero.
*/
public Builder threadFactory(ThreadFactory threadFactory) {
this.delegate.threadFactory(threadFactory);
return this;
}
/**
* Aggregates and reports reporter metrics to a monitoring system. Defaults to no-op.
*/
public Builder metrics(ReporterMetrics metrics) {
this.delegate.metrics(metrics);
return this;
}
/**
* Maximum bytes sendable per message including overhead. Defaults to, and is limited by {@link
* BytesMessageSender#messageMaxBytes()}.
*/
public Builder messageMaxBytes(int messageMaxBytes) {
this.delegate.messageMaxBytes(messageMaxBytes);
return this;
}
/**
* Default 1 second. 0 implies spans are {@link #flush() flushed} externally.
*
*
Instead of sending one message at a time, spans are bundled into messages, up to {@link
* BytesMessageSender#messageMaxBytes()}. This timeout ensures that spans are not stuck in an
* incomplete message.
*
*
Note: this timeout starts when the first unsent span is reported.
*/
public Builder messageTimeout(long timeout, TimeUnit unit) {
this.delegate.messageTimeout(timeout, unit);
return this;
}
/** How long to block for in-flight spans to send out-of-process on close. Default 1 second */
public Builder closeTimeout(long timeout, TimeUnit unit) {
this.delegate.closeTimeout(timeout, unit);
return this;
}
/** Maximum backlog of spans reported vs sent. Default 10000 */
public Builder queuedMaxSpans(int queuedMaxSpans) {
this.delegate.queuedMaxSpans(queuedMaxSpans);
return this;
}
/**
* Maximum backlog of span bytes reported vs sent. Disabled by default
*
* @deprecated This will be removed in version 4.0. Use {@link #queuedMaxSpans(int)} instead.
*/
@Deprecated
public Builder queuedMaxBytes(int queuedMaxBytes) {
this.delegate.queuedMaxBytes(queuedMaxBytes);
return this;
}
/** Builds an async reporter that encodes zipkin spans as they are reported. */
public AsyncReporter build() {
return build(SpanBytesEncoder.forEncoding(encoding));
}
/** Builds an async reporter that encodes arbitrary spans as they are reported. */
public AsyncReporter build(BytesEncoder encoder) {
if (encoder == null) throw new NullPointerException("encoder == null");
return new AsyncReporter(delegate.build(new BytesEncoderAdapter(encoder)));
}
}
static final class BytesEncoderAdapter implements BytesEncoder {
final BytesEncoder delegate;
BytesEncoderAdapter(BytesEncoder delegate) {
this.delegate = delegate;
}
@Override public Encoding encoding() {
return delegate.encoding();
}
@Override public int sizeInBytes(S input) {
return delegate.sizeInBytes(input);
}
@Override public byte[] encode(S input) {
return delegate.encode(input);
}
@Override public String toString() {
return delegate.toString();
}
}
}