/**
* Copyright 2015-2017 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 zipkin.server.brave;
import brave.Tracer;
import brave.sampler.BoundarySampler;
import brave.sampler.Sampler;
import com.github.kristofa.brave.Brave;
import com.github.kristofa.brave.InheritableServerClientAndLocalSpanState;
import com.github.kristofa.brave.ServerClientAndLocalSpanState;
import com.github.kristofa.brave.TracerAdapter;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import zipkin.Codec;
import zipkin.Endpoint;
import zipkin.Span;
import zipkin.collector.CollectorMetrics;
import zipkin.internal.Nullable;
import zipkin.reporter.AsyncReporter;
import zipkin.reporter.Callback;
import zipkin.reporter.Encoding;
import zipkin.reporter.Reporter;
import zipkin.reporter.ReporterMetrics;
import zipkin.reporter.Sender;
import zipkin.server.ConditionalOnSelfTracing;
import zipkin.storage.StorageComponent;
import static zipkin.internal.Util.propagateIfFatal;
@Configuration
@ConditionalOnSelfTracing
@Import(ApiTracerConfiguration.class)
public class BraveConfiguration {
/** This gets the lanIP without trying to lookup its name. */
// http://stackoverflow.com/questions/8765578/get-local-ip-address-without-connecting-to-the-internet
@Bean
@Scope Endpoint local(@Value("${server.port:9411}") int port) {
Endpoint.Builder builder = Endpoint.builder()
.serviceName("zipkin-server")
.port(port == -1 ? 0 : port);
try {
InetAddress address = Collections.list(NetworkInterface.getNetworkInterfaces()).stream()
.flatMap(i -> Collections.list(i.getInetAddresses()).stream())
.filter(ip -> ip.isSiteLocalAddress())
.findAny().get();
builder.parseIp(address);
} catch (Exception ignored) {
}
return builder.build();
}
// Note: there's a chicken or egg problem here. TracedStorageComponent wraps StorageComponent with
// Brave. During initialization, if we eagerly reference StorageComponent from within Brave,
// BraveTracedStorageComponentEnhancer won't be able to process it. TL;DR; if you take out Lazy
// here, self-tracing will not affect the storage component, which reduces its effectiveness.
@Bean Reporter<Span> reporter(@Lazy StorageComponent storage,
@Value("${zipkin.self-tracing.flush-interval:1}") int flushInterval,
CollectorMetrics metrics) {
return AsyncReporter.builder(new LocalSender(storage))
.messageTimeout(flushInterval, TimeUnit.SECONDS)
.metrics(new ReporterMetricsAdapter(metrics.forTransport("local"))).build();
}
@Bean ServerClientAndLocalSpanState braveState(@Qualifier("local") Endpoint local) {
com.twitter.zipkin.gen.Endpoint braveEndpoint = com.twitter.zipkin.gen.Endpoint.builder()
.ipv4(local.ipv4)
.ipv6(local.ipv6)
.port(local.port)
.serviceName(local.serviceName)
.build();
return new InheritableServerClientAndLocalSpanState(braveEndpoint);
}
@Bean Tracer braveTracer(Reporter<Span> reporter, @Qualifier("local") Endpoint local,
@Value("${zipkin.self-tracing.sample-rate:1.0}") float rate) {
return Tracer.newBuilder()
.localEndpoint(local)
.sampler(rate < 0.01 ? BoundarySampler.create(rate) : Sampler.create(rate))
.reporter(reporter)
.build();
}
@Bean Brave brave(Tracer braveTracer, ServerClientAndLocalSpanState braveState) {
return TracerAdapter.newBrave(braveTracer, braveState);
}
/**
* Defined locally as StorageComponent is a lazy proxy, and we need to avoid eagerly calling it.
*/
static final class LocalSender implements Sender {
private final StorageComponent delegate;
LocalSender(StorageComponent delegate) {
this.delegate = delegate;
}
@Override public Encoding encoding() {
return Encoding.THRIFT;
}
@Override public int messageMaxBytes() {
return 5 * 1024 * 1024; // arbitrary
}
@Override public int messageSizeInBytes(List<byte[]> list) {
return Encoding.THRIFT.listSizeInBytes(list);
}
@Override public void sendSpans(List<byte[]> encodedSpans, Callback callback) {
try {
List<Span> spans = new ArrayList<>(encodedSpans.size());
for (byte[] encodedSpan : encodedSpans) {
spans.add(Codec.THRIFT.readSpan(encodedSpan));
}
delegate.asyncSpanConsumer().accept(spans, new CallbackAdapter(callback));
} catch (Throwable t) {
propagateIfFatal(t);
callback.onError(t);
}
}
@Override public CheckResult check() {
return CheckResult.OK;
}
@Override public void close() throws IOException {
}
}
static final class CallbackAdapter implements zipkin.storage.Callback<Void> {
final Callback delegate;
CallbackAdapter(Callback delegate) {
this.delegate = delegate;
}
@Override public void onSuccess(@Nullable Void aVoid) {
delegate.onComplete();
}
@Override public void onError(Throwable throwable) {
delegate.onError(throwable);
}
}
static final class ReporterMetricsAdapter implements ReporterMetrics {
final CollectorMetrics delegate;
ReporterMetricsAdapter(CollectorMetrics delegate) {
this.delegate = delegate;
}
@Override public void incrementMessages() {
delegate.incrementMessages();
}
@Override public void incrementMessagesDropped(Throwable throwable) {
delegate.incrementMessagesDropped();
}
@Override public void incrementSpans(int i) {
delegate.incrementSpans(i);
}
@Override public void incrementSpanBytes(int i) {
delegate.incrementBytes(i);
}
@Override public void incrementMessageBytes(int i) {
}
@Override public void incrementSpansDropped(int i) {
delegate.incrementMessagesDropped();
}
@Override public void updateQueuedSpans(int i) {
}
@Override public void updateQueuedBytes(int i) {
}
}
}