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

zipkin.collector.Collector Maven / Gradle / Ivy

There is a newer version: 2.9.4
Show newest version
/**
 * Copyright 2015-2016 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.collector;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;
import zipkin.storage.Callback;
import zipkin.Codec;
import zipkin.Span;
import zipkin.storage.StorageComponent;

import static java.lang.String.format;
import static java.util.logging.Level.WARNING;
import static zipkin.internal.Util.checkNotNull;

/**
 * This component takes action on spans received from a transport. This includes deserializing,
 * sampling and scheduling for storage.
 *
 * 

Callbacks passed do not propagate to the storage layer. They only return success or failures * before storage is attempted. This ensures that calling threads are disconnected from storage * threads. */ public final class Collector { /** Needed to scope this to the correct logging category */ public static Builder builder(Class loggingClass) { return new Builder(Logger.getLogger(checkNotNull(loggingClass, "loggingClass").getName())); } public static final class Builder { final Logger logger; StorageComponent storage = null; CollectorSampler sampler = CollectorSampler.ALWAYS_SAMPLE; CollectorMetrics metrics = CollectorMetrics.NOOP_METRICS; Builder(Logger logger) { this.logger = logger; } /** @see {@link CollectorComponent.Builder#storage(StorageComponent)} */ public Builder storage(StorageComponent storage) { this.storage = checkNotNull(storage, "storage"); return this; } /** @see {@link CollectorComponent.Builder#metrics(CollectorMetrics)} */ public Builder metrics(CollectorMetrics metrics) { this.metrics = checkNotNull(metrics, "metrics"); return this; } /** @see {@link CollectorComponent.Builder#sampler(CollectorSampler)} */ public Builder sampler(CollectorSampler sampler) { this.sampler = checkNotNull(sampler, "sampler"); return this; } public Collector build() { return new Collector(this); } } final Logger logger; final StorageComponent storage; final CollectorSampler sampler; final CollectorMetrics metrics; Collector(Builder builder) { this.logger = checkNotNull(builder.logger, "logger"); this.storage = checkNotNull(builder.storage, "storage"); this.sampler = builder.sampler == null ? CollectorSampler.ALWAYS_SAMPLE : builder.sampler; this.metrics = builder.metrics == null ? CollectorMetrics.NOOP_METRICS : builder.metrics; } public void acceptSpans(byte[] serializedSpans, Codec codec, Callback callback) { metrics.incrementBytes(serializedSpans.length); List spans; try { spans = codec.readSpans(serializedSpans); } catch (RuntimeException e) { callback.onError(errorReading(e)); return; } accept(spans, callback); } public void acceptSpans(List serializedSpans, Codec codec, Callback callback) { List spans = new ArrayList(serializedSpans.size()); try { int bytesRead = 0; for (byte[] serializedSpan : serializedSpans) { bytesRead += serializedSpan.length; spans.add(codec.readSpan(serializedSpan)); } metrics.incrementBytes(bytesRead); } catch (RuntimeException e) { callback.onError(errorReading(e)); return; } accept(spans, callback); } public void accept(List spans, Callback callback) { if (spans.isEmpty()) { callback.onSuccess(null); return; } metrics.incrementSpans(spans.size()); List sampled = sample(spans); if (sampled.isEmpty()) { callback.onSuccess(null); return; } try { storage.asyncSpanConsumer().accept(sampled, acceptSpansCallback(sampled)); callback.onSuccess(null); } catch (RuntimeException e) { callback.onError(errorStoringSpans(sampled, e)); return; } } List sample(List input) { List sampled = new ArrayList(input.size()); for (Span s : input) { if (sampler.isSampled(s)) sampled.add(s); } int dropped = input.size() - sampled.size(); if (dropped > 0) metrics.incrementSpansDropped(dropped); return sampled; } Callback acceptSpansCallback(final List spans) { return new Callback() { @Override public void onSuccess(Void value) { } @Override public void onError(Throwable t) { errorStoringSpans(spans, t); } @Override public String toString() { return appendSpanIds(spans, new StringBuilder("AcceptSpans(")).append(")").toString(); } }; } RuntimeException errorReading(Throwable e) { return errorReading("Cannot decode spans", e); } RuntimeException errorReading(String message, Throwable e) { metrics.incrementMessagesDropped(); return doError(message, e); } /** * When storing spans, an exception can be raised before or after the fact. This adds context of * span ids to give logs more relevance. */ RuntimeException errorStoringSpans(List spans, Throwable e) { metrics.incrementSpansDropped(spans.size()); // The exception could be related to a span being huge. Instead of filling logs, // print trace id, span id pairs StringBuilder msg = appendSpanIds(spans, new StringBuilder("Cannot store spans ")); return doError(msg.toString(), e); } RuntimeException doError(String message, Throwable e) { message = format("%s due to %s(%s)", message, e.getClass().getSimpleName(), e.getMessage() == null ? "" : e.getMessage()); logger.log(WARNING, message, e); return new RuntimeException(message, e); } static StringBuilder appendSpanIds(List spans, StringBuilder message) { message.append("["); for (Iterator iterator = spans.iterator(); iterator.hasNext(); ) { message.append(iterator.next().idString()); if (iterator.hasNext()) message.append(", "); } return message.append("]"); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy