/*
* Copyright 2015, The Sporting Exchange Limited
*
* 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 com.betfair.cougar.modules.zipkin.impl;
import com.betfair.cougar.modules.zipkin.api.ZipkinData;
import com.google.common.collect.Lists;
import com.twitter.zipkin.gen.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.nio.ByteBuffer;
import java.util.Objects;
/**
* A container used for storing Zipkin annotations relative to a specific Zipkin span, to be sent once the span has been
* fully populated.
*/
public final class ZipkinAnnotationsStore {
private static final int SHORT_SIZE_B = Short.SIZE / 8;
private static final int INT_SIZE_B = Integer.SIZE / 8;
private static final int LONG_SIZE_B = Long.SIZE / 8;
private static final int DOUBLE_SIZE_B = Double.SIZE / 8;
private static final ByteBuffer TRUE_BB = ByteBuffer.wrap(new byte[]{1});
private static final ByteBuffer FALSE_BB = ByteBuffer.wrap(new byte[]{0});
private Endpoint defaultEndpoint;
private Span underlyingSpan;
/**
* Creates a new annotations store for a specific span, to be created from the passed in ZipkinData.
*
* @param zipkinData The ZipkinData to be used for creating the underlying span.
*/
ZipkinAnnotationsStore(@Nonnull ZipkinData zipkinData) {
this.underlyingSpan = new Span(zipkinData.getTraceId(), zipkinData.getSpanName(), zipkinData.getSpanId(),
Lists.<Annotation>newArrayList(), Lists.<BinaryAnnotation>newArrayList());
if (zipkinData.getParentSpanId() != null) {
underlyingSpan.setParent_id(zipkinData.getParentSpanId());
}
}
// PUBLIC METHODS
/**
* Adds an annotation for an event that happened on a specific timestamp.
*
* @param timestamp The timestamp of the annotation, in microseconds
* @param s The annotation value to emit
* @return this object
*/
@Nonnull
public ZipkinAnnotationsStore addAnnotation(long timestamp, @Nonnull String s) {
return addAnnotation(timestamp, s, defaultEndpoint);
}
/**
* Adds a (binary) string annotation for an event.
*
* @param key The key of the annotation
* @param value The value of the annotation
* @return this object
*/
@Nonnull
public ZipkinAnnotationsStore addAnnotation(@Nonnull String key, @Nonnull String value) {
return addBinaryAnnotation(key, value, defaultEndpoint);
}
/**
* Adds a (binary) short annotation for an event.
*
* @param key The key of the annotation
* @param value The value of the annotation
* @return this object
*/
@Nonnull
public ZipkinAnnotationsStore addAnnotation(@Nonnull String key, short value) {
return addBinaryAnnotation(key, value, defaultEndpoint);
}
/**
* Adds a (binary) int annotation for an event.
*
* @param key The key of the annotation
* @param value The value of the annotation
* @return this object
*/
@Nonnull
public ZipkinAnnotationsStore addAnnotation(@Nonnull String key, int value) {
return addBinaryAnnotation(key, value, defaultEndpoint);
}
/**
* Adds a (binary) long annotation for an event.
*
* @param key The key of the annotation
* @param value The value of the annotation
* @return this object
*/
@Nonnull
public ZipkinAnnotationsStore addAnnotation(@Nonnull String key, long value) {
return addBinaryAnnotation(key, value, defaultEndpoint);
}
/**
* Adds a (binary) double annotation for an event.
*
* @param key The key of the annotation
* @param value The value of the annotation
* @return this object
*/
@Nonnull
public ZipkinAnnotationsStore addAnnotation(@Nonnull String key, double value) {
return addBinaryAnnotation(key, value, defaultEndpoint);
}
/**
* Adds a (binary) boolean annotation for an event.
*
* @param key The key of the annotation
* @param value The value of the annotation
* @return this object
*/
@Nonnull
public ZipkinAnnotationsStore addAnnotation(@Nonnull String key, boolean value) {
return addBinaryAnnotation(key, value, defaultEndpoint);
}
/**
* Adds a (binary) byte array annotation for an event.
*
* @param key The key of the annotation
* @param value The value of the annotation
* @return this object
*/
@Nonnull
public ZipkinAnnotationsStore addAnnotation(@Nonnull String key, byte[] value) {
return addBinaryAnnotation(key, value, defaultEndpoint);
}
// PACKAGE-PRIVATE METHODS
@Nonnull
ZipkinAnnotationsStore defaultEndpoint(@Nonnull Endpoint defaultEndpoint) {
this.defaultEndpoint = defaultEndpoint;
return this;
}
@Nonnull
ZipkinAnnotationsStore addAnnotation(long timestampMicro, @Nonnull String s, @Nullable Endpoint endpoint) {
Objects.requireNonNull(s);
Annotation annotation = new Annotation(timestampMicro, s);
if (endpoint != null) {
// endpoint is optional - current version of zipkin web doesn't show spans without host though
annotation.setHost(endpoint);
}
underlyingSpan.addToAnnotations(annotation);
return this;
}
@Nonnull
ZipkinAnnotationsStore addBinaryAnnotation(@Nonnull String key, @Nonnull String value, @Nonnull Endpoint endpoint) {
// Using default charset
ByteBuffer wrappedValue = ByteBuffer.wrap(value.getBytes());
return addBinaryAnnotation(key, wrappedValue, AnnotationType.STRING, endpoint);
}
@Nonnull
ZipkinAnnotationsStore addBinaryAnnotation(@Nonnull String key, short value, @Nonnull Endpoint endpoint) {
ByteBuffer wrappedValue = ByteBuffer.allocate(SHORT_SIZE_B).putShort(value);
wrappedValue.flip();
return addBinaryAnnotation(key, wrappedValue, AnnotationType.I16, endpoint);
}
@Nonnull
ZipkinAnnotationsStore addBinaryAnnotation(@Nonnull String key, int value, @Nonnull Endpoint endpoint) {
ByteBuffer wrappedValue = ByteBuffer.allocate(INT_SIZE_B).putInt(value);
wrappedValue.flip();
return addBinaryAnnotation(key, wrappedValue, AnnotationType.I32, endpoint);
}
@Nonnull
ZipkinAnnotationsStore addBinaryAnnotation(@Nonnull String key, long value, @Nonnull Endpoint endpoint) {
ByteBuffer wrappedValue = ByteBuffer.allocate(LONG_SIZE_B).putLong(value);
wrappedValue.flip();
return addBinaryAnnotation(key, wrappedValue, AnnotationType.I64, endpoint);
}
@Nonnull
ZipkinAnnotationsStore addBinaryAnnotation(@Nonnull String key, double value, @Nonnull Endpoint endpoint) {
ByteBuffer wrappedValue = ByteBuffer.allocate(DOUBLE_SIZE_B).putDouble(value);
wrappedValue.flip();
return addBinaryAnnotation(key, wrappedValue, AnnotationType.DOUBLE, endpoint);
}
@Nonnull
ZipkinAnnotationsStore addBinaryAnnotation(@Nonnull String key, boolean value, @Nonnull Endpoint endpoint) {
ByteBuffer wrappedValue = value ? TRUE_BB : FALSE_BB;
return addBinaryAnnotation(key, wrappedValue, AnnotationType.BOOL, endpoint);
}
@Nonnull
ZipkinAnnotationsStore addBinaryAnnotation(@Nonnull String key, byte[] value, @Nonnull Endpoint endpoint) {
ByteBuffer wrappedValue = ByteBuffer.wrap(value);
return addBinaryAnnotation(key, wrappedValue, AnnotationType.BYTES, endpoint);
}
@Nonnull
Span generate() {
return underlyingSpan;
}
// PRIVATE METHODS
@Nonnull
private ZipkinAnnotationsStore addBinaryAnnotation(@Nonnull String key, @Nonnull ByteBuffer byteBuffer,
@Nonnull AnnotationType annotationType, @Nullable Endpoint endpoint) {
BinaryAnnotation binaryAnnotation = new BinaryAnnotation(key, byteBuffer, annotationType);
if (endpoint != null) {
// endpoint is optional - current version of zipkin web doesn't show spans without host though
binaryAnnotation.setHost(endpoint);
}
underlyingSpan.addToBinary_annotations(binaryAnnotation);
return this;
}
}