
com.wavefront.opentracing.reporting.WavefrontSpanReporter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wavefront-opentracing-sdk-java Show documentation
Show all versions of wavefront-opentracing-sdk-java Show documentation
Implements OpenTracing API for collecting and sending tracing data to Wavefront from Java applications.
The newest version!
package com.wavefront.opentracing.reporting;
import com.wavefront.internal.reporter.WavefrontInternalReporter;
import com.wavefront.internal_reporter_java.io.dropwizard.metrics5.DeltaCounter;
import com.wavefront.internal_reporter_java.io.dropwizard.metrics5.MetricName;
import com.wavefront.opentracing.Reference;
import com.wavefront.opentracing.WavefrontSpan;
import com.wavefront.opentracing.WavefrontSpanContext;
import com.wavefront.sdk.common.WavefrontSender;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import static com.wavefront.opentracing.common.Constants.DEFAULT_SOURCE;
/**
* The reporter which reports tracing spans to Wavefront via WavefrontSender.
*
* @author Vikram Raman ([email protected])
*/
public class WavefrontSpanReporter implements Reporter, Runnable {
private static final Logger logger = Logger.getLogger(WavefrontSpanReporter.class.getName());
private final WavefrontSender wavefrontSender;
private final String source;
private final LinkedBlockingQueue spanBuffer;
private final Thread sendingThread;
private final Random random;
private final float logPercent;
private final boolean reportSpanLogs;
/**
* Users create a WavefrontSpanReporter and provide it to the tracer, which upon initialization
* sets this internal metrics reporter. Though unlikely, marked as volatile for thread safety.
*/
private volatile WavefrontInternalReporter metricsReporter;
private DeltaCounter spansDropped;
private DeltaCounter spansReceived;
private DeltaCounter reportErrors;
private volatile boolean stop = false;
public static final class Builder {
private String source;
private int maxQueueSize = 50000;
private float logPercent = 0.1f;
private boolean reportSpanLogs = true;
public Builder() {
this.source = getDefaultSource();
}
private static String getDefaultSource() {
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException ex) {
return DEFAULT_SOURCE;
}
}
/**
* Set the source for this reporter.
*
* @param source the source for all spans
* @return {@code this}
*/
public Builder withSource(String source) {
this.source = source;
return this;
}
/**
* Set max queue size of in-memory buffer. Incoming spans are dropped if buffer is full.
*
* @param maxQueueSize Max queue size of in-memory buffer
* @return {@code this}
* @throws IllegalArgumentException if the queue size is not greater than 0
*/
public Builder withMaxQueueSize(int maxQueueSize) {
if (maxQueueSize <= 0) {
throw new IllegalArgumentException("invalid max queue size");
}
this.maxQueueSize = maxQueueSize;
return this;
}
/**
* Set the percent of log messages to be logged. Defaults to 10%.
*
* @param percent a value between 0.0 and 1.0
* @return {@code this}
* @throws IllegalArgumentException if the percent is not between 0.0 and 1.0
*/
public Builder withLoggingPercent(float percent) {
if (percent < 0.0 || percent > 1.0) {
throw new IllegalArgumentException("invalid logging percent");
}
this.logPercent = percent;
return this;
}
/**
* Disable the reporting of span logs.
*
* @return {@code this}
*/
public Builder disableSpanLogReporting() {
this.reportSpanLogs = false;
return this;
}
/**
* Builds a {@link WavefrontSpanReporter} for sending opentracing spans to a
* WavefrontSender that can send those spans either be a via proxy or direct ingestion.
*
* @return {@link WavefrontSpanReporter}
*/
public WavefrontSpanReporter build(WavefrontSender wavefrontSender) {
return new WavefrontSpanReporter(wavefrontSender, this.source, this.maxQueueSize,
this.logPercent, this.reportSpanLogs);
}
}
private WavefrontSpanReporter(WavefrontSender wavefrontSender, String source, int maxQueueSize,
float logPercent, boolean reportSpanLogs) {
this.wavefrontSender = wavefrontSender;
this.source = source;
this.spanBuffer = new LinkedBlockingQueue<>(maxQueueSize);
this.random = new Random();
this.logPercent = logPercent;
this.reportSpanLogs = reportSpanLogs;
sendingThread = new Thread(this, "wavefrontSpanReporter");
sendingThread.setDaemon(true);
sendingThread.start();
}
@Override
public void run() {
while (!stop) {
try {
WavefrontSpan span = spanBuffer.take();
send(span);
} catch (InterruptedException ex) {
if (logger.isLoggable(Level.INFO)) {
logger.info("reporting thread interrupted");
}
} catch (Throwable ex) {
logger.log(Level.WARNING, "Error processing buffer", ex);
}
}
}
@Override
public void report(WavefrontSpan span) {
if (metricsReporter != null) {
spansReceived.inc();
}
if (!spanBuffer.offer(span)) {
if (metricsReporter != null) {
spansDropped.inc();
}
if (loggingAllowed()) {
logger.warning("Buffer full, dropping span: " + span);
if (metricsReporter != null) {
logger.warning("Total spans dropped: " + spansDropped.getCount());
}
}
}
}
private void send(WavefrontSpan span) {
try {
WavefrontSpanContext ctx = span.context();
List parentRefs = span.getParents();
List followsRefs = span.getFollows();
List parents = parentRefs == null ? null : parentRefs.stream().
map(Reference::getSpanContext).
map(WavefrontSpanContext::getSpanId).
collect(Collectors.toList());
List follows = followsRefs == null ? null : followsRefs.stream().
map(Reference::getSpanContext).
map(WavefrontSpanContext::getSpanId).
collect(Collectors.toList());
wavefrontSender.sendSpan(span.getOperationName(), span.getStartTimeMicros() / 1000,
span.getDurationMicroseconds() / 1000, source, ctx.getTraceId(), ctx.getSpanId(),
parents, follows, span.getTagsAsList(), reportSpanLogs ? span.getSpanLogs() : null);
} catch (IOException e) {
if (loggingAllowed()) {
logger.log(Level.WARNING, "error reporting span: " + span, e);
}
if (metricsReporter != null) {
reportErrors.inc();
spansDropped.inc();
}
}
}
private boolean loggingAllowed() {
return random.nextFloat() <= logPercent;
}
public String getSource() {
return source;
}
public WavefrontSender getWavefrontSender() {
return wavefrontSender;
}
@Override
public int getFailureCount() {
return wavefrontSender.getFailureCount();
}
public void setMetricsReporter(WavefrontInternalReporter metricsReporter) {
this.metricsReporter = metricsReporter;
// init internal metrics
metricsReporter.newGauge(new MetricName("reporter.queue.size", Collections.emptyMap()),
() -> (() -> (double) spanBuffer.size())
);
metricsReporter.newGauge(new MetricName("reporter.queue.remaining_capacity",
Collections.emptyMap()), () -> (() -> (double) spanBuffer.remainingCapacity()));
spansReceived = metricsReporter.newDeltaCounter(new MetricName("reporter.spans.received",
Collections.emptyMap()));
spansDropped = metricsReporter.newDeltaCounter(new MetricName("reporter.spans.dropped",
Collections.emptyMap()));
reportErrors = metricsReporter.newDeltaCounter(new MetricName("reporter.errors",
Collections.emptyMap()));
}
@Override
public void close() throws IOException {
stop = true;
try {
// wait for 5 secs max
sendingThread.join(5000);
} catch (InterruptedException ex) {
// no-op
}
// flush buffer & close client
wavefrontSender.close();
}
@Override
public void flush() {
try {
wavefrontSender.flush();
} catch (IOException e) {
logger.warning("WavefrontSender flush() failed.");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy