1 /* 2 * Copyright 2018, OpenCensus Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.opencensus.exporter.trace.jaeger; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static io.opencensus.exporter.trace.jaeger.JaegerExporterConfiguration.DEFAULT_DEADLINE; 21 import static java.util.Collections.singletonList; 22 import static java.util.concurrent.TimeUnit.MILLISECONDS; 23 import static org.mockito.ArgumentMatchers.eq; 24 import static org.mockito.Mockito.mock; 25 import static org.mockito.Mockito.verify; 26 27 import com.google.common.collect.ImmutableMap; 28 import com.google.common.collect.Lists; 29 import io.jaegertracing.internal.exceptions.SenderException; 30 import io.jaegertracing.thrift.internal.senders.HttpSender; 31 import io.jaegertracing.thriftjava.Log; 32 import io.jaegertracing.thriftjava.Process; 33 import io.jaegertracing.thriftjava.Span; 34 import io.jaegertracing.thriftjava.SpanRef; 35 import io.jaegertracing.thriftjava.SpanRefType; 36 import io.jaegertracing.thriftjava.Tag; 37 import io.jaegertracing.thriftjava.TagType; 38 import io.opencensus.common.Timestamp; 39 import io.opencensus.trace.Annotation; 40 import io.opencensus.trace.AttributeValue; 41 import io.opencensus.trace.Link; 42 import io.opencensus.trace.MessageEvent; 43 import io.opencensus.trace.Span.Kind; 44 import io.opencensus.trace.SpanContext; 45 import io.opencensus.trace.SpanId; 46 import io.opencensus.trace.Status; 47 import io.opencensus.trace.TraceId; 48 import io.opencensus.trace.TraceOptions; 49 import io.opencensus.trace.Tracestate; 50 import io.opencensus.trace.export.SpanData; 51 import io.opencensus.trace.export.SpanData.TimedEvent; 52 import java.util.Collections; 53 import java.util.List; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 import org.mockito.ArgumentCaptor; 57 import org.mockito.Captor; 58 import org.mockito.junit.MockitoJUnitRunner; 59 60 @RunWith(MockitoJUnitRunner.class) 61 public class JaegerExporterHandlerTest { 62 private static final byte FF = (byte) 0xFF; 63 64 private final HttpSender mockSender = mock(HttpSender.class); 65 private final Process process = new Process("test"); 66 private final JaegerExporterHandler handler = 67 new JaegerExporterHandler(mockSender, process, DEFAULT_DEADLINE); 68 69 @Captor private ArgumentCaptor<List<Span>> captor; 70 71 @Test exportShouldConvertFromSpanDataToJaegerThriftSpan()72 public void exportShouldConvertFromSpanDataToJaegerThriftSpan() throws SenderException { 73 final long startTime = 1519629870001L; 74 final long endTime = 1519630148002L; 75 final SpanData spanData = 76 SpanData.create( 77 sampleSpanContext(), 78 SpanId.fromBytes(new byte[] {(byte) 0x7F, FF, FF, FF, FF, FF, FF, FF}), 79 true, 80 "test", 81 Kind.SERVER, 82 Timestamp.fromMillis(startTime), 83 SpanData.Attributes.create(sampleAttributes(), 0), 84 SpanData.TimedEvents.create(singletonList(sampleAnnotation()), 0), 85 SpanData.TimedEvents.create(singletonList(sampleMessageEvent()), 0), 86 SpanData.Links.create(sampleLinks(), 0), 87 0, 88 Status.OK, 89 Timestamp.fromMillis(endTime)); 90 91 handler.export(singletonList(spanData)); 92 93 verify(mockSender).send(eq(process), captor.capture()); 94 List<Span> spans = captor.getValue(); 95 96 assertThat(spans.size()).isEqualTo(1); 97 Span span = spans.get(0); 98 99 assertThat(span.operationName).isEqualTo("test"); 100 assertThat(span.spanId).isEqualTo(256L); 101 assertThat(span.traceIdHigh).isEqualTo(-72057594037927936L); 102 assertThat(span.traceIdLow).isEqualTo(1L); 103 assertThat(span.parentSpanId).isEqualTo(Long.MAX_VALUE); 104 assertThat(span.flags).isEqualTo(1); 105 assertThat(span.startTime).isEqualTo(MILLISECONDS.toMicros(startTime)); 106 assertThat(span.duration).isEqualTo(MILLISECONDS.toMicros(endTime - startTime)); 107 108 assertThat(span.tags.size()).isEqualTo(5); 109 assertThat(span.tags) 110 .containsExactly( 111 new Tag("BOOL", TagType.BOOL).setVBool(false), 112 new Tag("LONG", TagType.LONG).setVLong(Long.MAX_VALUE), 113 new Tag(JaegerExporterHandler.SPAN_KIND, TagType.STRING).setVStr("server"), 114 new Tag("STRING", TagType.STRING) 115 .setVStr("Judge of a man by his questions rather than by his answers. -- Voltaire"), 116 new Tag(JaegerExporterHandler.STATUS_CODE, TagType.LONG).setVLong(0)); 117 118 assertThat(span.logs.size()).isEqualTo(2); 119 Log log = span.logs.get(0); 120 assertThat(log.timestamp).isEqualTo(1519629872987654L); 121 assertThat(log.fields.size()).isEqualTo(4); 122 assertThat(log.fields) 123 .containsExactly( 124 new Tag("message", TagType.STRING).setVStr("annotation #1"), 125 new Tag("bool", TagType.BOOL).setVBool(true), 126 new Tag("long", TagType.LONG).setVLong(1337L), 127 new Tag("string", TagType.STRING) 128 .setVStr("Kind words do not cost much. Yet they accomplish much. -- Pascal")); 129 log = span.logs.get(1); 130 assertThat(log.timestamp).isEqualTo(1519629871123456L); 131 assertThat(log.fields.size()).isEqualTo(4); 132 assertThat(log.fields) 133 .containsExactly( 134 new Tag("message", TagType.STRING).setVStr("sent message"), 135 new Tag("id", TagType.LONG).setVLong(42L), 136 new Tag("compressed_size", TagType.LONG).setVLong(69), 137 new Tag("uncompressed_size", TagType.LONG).setVLong(96)); 138 139 assertThat(span.references.size()).isEqualTo(1); 140 SpanRef reference = span.references.get(0); 141 assertThat(reference.traceIdHigh).isEqualTo(-1L); 142 assertThat(reference.traceIdLow).isEqualTo(-256L); 143 assertThat(reference.spanId).isEqualTo(512L); 144 assertThat(reference.refType).isEqualTo(SpanRefType.CHILD_OF); 145 } 146 147 @Test convertErrorSpanDataToJaegerThriftSpan()148 public void convertErrorSpanDataToJaegerThriftSpan() throws SenderException { 149 long startTime = 1519629870001L; 150 long endTime = 1519630148002L; 151 String statusMessage = "timeout"; 152 SpanData spanData = 153 SpanData.create( 154 sampleSpanContext(), 155 SpanId.fromBytes(new byte[] {(byte) 0x7F, FF, FF, FF, FF, FF, FF, FF}), 156 true, 157 "test", 158 Kind.SERVER, 159 Timestamp.fromMillis(startTime), 160 SpanData.Attributes.create(Collections.<String, AttributeValue>emptyMap(), 0), 161 SpanData.TimedEvents.create(Collections.<TimedEvent<Annotation>>emptyList(), 0), 162 SpanData.TimedEvents.create(Collections.<TimedEvent<MessageEvent>>emptyList(), 0), 163 SpanData.Links.create(Collections.<Link>emptyList(), 0), 164 0, 165 Status.DEADLINE_EXCEEDED.withDescription(statusMessage), 166 Timestamp.fromMillis(endTime)); 167 168 handler.export(singletonList(spanData)); 169 170 verify(mockSender).send(eq(process), captor.capture()); 171 List<Span> spans = captor.getValue(); 172 173 assertThat(spans.size()).isEqualTo(1); 174 Span span = spans.get(0); 175 176 assertThat(span.tags.size()).isEqualTo(3); 177 assertThat(span.tags) 178 .containsExactly( 179 new Tag(JaegerExporterHandler.SPAN_KIND, TagType.STRING).setVStr("server"), 180 new Tag(JaegerExporterHandler.STATUS_CODE, TagType.LONG).setVLong(4), 181 new Tag(JaegerExporterHandler.STATUS_MESSAGE, TagType.STRING).setVStr(statusMessage)); 182 } 183 sampleSpanContext()184 private static SpanContext sampleSpanContext() { 185 return SpanContext.create( 186 TraceId.fromBytes(new byte[] {FF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}), 187 SpanId.fromBytes(new byte[] {0, 0, 0, 0, 0, 0, 1, 0}), 188 TraceOptions.builder().setIsSampled(true).build(), 189 Tracestate.builder().build()); 190 } 191 sampleAttributes()192 private static ImmutableMap<String, AttributeValue> sampleAttributes() { 193 return ImmutableMap.of( 194 "BOOL", AttributeValue.booleanAttributeValue(false), 195 "LONG", AttributeValue.longAttributeValue(Long.MAX_VALUE), 196 "STRING", 197 AttributeValue.stringAttributeValue( 198 "Judge of a man by his questions rather than by his answers. -- Voltaire")); 199 } 200 sampleAnnotation()201 private static SpanData.TimedEvent<Annotation> sampleAnnotation() { 202 return SpanData.TimedEvent.create( 203 Timestamp.create(1519629872L, 987654321), 204 Annotation.fromDescriptionAndAttributes( 205 "annotation #1", 206 ImmutableMap.of( 207 "bool", AttributeValue.booleanAttributeValue(true), 208 "long", AttributeValue.longAttributeValue(1337L), 209 "string", 210 AttributeValue.stringAttributeValue( 211 "Kind words do not cost much. Yet they accomplish much. -- Pascal")))); 212 } 213 sampleMessageEvent()214 private static SpanData.TimedEvent<MessageEvent> sampleMessageEvent() { 215 return SpanData.TimedEvent.create( 216 Timestamp.create(1519629871L, 123456789), 217 MessageEvent.builder(MessageEvent.Type.SENT, 42L) 218 .setCompressedMessageSize(69) 219 .setUncompressedMessageSize(96) 220 .build()); 221 } 222 sampleLinks()223 private static List<Link> sampleLinks() { 224 return Lists.newArrayList( 225 Link.fromSpanContext( 226 SpanContext.create( 227 TraceId.fromBytes( 228 new byte[] {FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 0}), 229 SpanId.fromBytes(new byte[] {0, 0, 0, 0, 0, 0, 2, 0}), 230 TraceOptions.builder().setIsSampled(false).build(), 231 Tracestate.builder().build()), 232 Link.Type.CHILD_LINKED_SPAN, 233 ImmutableMap.of( 234 "Bool", AttributeValue.booleanAttributeValue(true), 235 "Long", AttributeValue.longAttributeValue(299792458L), 236 "String", 237 AttributeValue.stringAttributeValue( 238 "Man is condemned to be free; because once thrown into the world, " 239 + "he is responsible for everything he does. -- Sartre")))); 240 } 241 } 242