All Downloads are FREE. Search and download functionalities are using the official Maven repository.

zipkin2.reporter.AsyncReporter Maven / Gradle / Ivy

/*
 * Copyright 2016-2024 The OpenZipkin Authors
 *
 * Licensed 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 zipkin2.reporter;

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 Sender} 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, Flushable { /** * 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 Sender#sendSpans(List) sent} to * Zipkin collectors. */ public static AsyncReporter create(Sender sender) { return new Builder(sender).build(); } /** Like {@link #create(Sender)}, except you can configure settings such as the timeout. */ public static Builder builder(Sender 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(Sender 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 * Sender#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 * Sender#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. Default 1% of heap */ 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() { switch (encoding) { case JSON: return build(SpanBytesEncoder.JSON_V2); case PROTO3: return build(SpanBytesEncoder.PROTO3); case THRIFT: return build(SpanBytesEncoder.THRIFT); default: throw new UnsupportedOperationException(encoding.name()); } } /** 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 BytesEncoderAdapterimplements 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(); } } }