• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.google.common.truth.Truth.assertWithMessage;
21 import static java.lang.String.format;
22 import static java.lang.System.currentTimeMillis;
23 import static java.util.concurrent.TimeUnit.MILLISECONDS;
24 
25 import com.google.api.client.http.GenericUrl;
26 import com.google.api.client.http.HttpRequest;
27 import com.google.api.client.http.HttpRequestFactory;
28 import com.google.api.client.http.HttpResponse;
29 import com.google.api.client.http.javanet.NetHttpTransport;
30 import com.google.gson.GsonBuilder;
31 import com.google.gson.JsonArray;
32 import com.google.gson.JsonNull;
33 import com.google.gson.JsonObject;
34 import com.google.gson.JsonParser;
35 import io.opencensus.common.Scope;
36 import io.opencensus.trace.AttributeValue;
37 import io.opencensus.trace.SpanBuilder;
38 import io.opencensus.trace.Status;
39 import io.opencensus.trace.Tracer;
40 import io.opencensus.trace.Tracing;
41 import io.opencensus.trace.samplers.Samplers;
42 import java.io.IOException;
43 import java.util.Random;
44 import org.junit.AfterClass;
45 import org.junit.AssumptionViolatedException;
46 import org.junit.Before;
47 import org.junit.BeforeClass;
48 import org.junit.Test;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51 import org.testcontainers.containers.GenericContainer;
52 import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
53 
54 public class JaegerExporterHandlerIntegrationTest {
55   private static final String JAEGER_IMAGE = "jaegertracing/all-in-one:1.3";
56   private static final int JAEGER_HTTP_PORT = 16686;
57   private static final int JAEGER_HTTP_PORT_THRIFT = 14268;
58   private static final String SERVICE_NAME = "test";
59   private static final String SPAN_NAME = "my.org/ProcessVideo";
60   private static final String START_PROCESSING_VIDEO = "Start processing video.";
61   private static final String FINISHED_PROCESSING_VIDEO = "Finished processing video.";
62 
63   private static final Logger logger =
64       LoggerFactory.getLogger(JaegerExporterHandlerIntegrationTest.class);
65 
66   private final HttpRequestFactory httpRequestFactory =
67       new NetHttpTransport().createRequestFactory();
68 
69   private static GenericContainer<?> container;
70 
71   /** Starts a docker container optionally. For example, skips if Docker is unavailable. */
72   @SuppressWarnings("rawtypes")
73   @BeforeClass
startContainer()74   public static void startContainer() {
75     try {
76       container =
77           new GenericContainer(JAEGER_IMAGE)
78               .withExposedPorts(JAEGER_HTTP_PORT, JAEGER_HTTP_PORT_THRIFT)
79               .waitingFor(new HttpWaitStrategy());
80       container.start();
81     } catch (RuntimeException e) {
82       throw new AssumptionViolatedException("could not start docker container", e);
83     }
84   }
85 
86   @AfterClass
stopContainer()87   public static void stopContainer() {
88     if (container != null) {
89       container.stop();
90     }
91   }
92 
93   @Before
before()94   public void before() {
95     JaegerTraceExporter.createAndRegister(thriftTracesEndpoint(), SERVICE_NAME);
96   }
97 
98   @Test
exportToJaeger()99   public void exportToJaeger() throws InterruptedException, IOException {
100     Tracer tracer = Tracing.getTracer();
101     final long startTimeInMillis = currentTimeMillis();
102 
103     SpanBuilder spanBuilder =
104         tracer.spanBuilder(SPAN_NAME).setRecordEvents(true).setSampler(Samplers.alwaysSample());
105     int spanDurationInMillis = new Random().nextInt(10) + 1;
106 
107     Scope scopedSpan = spanBuilder.startScopedSpan();
108     try {
109       tracer.getCurrentSpan().addAnnotation(START_PROCESSING_VIDEO);
110       Thread.sleep(spanDurationInMillis); // Fake work.
111       tracer.getCurrentSpan().putAttribute("foo", AttributeValue.stringAttributeValue("bar"));
112       tracer.getCurrentSpan().addAnnotation(FINISHED_PROCESSING_VIDEO);
113     } catch (Exception e) {
114       tracer.getCurrentSpan().addAnnotation("Exception thrown when processing video.");
115       tracer.getCurrentSpan().setStatus(Status.UNKNOWN);
116       logger.error(e.getMessage());
117     } finally {
118       scopedSpan.close();
119     }
120 
121     logger.info("Wait longer than the reporting duration...");
122     // Wait for a duration longer than reporting duration (5s) to ensure spans are exported.
123     long timeWaitingForSpansToBeExportedInMillis = 5100L;
124     Thread.sleep(timeWaitingForSpansToBeExportedInMillis);
125     JaegerTraceExporter.unregister();
126     final long endTimeInMillis = currentTimeMillis();
127 
128     // Get traces recorded by Jaeger:
129     HttpRequest request =
130         httpRequestFactory.buildGetRequest(new GenericUrl(tracesForServiceEndpoint(SERVICE_NAME)));
131     HttpResponse response = request.execute();
132     String body = response.parseAsString();
133     assertWithMessage("Response was: " + body).that(response.getStatusCode()).isEqualTo(200);
134 
135     JsonObject result = new JsonParser().parse(body).getAsJsonObject();
136     // Pretty-print for debugging purposes:
137     logger.debug(new GsonBuilder().setPrettyPrinting().create().toJson(result));
138 
139     assertThat(result).isNotNull();
140     assertThat(result.get("total").getAsInt()).isEqualTo(0);
141     assertThat(result.get("limit").getAsInt()).isEqualTo(0);
142     assertThat(result.get("offset").getAsInt()).isEqualTo(0);
143     assertThat(result.get("errors").getAsJsonNull()).isEqualTo(JsonNull.INSTANCE);
144     JsonArray data = result.get("data").getAsJsonArray();
145     assertThat(data).isNotNull();
146     assertThat(data.size()).isEqualTo(1);
147     JsonObject trace = data.get(0).getAsJsonObject();
148     assertThat(trace).isNotNull();
149     assertThat(trace.get("traceID").getAsString()).matches("[a-z0-9]{1,32}");
150 
151     JsonArray spans = trace.get("spans").getAsJsonArray();
152     assertThat(spans).isNotNull();
153     assertThat(spans.size()).isEqualTo(1);
154 
155     JsonObject span = spans.get(0).getAsJsonObject();
156     assertThat(span).isNotNull();
157     assertThat(span.get("traceID").getAsString()).matches("[a-z0-9]{1,32}");
158     assertThat(span.get("spanID").getAsString()).matches("[a-z0-9]{1,16}");
159     assertThat(span.get("flags").getAsInt()).isEqualTo(1);
160     assertThat(span.get("operationName").getAsString()).isEqualTo(SPAN_NAME);
161     assertThat(span.get("references").getAsJsonArray()).isEmpty();
162     assertThat(span.get("startTime").getAsLong())
163         .isAtLeast(MILLISECONDS.toMicros(startTimeInMillis));
164     assertThat(span.get("startTime").getAsLong()).isAtMost(MILLISECONDS.toMicros(endTimeInMillis));
165     assertThat(span.get("duration").getAsLong())
166         .isAtLeast(MILLISECONDS.toMicros(spanDurationInMillis));
167     assertThat(span.get("duration").getAsLong())
168         .isAtMost(
169             MILLISECONDS.toMicros(spanDurationInMillis + timeWaitingForSpansToBeExportedInMillis));
170 
171     JsonArray tags = span.get("tags").getAsJsonArray();
172     assertThat(tags.size()).isEqualTo(1);
173     JsonObject tag = tags.get(0).getAsJsonObject();
174     assertThat(tag.get("key").getAsString()).isEqualTo("foo");
175     assertThat(tag.get("type").getAsString()).isEqualTo("string");
176     assertThat(tag.get("value").getAsString()).isEqualTo("bar");
177 
178     JsonArray logs = span.get("logs").getAsJsonArray();
179     assertThat(logs.size()).isEqualTo(2);
180 
181     JsonObject log1 = logs.get(0).getAsJsonObject();
182     long ts1 = log1.get("timestamp").getAsLong();
183     assertThat(ts1).isAtLeast(MILLISECONDS.toMicros(startTimeInMillis));
184     assertThat(ts1).isAtMost(MILLISECONDS.toMicros(endTimeInMillis));
185     JsonArray fields1 = log1.get("fields").getAsJsonArray();
186     assertThat(fields1.size()).isEqualTo(1);
187     JsonObject field1 = fields1.get(0).getAsJsonObject();
188     assertThat(field1.get("key").getAsString()).isEqualTo("description");
189     assertThat(field1.get("type").getAsString()).isEqualTo("string");
190     assertThat(field1.get("value").getAsString()).isEqualTo(START_PROCESSING_VIDEO);
191 
192     JsonObject log2 = logs.get(1).getAsJsonObject();
193     long ts2 = log2.get("timestamp").getAsLong();
194     assertThat(ts2).isAtLeast(MILLISECONDS.toMicros(startTimeInMillis));
195     assertThat(ts2).isAtMost(MILLISECONDS.toMicros(endTimeInMillis));
196     assertThat(ts2).isAtLeast(ts1);
197     JsonArray fields2 = log2.get("fields").getAsJsonArray();
198     assertThat(fields2.size()).isEqualTo(1);
199     JsonObject field2 = fields2.get(0).getAsJsonObject();
200     assertThat(field2.get("key").getAsString()).isEqualTo("description");
201     assertThat(field2.get("type").getAsString()).isEqualTo("string");
202     assertThat(field2.get("value").getAsString()).isEqualTo(FINISHED_PROCESSING_VIDEO);
203 
204     assertThat(span.get("processID").getAsString()).isEqualTo("p1");
205     assertThat(span.get("warnings").getAsJsonNull()).isEqualTo(JsonNull.INSTANCE);
206 
207     JsonObject processes = trace.get("processes").getAsJsonObject();
208     assertThat(processes.size()).isEqualTo(1);
209     JsonObject p1 = processes.get("p1").getAsJsonObject();
210     assertThat(p1.get("serviceName").getAsString()).isEqualTo(SERVICE_NAME);
211     assertThat(p1.get("tags").getAsJsonArray().size()).isEqualTo(0);
212     assertThat(trace.get("warnings").getAsJsonNull()).isEqualTo(JsonNull.INSTANCE);
213   }
214 
thriftTracesEndpoint()215   private static String thriftTracesEndpoint() {
216     return format(
217         "http://%s:%s/api/traces",
218         container.getContainerIpAddress(), container.getMappedPort(JAEGER_HTTP_PORT_THRIFT));
219   }
220 
tracesForServiceEndpoint(String service)221   private static String tracesForServiceEndpoint(String service) {
222     return format(
223         "http://%s:%s/api/traces?service=%s",
224         container.getContainerIpAddress(), container.getMappedPort(JAEGER_HTTP_PORT), service);
225   }
226 }
227