use of zipkin2.Span.Kind.CONSUMER in project zipkin by openzipkin.
the class ITActiveMQCollector method skipsOnSpanStorageException.
/**
* Guards against errors that leak from storage, such as InvalidQueryException
*/
@Test
public void skipsOnSpanStorageException() throws Exception {
collector.close();
AtomicInteger counter = new AtomicInteger();
consumer = (input) -> new Call.Base<Void>() {
@Override
protected Void doExecute() {
throw new AssertionError();
}
@Override
protected void doEnqueue(Callback<Void> callback) {
if (counter.getAndIncrement() == 1) {
callback.onError(new RuntimeException("storage fell over"));
} else {
receivedSpans.add(spans);
callback.onSuccess(null);
}
}
@Override
public Call<Void> clone() {
throw new AssertionError();
}
};
activemq.pushMessage(collector.queue, PROTO3.encodeList(spans));
// tossed on error
activemq.pushMessage(collector.queue, PROTO3.encodeList(spans));
activemq.pushMessage(collector.queue, PROTO3.encodeList(spans));
collector = builder().storage(buildStorage(consumer)).build().start();
assertThat(receivedSpans.take()).containsExactlyElementsOf(spans);
// the only way we could read this, is if the malformed span was skipped.
assertThat(receivedSpans.take()).containsExactlyElementsOf(spans);
assertThat(activemqMetrics.messages()).isEqualTo(3);
// storage failure not message failure
assertThat(activemqMetrics.messagesDropped()).isZero();
assertThat(activemqMetrics.bytes()).isEqualTo(PROTO3.encodeList(spans).length * 3);
assertThat(activemqMetrics.spans()).isEqualTo(spans.size() * 3);
// only one dropped
assertThat(activemqMetrics.spansDropped()).isEqualTo(spans.size());
}
use of zipkin2.Span.Kind.CONSUMER in project zipkin by openzipkin.
the class KafkaCollectorWorker method run.
@Override
public void run() {
try (KafkaConsumer<byte[], byte[]> kafkaConsumer = new KafkaConsumer<>(properties)) {
kafkaConsumer.subscribe(topics, // added for integration tests only, see ITKafkaCollector
new ConsumerRebalanceListener() {
@Override
public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
// technically we should remove only the revoked partitions but for test purposes it
// does not matter
assignedPartitions.set(Collections.emptyList());
}
@Override
public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
assignedPartitions.set(Collections.unmodifiableList(new ArrayList<>(partitions)));
}
});
LOG.debug("Kafka consumer starting polling loop.");
while (running.get()) {
final ConsumerRecords<byte[], byte[]> consumerRecords = kafkaConsumer.poll(Duration.of(1000, ChronoUnit.MILLIS));
LOG.debug("Kafka polling returned batch of {} messages.", consumerRecords.count());
for (ConsumerRecord<byte[], byte[]> record : consumerRecords) {
final byte[] bytes = record.value();
metrics.incrementMessages();
metrics.incrementBytes(bytes.length);
// lenient on empty messages
if (bytes.length == 0)
continue;
if (bytes.length < 2) {
// need two bytes to check if protobuf
metrics.incrementMessagesDropped();
} else {
// If we received legacy single-span encoding, decode it into a singleton list
if (!protobuf3(bytes) && bytes[0] <= 16 && bytes[0] != 12) /* thrift, but not list */
{
Span span;
try {
span = SpanBytesDecoder.THRIFT.decodeOne(bytes);
} catch (RuntimeException e) {
metrics.incrementMessagesDropped();
continue;
}
collector.accept(Collections.singletonList(span), NOOP);
} else {
collector.acceptSpans(bytes, NOOP);
}
}
}
}
} catch (RuntimeException | Error e) {
LOG.warn("Unexpected error in polling loop spans", e);
throw e;
} finally {
LOG.debug("Kafka consumer polling loop stopped. Kafka consumer closed.");
}
}
use of zipkin2.Span.Kind.CONSUMER in project zipkin by openzipkin.
the class ITRabbitMQCollector method skipsOnSpanStorageException.
/**
* Guards against errors that leak from storage, such as InvalidQueryException
*/
@Test
void skipsOnSpanStorageException() throws Exception {
AtomicInteger counter = new AtomicInteger();
consumer = (input) -> new Call.Base<Void>() {
@Override
protected Void doExecute() {
throw new AssertionError();
}
@Override
protected void doEnqueue(Callback<Void> callback) {
if (counter.getAndIncrement() == 1) {
callback.onError(new RuntimeException("storage fell over"));
} else {
receivedSpans.add(spans);
callback.onSuccess(null);
}
}
@Override
public Call<Void> clone() {
throw new AssertionError();
}
};
final StorageComponent storage = buildStorage(consumer);
RabbitMQCollector.Builder builder = builder("storage_exception").storage(storage);
produceSpans(THRIFT.encodeList(spans), builder.queue);
// tossed on error
produceSpans(THRIFT.encodeList(spans), builder.queue);
produceSpans(THRIFT.encodeList(spans), builder.queue);
try (RabbitMQCollector collector = builder.build()) {
collector.start();
assertThat(receivedSpans.take()).containsExactlyElementsOf(spans);
// the only way we could read this, is if the malformed span was skipped.
assertThat(receivedSpans.take()).containsExactlyElementsOf(spans);
}
assertThat(rabbitmqMetrics.messages()).isEqualTo(3);
// storage failure isn't a message failure
assertThat(rabbitmqMetrics.messagesDropped()).isZero();
assertThat(rabbitmqMetrics.bytes()).isEqualTo(THRIFT.encodeList(spans).length * 3);
assertThat(rabbitmqMetrics.spans()).isEqualTo(spans.size() * 3);
// only one dropped
assertThat(rabbitmqMetrics.spansDropped()).isEqualTo(spans.size());
}
use of zipkin2.Span.Kind.CONSUMER in project zipkin by openzipkin.
the class V1SpanConverter method processAnnotations.
void processAnnotations(V1Span source) {
for (int i = 0, length = source.annotations.size(); i < length; i++) {
V1Annotation a = source.annotations.get(i);
Span.Builder currentSpan = forEndpoint(source, a.endpoint);
// core annotations require an endpoint. Don't give special treatment when that's missing
if (a.value.length() == 2 && a.endpoint != null) {
if (a.value.equals("cs")) {
currentSpan.kind(Kind.CLIENT);
cs = a;
} else if (a.value.equals("sr")) {
currentSpan.kind(Kind.SERVER);
sr = a;
} else if (a.value.equals("ss")) {
currentSpan.kind(Kind.SERVER);
ss = a;
} else if (a.value.equals("cr")) {
currentSpan.kind(Kind.CLIENT);
cr = a;
} else if (a.value.equals("ms")) {
currentSpan.kind(Kind.PRODUCER);
ms = a;
} else if (a.value.equals("mr")) {
currentSpan.kind(Kind.CONSUMER);
mr = a;
} else if (a.value.equals("ws")) {
ws = a;
} else if (a.value.equals("wr")) {
wr = a;
} else {
currentSpan.addAnnotation(a.timestamp, a.value);
}
} else {
currentSpan.addAnnotation(a.timestamp, a.value);
}
}
// When bridging between event and span model, you can end up missing a start annotation
if (cs == null && endTimestampReflectsSpanDuration(cr, source)) {
cs = V1Annotation.create(source.timestamp, "cs", cr.endpoint);
}
if (sr == null && endTimestampReflectsSpanDuration(ss, source)) {
sr = V1Annotation.create(source.timestamp, "sr", ss.endpoint);
}
if (cs != null && sr != null) {
// in a shared span, the client side owns span duration by annotations or explicit timestamp
maybeTimestampDuration(source, cs, cr);
// special-case loopback: We need to make sure on loopback there are two span2s
Span.Builder client = forEndpoint(source, cs.endpoint);
Span.Builder server;
if (hasSameServiceName(cs.endpoint, sr.endpoint)) {
client.kind(Kind.CLIENT);
// fork a new span for the server side
server = newSpanBuilder(source, sr.endpoint).kind(Kind.SERVER);
} else {
server = forEndpoint(source, sr.endpoint);
}
// the server side is smaller than that, we have to read annotations to find out
server.shared(true).timestamp(sr.timestamp);
if (ss != null)
server.duration(ss.timestamp - sr.timestamp);
// one-way has no duration
if (cr == null && source.duration == 0)
client.duration(null);
} else if (cs != null && cr != null) {
maybeTimestampDuration(source, cs, cr);
} else if (sr != null && ss != null) {
maybeTimestampDuration(source, sr, ss);
} else {
// otherwise, the span is incomplete. revert special-casing
handleIncompleteRpc(source);
}
// implied shared. When we only see the server-side, carry this signal over.
if (cs == null && sr != null && // case could be due to the client-side of that RPC.
(source.timestamp == 0 || (ss != null && source.duration == 0))) {
forEndpoint(source, sr.endpoint).shared(true);
}
// ms and mr are not supposed to be in the same span, but in case they are..
if (ms != null && mr != null) {
// special-case loopback: We need to make sure on loopback there are two span2s
Span.Builder producer = forEndpoint(source, ms.endpoint);
Span.Builder consumer;
if (hasSameServiceName(ms.endpoint, mr.endpoint)) {
producer.kind(Kind.PRODUCER);
// fork a new span for the consumer side
consumer = newSpanBuilder(source, mr.endpoint).kind(Kind.CONSUMER);
} else {
consumer = forEndpoint(source, mr.endpoint);
}
consumer.shared(true);
if (wr != null) {
consumer.timestamp(wr.timestamp).duration(mr.timestamp - wr.timestamp);
} else {
consumer.timestamp(mr.timestamp);
}
producer.timestamp(ms.timestamp).duration(ws != null ? ws.timestamp - ms.timestamp : null);
} else if (ms != null) {
maybeTimestampDuration(source, ms, ws);
} else if (mr != null) {
if (wr != null) {
maybeTimestampDuration(source, wr, mr);
} else {
maybeTimestampDuration(source, mr, null);
}
} else {
if (ws != null)
forEndpoint(source, ws.endpoint).addAnnotation(ws.timestamp, ws.value);
if (wr != null)
forEndpoint(source, wr.endpoint).addAnnotation(wr.timestamp, wr.value);
}
}
use of zipkin2.Span.Kind.CONSUMER in project zipkin by openzipkin.
the class ScribeSpanConsumerTest method entriesWithoutSpansAreSkipped.
@Test
void entriesWithoutSpansAreSkipped() throws Exception {
SpanConsumer consumer = (callback) -> {
// as we shouldn't get here.
throw new AssertionError();
};
ScribeSpanConsumer scribe = newScribeSpanConsumer("zipkin", consumer);
LogEntry entry = new LogEntry();
entry.category = "notzipkin";
entry.message = "hello world";
expectSuccess(scribe, entry);
// Storage finishes after callback so wait for it.
await().untilAsserted(() -> assertThat(scribeMetrics.messages()).isEqualTo(1));
assertThat(scribeMetrics.messagesDropped()).isZero();
assertThat(scribeMetrics.bytes()).isZero();
assertThat(scribeMetrics.spans()).isZero();
assertThat(scribeMetrics.spansDropped()).isZero();
}
Aggregations