blob: d5d16d5ec5f9a7a79c3a15a150112794c475de96 [file] [log] [blame]
/*
* Copyright 2018, OpenCensus 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 io.opencensus.exporter.trace.stackdriver;
import static com.google.common.truth.Truth.assertThat;
import static io.opencensus.exporter.trace.stackdriver.StackdriverTraceConfiguration.DEFAULT_DEADLINE;
import static io.opencensus.exporter.trace.stackdriver.StackdriverV2ExporterHandler.createResourceLabelKey;
import static io.opencensus.exporter.trace.stackdriver.StackdriverV2ExporterHandler.toStringAttributeValueProto;
import com.google.auth.Credentials;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.cloudtrace.v2.AttributeValue;
import com.google.devtools.cloudtrace.v2.Span;
import com.google.devtools.cloudtrace.v2.Span.TimeEvent;
import com.google.devtools.cloudtrace.v2.Span.TimeEvent.MessageEvent;
import com.google.devtools.cloudtrace.v2.StackTrace;
import com.google.devtools.cloudtrace.v2.TruncatableString;
import com.google.protobuf.Int32Value;
import io.opencensus.common.Timestamp;
import io.opencensus.contrib.resource.util.CloudResource;
import io.opencensus.contrib.resource.util.HostResource;
import io.opencensus.resource.Resource;
import io.opencensus.trace.Annotation;
import io.opencensus.trace.Link;
import io.opencensus.trace.Span.Kind;
import io.opencensus.trace.SpanContext;
import io.opencensus.trace.SpanId;
import io.opencensus.trace.Status;
import io.opencensus.trace.TraceId;
import io.opencensus.trace.TraceOptions;
import io.opencensus.trace.Tracestate;
import io.opencensus.trace.export.SpanData;
import io.opencensus.trace.export.SpanData.TimedEvent;
import io.opencensus.trace.export.SpanData.TimedEvents;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for proto conversions in {@link StackdriverV2ExporterHandler}. */
@RunWith(JUnit4.class)
public final class StackdriverV2ExporterHandlerProtoTest {
private static final Credentials FAKE_CREDENTIALS =
GoogleCredentials.newBuilder().setAccessToken(new AccessToken("fake", new Date(100))).build();
// OpenCensus constants
private static final Timestamp startTimestamp = Timestamp.create(123, 456);
private static final Timestamp eventTimestamp1 = Timestamp.create(123, 457);
private static final Timestamp eventTimestamp2 = Timestamp.create(123, 458);
private static final Timestamp eventTimestamp3 = Timestamp.create(123, 459);
private static final Timestamp endTimestamp = Timestamp.create(123, 460);
private static final String PROJECT_ID = "PROJECT_ID";
private static final String TRACE_ID = "4bf92f3577b34da6a3ce929d0e0e4736";
private static final String SPAN_ID = "24aa0b2d371f48c9";
private static final String PARENT_SPAN_ID = "71da8d631536f5f1";
private static final String SPAN_NAME = "MySpanName";
private static final String SD_SPAN_NAME =
String.format("projects/%s/traces/%s/spans/%s", PROJECT_ID, TRACE_ID, SPAN_ID);
private static final String ANNOTATION_TEXT = "MyAnnotationText";
private static final String ATTRIBUTE_KEY_1 = "MyAttributeKey1";
private static final String ATTRIBUTE_KEY_2 = "MyAttributeKey2";
private static final int DROPPED_ATTRIBUTES_COUNT = 1;
private static final int DROPPED_ANNOTATIONS_COUNT = 2;
private static final int DROPPED_NETWORKEVENTS_COUNT = 3;
private static final int DROPPED_LINKS_COUNT = 4;
private static final int CHILD_SPAN_COUNT = 13;
private static final Annotation annotation = Annotation.fromDescription(ANNOTATION_TEXT);
private static final io.opencensus.trace.MessageEvent recvMessageEvent =
io.opencensus.trace.MessageEvent.builder(io.opencensus.trace.MessageEvent.Type.RECEIVED, 1)
.build();
private static final io.opencensus.trace.MessageEvent sentMessageEvent =
io.opencensus.trace.MessageEvent.builder(io.opencensus.trace.MessageEvent.Type.SENT, 1)
.build();
private static final Status status = Status.DEADLINE_EXCEEDED.withDescription("TooSlow");
private static final SpanId parentSpanId = SpanId.fromLowerBase16(PARENT_SPAN_ID);
private static final SpanId spanId = SpanId.fromLowerBase16(SPAN_ID);
private static final TraceId traceId = TraceId.fromLowerBase16(TRACE_ID);
private static final TraceOptions traceOptions = TraceOptions.DEFAULT;
private static final SpanContext spanContext =
SpanContext.create(traceId, spanId, traceOptions, Tracestate.builder().build());
private static final List<TimedEvent<Annotation>> annotationsList =
ImmutableList.of(
SpanData.TimedEvent.create(eventTimestamp1, annotation),
SpanData.TimedEvent.create(eventTimestamp3, annotation));
private static final List<TimedEvent<io.opencensus.trace.MessageEvent>> networkEventsList =
ImmutableList.of(
SpanData.TimedEvent.create(eventTimestamp1, recvMessageEvent),
SpanData.TimedEvent.create(eventTimestamp2, sentMessageEvent));
private static final List<Link> linksList =
ImmutableList.of(Link.fromSpanContext(spanContext, Link.Type.CHILD_LINKED_SPAN));
private static final SpanData.Attributes attributes =
SpanData.Attributes.create(
ImmutableMap.of(
ATTRIBUTE_KEY_1,
io.opencensus.trace.AttributeValue.longAttributeValue(10L),
ATTRIBUTE_KEY_2,
io.opencensus.trace.AttributeValue.booleanAttributeValue(true)),
DROPPED_ATTRIBUTES_COUNT);
private static final TimedEvents<Annotation> annotations =
TimedEvents.create(annotationsList, DROPPED_ANNOTATIONS_COUNT);
private static final TimedEvents<io.opencensus.trace.MessageEvent> messageEvents =
TimedEvents.create(networkEventsList, DROPPED_NETWORKEVENTS_COUNT);
private static final SpanData.Links links = SpanData.Links.create(linksList, DROPPED_LINKS_COUNT);
private static final Map<String, AttributeValue> EMPTY_RESOURCE_LABELS = Collections.emptyMap();
private static final Resource CUSTOM_RESOURCE =
Resource.create(
"MyOwnResouce",
ImmutableMap.of(
CloudResource.ACCOUNT_ID_KEY,
"my-project",
CloudResource.ZONE_KEY,
"us-east1",
HostResource.ID_KEY,
"my-instance"));
private static final ImmutableMap<String, AttributeValue> EXPECTED_RESOURCE_ATTRIBUTES =
ImmutableMap.of(
createResourceLabelKey(CloudResource.ACCOUNT_ID_KEY),
toStringAttributeValueProto("my-project"),
createResourceLabelKey(CloudResource.ZONE_KEY),
toStringAttributeValueProto("us-east1"),
createResourceLabelKey(HostResource.ID_KEY),
toStringAttributeValueProto("my-instance"));
private StackdriverV2ExporterHandler handler;
@Before
public void setUp() throws IOException {
handler =
StackdriverV2ExporterHandler.createWithCredentials(
PROJECT_ID,
FAKE_CREDENTIALS,
Collections.<String, io.opencensus.trace.AttributeValue>emptyMap(),
DEFAULT_DEADLINE);
}
@Test
public void generateSpan() {
SpanData spanData =
SpanData.create(
spanContext,
parentSpanId,
/* hasRemoteParent= */ true,
SPAN_NAME,
null,
startTimestamp,
attributes,
annotations,
messageEvents,
links,
CHILD_SPAN_COUNT,
status,
endTimestamp);
TimeEvent annotationTimeEvent1 =
TimeEvent.newBuilder()
.setAnnotation(
TimeEvent.Annotation.newBuilder()
.setDescription(
TruncatableString.newBuilder().setValue(ANNOTATION_TEXT).build())
.setAttributes(Span.Attributes.newBuilder().build())
.build())
.setTime(
com.google.protobuf.Timestamp.newBuilder()
.setSeconds(eventTimestamp1.getSeconds())
.setNanos(eventTimestamp1.getNanos())
.build())
.build();
TimeEvent annotationTimeEvent2 =
TimeEvent.newBuilder()
.setAnnotation(
TimeEvent.Annotation.newBuilder()
.setDescription(
TruncatableString.newBuilder().setValue(ANNOTATION_TEXT).build())
.setAttributes(Span.Attributes.newBuilder().build())
.build())
.setTime(
com.google.protobuf.Timestamp.newBuilder()
.setSeconds(eventTimestamp3.getSeconds())
.setNanos(eventTimestamp3.getNanos())
.build())
.build();
TimeEvent sentTimeEvent =
TimeEvent.newBuilder()
.setMessageEvent(
TimeEvent.MessageEvent.newBuilder()
.setType(MessageEvent.Type.SENT)
.setId(sentMessageEvent.getMessageId()))
.setTime(
com.google.protobuf.Timestamp.newBuilder()
.setSeconds(eventTimestamp2.getSeconds())
.setNanos(eventTimestamp2.getNanos())
.build())
.build();
TimeEvent recvTimeEvent =
TimeEvent.newBuilder()
.setMessageEvent(
TimeEvent.MessageEvent.newBuilder()
.setType(MessageEvent.Type.RECEIVED)
.setId(recvMessageEvent.getMessageId()))
.setTime(
com.google.protobuf.Timestamp.newBuilder()
.setSeconds(eventTimestamp1.getSeconds())
.setNanos(eventTimestamp1.getNanos())
.build())
.build();
Span.Links spanLinks =
Span.Links.newBuilder()
.setDroppedLinksCount(DROPPED_LINKS_COUNT)
.addLink(
Span.Link.newBuilder()
.setType(Span.Link.Type.CHILD_LINKED_SPAN)
.setTraceId(TRACE_ID)
.setSpanId(SPAN_ID)
.setAttributes(Span.Attributes.newBuilder().build())
.build())
.build();
com.google.rpc.Status spanStatus =
com.google.rpc.Status.newBuilder()
.setCode(com.google.rpc.Code.DEADLINE_EXCEEDED.getNumber())
.setMessage("TooSlow")
.build();
com.google.protobuf.Timestamp startTime =
com.google.protobuf.Timestamp.newBuilder()
.setSeconds(startTimestamp.getSeconds())
.setNanos(startTimestamp.getNanos())
.build();
com.google.protobuf.Timestamp endTime =
com.google.protobuf.Timestamp.newBuilder()
.setSeconds(endTimestamp.getSeconds())
.setNanos(endTimestamp.getNanos())
.build();
Span span =
handler.generateSpan(
spanData, EMPTY_RESOURCE_LABELS, Collections.<String, AttributeValue>emptyMap());
assertThat(span.getName()).isEqualTo(SD_SPAN_NAME);
assertThat(span.getSpanId()).isEqualTo(SPAN_ID);
assertThat(span.getParentSpanId()).isEqualTo(PARENT_SPAN_ID);
assertThat(span.getDisplayName())
.isEqualTo(TruncatableString.newBuilder().setValue(SPAN_NAME).build());
assertThat(span.getStartTime()).isEqualTo(startTime);
assertThat(span.getEndTime()).isEqualTo(endTime);
assertThat(span.getAttributes().getDroppedAttributesCount())
.isEqualTo(DROPPED_ATTRIBUTES_COUNT);
// The generated attributes map contains more values (e.g. agent). We only test what we added.
assertThat(span.getAttributes().getAttributeMapMap())
.containsEntry(ATTRIBUTE_KEY_1, AttributeValue.newBuilder().setIntValue(10L).build());
assertThat(span.getAttributes().getAttributeMapMap())
.containsEntry(ATTRIBUTE_KEY_2, AttributeValue.newBuilder().setBoolValue(true).build());
// TODO(@Hailong): add stack trace test in the future.
assertThat(span.getStackTrace()).isEqualTo(StackTrace.newBuilder().build());
assertThat(span.getTimeEvents().getDroppedMessageEventsCount())
.isEqualTo(DROPPED_NETWORKEVENTS_COUNT);
assertThat(span.getTimeEvents().getDroppedAnnotationsCount())
.isEqualTo(DROPPED_ANNOTATIONS_COUNT);
assertThat(span.getTimeEvents().getTimeEventList())
.containsExactly(annotationTimeEvent1, annotationTimeEvent2, sentTimeEvent, recvTimeEvent);
assertThat(span.getLinks()).isEqualTo(spanLinks);
assertThat(span.getStatus()).isEqualTo(spanStatus);
assertThat(span.getSameProcessAsParentSpan())
.isEqualTo(com.google.protobuf.BoolValue.of(false));
assertThat(span.getChildSpanCount())
.isEqualTo(Int32Value.newBuilder().setValue(CHILD_SPAN_COUNT).build());
}
@Test
public void getResourceLabels() {
Map<String, AttributeValue> actualLabels =
StackdriverV2ExporterHandler.getResourceLabels(CUSTOM_RESOURCE);
assertThat(actualLabels).containsExactlyEntriesIn(EXPECTED_RESOURCE_ATTRIBUTES);
}
@Test
public void generateSpan_WithResourceLabels() {
SpanData spanData =
SpanData.create(
spanContext,
parentSpanId,
/* hasRemoteParent= */ true,
SPAN_NAME,
null,
startTimestamp,
attributes,
annotations,
messageEvents,
links,
CHILD_SPAN_COUNT,
status,
endTimestamp);
Span span =
handler.generateSpan(
spanData, EXPECTED_RESOURCE_ATTRIBUTES, Collections.<String, AttributeValue>emptyMap());
Map<String, AttributeValue> attributeMap = span.getAttributes().getAttributeMapMap();
assertThat(attributeMap.entrySet())
.containsAtLeastElementsIn(EXPECTED_RESOURCE_ATTRIBUTES.entrySet());
}
@Test
public void mapHttpAttributes() {
Map<String, io.opencensus.trace.AttributeValue> attributesMap =
new HashMap<String, io.opencensus.trace.AttributeValue>();
attributesMap.put("http.host", io.opencensus.trace.AttributeValue.stringAttributeValue("host"));
attributesMap.put(
"http.method", io.opencensus.trace.AttributeValue.stringAttributeValue("method"));
attributesMap.put("http.path", io.opencensus.trace.AttributeValue.stringAttributeValue("path"));
attributesMap.put(
"http.route", io.opencensus.trace.AttributeValue.stringAttributeValue("route"));
attributesMap.put(
"http.user_agent", io.opencensus.trace.AttributeValue.stringAttributeValue("user_agent"));
attributesMap.put(
"http.status_code", io.opencensus.trace.AttributeValue.longAttributeValue(200L));
SpanData.Attributes httpAttributes = SpanData.Attributes.create(attributesMap, 0);
SpanData spanData =
SpanData.create(
spanContext,
parentSpanId,
/* hasRemoteParent= */ true,
SPAN_NAME,
null,
startTimestamp,
httpAttributes,
annotations,
messageEvents,
links,
CHILD_SPAN_COUNT,
status,
endTimestamp);
Span span =
handler.generateSpan(
spanData, EMPTY_RESOURCE_LABELS, Collections.<String, AttributeValue>emptyMap());
Map<String, AttributeValue> attributes = span.getAttributes().getAttributeMapMap();
assertThat(attributes).containsEntry("/http/host", toStringAttributeValueProto("host"));
assertThat(attributes).containsEntry("/http/method", toStringAttributeValueProto("method"));
assertThat(attributes).containsEntry("/http/path", toStringAttributeValueProto("path"));
assertThat(attributes).containsEntry("/http/route", toStringAttributeValueProto("route"));
assertThat(attributes)
.containsEntry("/http/user_agent", toStringAttributeValueProto("user_agent"));
assertThat(attributes)
.containsEntry("/http/status_code", AttributeValue.newBuilder().setIntValue(200L).build());
}
@Test
public void exportChildSpanCount() {
SpanData spanData =
SpanData.create(
spanContext,
parentSpanId,
/* hasRemoteParent= */ true,
SPAN_NAME,
Kind.SERVER,
startTimestamp,
attributes,
annotations,
messageEvents,
links,
CHILD_SPAN_COUNT,
status,
endTimestamp);
assertThat(
handler
.generateSpan(
spanData, EMPTY_RESOURCE_LABELS, Collections.<String, AttributeValue>emptyMap())
.getChildSpanCount()
.getValue())
.isEqualTo(CHILD_SPAN_COUNT);
}
@Test
public void generateSpanName_ForServer() {
SpanData spanData =
SpanData.create(
spanContext,
parentSpanId,
/* hasRemoteParent= */ true,
SPAN_NAME,
Kind.SERVER,
startTimestamp,
attributes,
annotations,
messageEvents,
links,
CHILD_SPAN_COUNT,
status,
endTimestamp);
assertThat(
handler
.generateSpan(
spanData, EMPTY_RESOURCE_LABELS, Collections.<String, AttributeValue>emptyMap())
.getDisplayName()
.getValue())
.isEqualTo("Recv." + SPAN_NAME);
}
@Test
public void generateSpanName_ForServerWithRecv() {
SpanData spanData =
SpanData.create(
spanContext,
parentSpanId,
/* hasRemoteParent= */ true,
"Recv." + SPAN_NAME,
Kind.SERVER,
startTimestamp,
attributes,
annotations,
messageEvents,
links,
CHILD_SPAN_COUNT,
status,
endTimestamp);
assertThat(
handler
.generateSpan(
spanData, EMPTY_RESOURCE_LABELS, Collections.<String, AttributeValue>emptyMap())
.getDisplayName()
.getValue())
.isEqualTo("Recv." + SPAN_NAME);
}
@Test
public void generateSpanName_ForClient() {
SpanData spanData =
SpanData.create(
spanContext,
parentSpanId,
/* hasRemoteParent= */ true,
SPAN_NAME,
Kind.CLIENT,
startTimestamp,
attributes,
annotations,
messageEvents,
links,
CHILD_SPAN_COUNT,
status,
endTimestamp);
assertThat(
handler
.generateSpan(
spanData, EMPTY_RESOURCE_LABELS, Collections.<String, AttributeValue>emptyMap())
.getDisplayName()
.getValue())
.isEqualTo("Sent." + SPAN_NAME);
}
@Test
public void generateSpanName_ForClientWithSent() {
SpanData spanData =
SpanData.create(
spanContext,
parentSpanId,
/* hasRemoteParent= */ true,
"Sent." + SPAN_NAME,
Kind.CLIENT,
startTimestamp,
attributes,
annotations,
messageEvents,
links,
CHILD_SPAN_COUNT,
status,
endTimestamp);
assertThat(
handler
.generateSpan(
spanData, EMPTY_RESOURCE_LABELS, Collections.<String, AttributeValue>emptyMap())
.getDisplayName()
.getValue())
.isEqualTo("Sent." + SPAN_NAME);
}
@Test
public void addFixedAttributes() {
final ImmutableMap<String, AttributeValue> fixedAttributes =
ImmutableMap.of(
"string_attr_key",
toStringAttributeValueProto("my-project"),
"long_attr_key",
AttributeValue.newBuilder().setIntValue(1234).build(),
"bool_attr_key",
AttributeValue.newBuilder().setBoolValue(true).build());
SpanData spanData =
SpanData.create(
spanContext,
parentSpanId,
/* hasRemoteParent= */ true,
"Sent." + SPAN_NAME,
Kind.CLIENT,
startTimestamp,
attributes,
annotations,
messageEvents,
links,
CHILD_SPAN_COUNT,
status,
endTimestamp);
Span span = handler.generateSpan(spanData, EMPTY_RESOURCE_LABELS, fixedAttributes);
Map<String, AttributeValue> attributeMap = span.getAttributes().getAttributeMapMap();
assertThat(attributeMap.entrySet()).containsAtLeastElementsIn(fixedAttributes.entrySet());
}
}