Skip to content
This repository was archived by the owner on Dec 23, 2023. It is now read-only.

Commit ddca5df

Browse files
authored
Exporter/Zipkin: Use configurations and add deadline. (#1885)
1 parent 1dc78a8 commit ddca5df

6 files changed

Lines changed: 365 additions & 24 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Also provide a `MetricServiceStub` option so that advanced users can use a custo
77
Monitoring client to make RPCs.
88
- Use `JaegerExporterConfiguration` for creating `JaegerTraceExporter`. Provide a `Deadline` option
99
with default value 10 seconds.
10+
- Use `ZipkinExporterConfiguration` for creating `ZipkinTraceExporter`. Provide a `Deadline` option
11+
with default value 10 seconds.
1012

1113
## 0.21.0 - 2019-04-30
1214
- Add HTTP text format serializer to Tag propagation component.

exporters/trace/zipkin/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ description = 'OpenCensus Trace Zipkin Exporter'
66
}
77

88
dependencies {
9+
compileOnly libraries.auto_value
10+
911
compile project(':opencensus-api'),
1012
libraries.guava,
1113
libraries.zipkin_reporter,
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
* Copyright 2019, 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.zipkin;
18+
19+
import com.google.auto.value.AutoValue;
20+
import com.google.common.annotations.VisibleForTesting;
21+
import com.google.common.base.Preconditions;
22+
import io.opencensus.common.Duration;
23+
import javax.annotation.Nullable;
24+
import javax.annotation.concurrent.Immutable;
25+
import zipkin2.codec.SpanBytesEncoder;
26+
import zipkin2.reporter.Sender;
27+
28+
/**
29+
* Configurations for {@link ZipkinTraceExporter}.
30+
*
31+
* @since 0.22
32+
*/
33+
@AutoValue
34+
@Immutable
35+
public abstract class ZipkinExporterConfiguration {
36+
37+
@VisibleForTesting static final Duration DEFAULT_DEADLINE = Duration.create(10, 0);
38+
39+
ZipkinExporterConfiguration() {}
40+
41+
/**
42+
* Returns the service name.
43+
*
44+
* @return the service name.
45+
* @since 0.22
46+
*/
47+
public abstract String getServiceName();
48+
49+
/**
50+
* Returns the Zipkin V2 URL.
51+
*
52+
* @return the Zipkin V2 URL.
53+
* @since 0.22
54+
*/
55+
public abstract String getV2Url();
56+
57+
/**
58+
* Returns the Zipkin sender.
59+
*
60+
* @return the Zipkin sender.
61+
* @since 0.22
62+
*/
63+
@Nullable
64+
public abstract Sender getSender();
65+
66+
/**
67+
* Returns the {@link SpanBytesEncoder}.
68+
*
69+
* <p>Default is {@link SpanBytesEncoder#JSON_V2}.
70+
*
71+
* @return the {@code SpanBytesEncoder}
72+
* @since 0.22
73+
*/
74+
public abstract SpanBytesEncoder getEncoder();
75+
76+
/**
77+
* Returns the deadline for exporting to Zipkin.
78+
*
79+
* <p>Default value is 10 seconds.
80+
*
81+
* @return the export deadline.
82+
* @since 0.22
83+
*/
84+
public abstract Duration getDeadline();
85+
86+
/**
87+
* Returns a new {@link Builder}.
88+
*
89+
* @return a {@code Builder}.
90+
* @since 0.22
91+
*/
92+
public static Builder builder() {
93+
return new AutoValue_ZipkinExporterConfiguration.Builder()
94+
.setV2Url("")
95+
.setEncoder(SpanBytesEncoder.JSON_V2)
96+
.setDeadline(DEFAULT_DEADLINE);
97+
}
98+
99+
/**
100+
* Builder for {@link ZipkinExporterConfiguration}.
101+
*
102+
* @since 0.22
103+
*/
104+
@AutoValue.Builder
105+
public abstract static class Builder {
106+
107+
@VisibleForTesting static final Duration ZERO = Duration.fromMillis(0);
108+
109+
Builder() {}
110+
111+
/**
112+
* Sets the service name.
113+
*
114+
* @param serviceName the service name.
115+
* @return this.
116+
* @since 0.22
117+
*/
118+
public abstract Builder setServiceName(String serviceName);
119+
120+
/**
121+
* Sets the Zipkin V2 URL, e.g.: "http://127.0.0.1:9411/api/v2/spans".
122+
*
123+
* <p>At least one of {@code V2Url} and {@code Sender} needs to be specified. If both {@code
124+
* V2Url} and {@code Sender} are set, {@code Sender} takes precedence.
125+
*
126+
* @param v2Url the Zipkin V2 URL.
127+
* @return this.
128+
* @since 0.22
129+
*/
130+
public abstract Builder setV2Url(String v2Url);
131+
132+
/**
133+
* Sets the Zipkin sender.
134+
*
135+
* <p>At least one of {@code V2Url} and {@code Sender} needs to be specified. If both {@code
136+
* V2Url} and {@code Sender} are set, {@code Sender} takes precedence.
137+
*
138+
* @param sender the Zipkin sender.
139+
* @return this.
140+
* @since 0.22
141+
*/
142+
public abstract Builder setSender(Sender sender);
143+
144+
/**
145+
* Sets the {@link SpanBytesEncoder}.
146+
*
147+
* @param encoder the {@code SpanBytesEncoder}.
148+
* @return this
149+
* @since 0.22
150+
*/
151+
public abstract Builder setEncoder(SpanBytesEncoder encoder);
152+
153+
/**
154+
* Sets the deadline for exporting to Zipkin.
155+
*
156+
* @param deadline the export deadline.
157+
* @return this
158+
* @since 0.22
159+
*/
160+
public abstract Builder setDeadline(Duration deadline);
161+
162+
abstract Duration getDeadline();
163+
164+
abstract String getV2Url();
165+
166+
@Nullable
167+
abstract Sender getSender();
168+
169+
abstract ZipkinExporterConfiguration autoBuild();
170+
171+
/**
172+
* Builds a {@link ZipkinExporterConfiguration}.
173+
*
174+
* @return a {@code ZipkinExporterConfiguration}.
175+
* @since 0.22
176+
*/
177+
public ZipkinExporterConfiguration build() {
178+
Preconditions.checkArgument(getDeadline().compareTo(ZERO) > 0, "Deadline must be positive.");
179+
Preconditions.checkArgument(
180+
!getV2Url().isEmpty() || getSender() != null,
181+
"Neither Zipkin V2 URL nor Zipkin sender is specified.");
182+
return autoBuild();
183+
}
184+
}
185+
}

exporters/trace/zipkin/src/main/java/io/opencensus/exporter/trace/zipkin/ZipkinExporterHandler.java

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
import static java.util.concurrent.TimeUnit.NANOSECONDS;
2020
import static java.util.concurrent.TimeUnit.SECONDS;
2121

22+
import com.google.common.util.concurrent.SimpleTimeLimiter;
23+
import com.google.common.util.concurrent.TimeLimiter;
24+
import io.opencensus.common.Duration;
2225
import io.opencensus.common.Function;
2326
import io.opencensus.common.Functions;
2427
import io.opencensus.common.Scope;
@@ -43,6 +46,10 @@
4346
import java.util.Enumeration;
4447
import java.util.List;
4548
import java.util.Map;
49+
import java.util.concurrent.Callable;
50+
import java.util.concurrent.Executors;
51+
import java.util.concurrent.TimeUnit;
52+
import java.util.concurrent.TimeoutException;
4653
import java.util.logging.Level;
4754
import java.util.logging.Logger;
4855
import zipkin2.Endpoint;
@@ -64,11 +71,14 @@ final class ZipkinExporterHandler extends SpanExporter.Handler {
6471
private final SpanBytesEncoder encoder;
6572
private final Sender sender;
6673
private final Endpoint localEndpoint;
74+
private final Duration deadline;
6775

68-
ZipkinExporterHandler(SpanBytesEncoder encoder, Sender sender, String serviceName) {
76+
ZipkinExporterHandler(
77+
SpanBytesEncoder encoder, Sender sender, String serviceName, Duration deadline) {
6978
this.encoder = encoder;
7079
this.sender = sender;
7180
this.localEndpoint = produceLocalEndpoint(serviceName);
81+
this.deadline = deadline;
7282
}
7383

7484
/** Logic borrowed from brave.internal.Platform.produceLocalEndpoint */
@@ -187,29 +197,49 @@ private static String attributeValueToString(AttributeValue attributeValue) {
187197
}
188198

189199
@Override
190-
public void export(Collection<SpanData> spanDataList) {
200+
public void export(final Collection<SpanData> spanDataList) {
191201
// Start a new span with explicit 1/10000 sampling probability to avoid the case when user
192202
// sets the default sampler to always sample and we get the gRPC span of the zipkin
193203
// export call always sampled and go to an infinite loop.
194204
Scope scope =
195205
tracer.spanBuilder("SendZipkinSpans").setSampler(probabilitySampler).startScopedSpan();
196206
try {
197-
List<byte[]> encodedSpans = new ArrayList<byte[]>(spanDataList.size());
198-
for (SpanData spanData : spanDataList) {
199-
encodedSpans.add(encoder.encode(generateSpan(spanData, localEndpoint)));
200-
}
201-
try {
202-
sender.sendSpans(encodedSpans).execute();
203-
} catch (IOException e) {
204-
tracer
205-
.getCurrentSpan()
206-
.setStatus(
207-
Status.UNKNOWN.withDescription(
208-
e.getMessage() == null ? e.getClass().getSimpleName() : e.getMessage()));
209-
throw new RuntimeException(e); // TODO: should we instead do drop metrics?
210-
}
207+
TimeLimiter timeLimiter = SimpleTimeLimiter.create(Executors.newSingleThreadExecutor());
208+
timeLimiter.callWithTimeout(
209+
new Callable<Void>() {
210+
@Override
211+
public Void call() throws Exception {
212+
doExport(spanDataList);
213+
return null;
214+
}
215+
},
216+
deadline.toMillis(),
217+
TimeUnit.MILLISECONDS);
218+
} catch (TimeoutException e) {
219+
handleException(getMessageOrDefault(e), "Timeout when exporting traces to Zipkin: " + e);
220+
} catch (InterruptedException e) {
221+
handleException(getMessageOrDefault(e), "Interrupted when exporting traces to Zipkin: " + e);
222+
} catch (Exception e) {
223+
handleException(getMessageOrDefault(e), "Failed to export traces to Zipkin: " + e);
211224
} finally {
212225
scope.close();
213226
}
214227
}
228+
229+
private void doExport(Collection<SpanData> spanDataList) throws IOException {
230+
List<byte[]> encodedSpans = new ArrayList<byte[]>(spanDataList.size());
231+
for (SpanData spanData : spanDataList) {
232+
encodedSpans.add(encoder.encode(generateSpan(spanData, localEndpoint)));
233+
}
234+
sender.sendSpans(encodedSpans).execute();
235+
}
236+
237+
private static void handleException(String description, String logMessage) {
238+
tracer.getCurrentSpan().setStatus(Status.UNKNOWN.withDescription(description));
239+
logger.log(Level.WARNING, logMessage);
240+
}
241+
242+
private static String getMessageOrDefault(final Exception e) {
243+
return e.getMessage() == null ? e.getClass().getSimpleName() : e.getMessage();
244+
}
215245
}

exporters/trace/zipkin/src/main/java/io/opencensus/exporter/trace/zipkin/ZipkinTraceExporter.java

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@
3636
*
3737
* <pre>{@code
3838
* public static void main(String[] args) {
39-
* ZipkinTraceExporter.createAndRegister("http://127.0.0.1:9411/api/v2/spans", "myservicename");
39+
* ZipkinTraceExporter.createAndRegister(
40+
* ZipkinExporterConfiguration.builder()
41+
* .setV2Url("http://127.0.0.1:9411/api/v2/spans")
42+
* .setServiceName("myservicename")
43+
* .build());
4044
* ... // Do work.
4145
* }
4246
* }</pre>
@@ -54,6 +58,32 @@ public final class ZipkinTraceExporter {
5458

5559
private ZipkinTraceExporter() {}
5660

61+
/**
62+
* Creates and registers the Zipkin Trace exporter to the OpenCensus library. Only one Zipkin
63+
* exporter can be registered at any point.
64+
*
65+
* @param configuration configuration for this exporter.
66+
* @throws IllegalStateException if a Zipkin exporter is already registered.
67+
* @since 0.22
68+
*/
69+
public static void createAndRegister(ZipkinExporterConfiguration configuration) {
70+
synchronized (monitor) {
71+
checkState(handler == null, "Zipkin exporter is already registered.");
72+
Sender sender = configuration.getSender();
73+
if (sender == null) {
74+
sender = URLConnectionSender.create(configuration.getV2Url());
75+
}
76+
Handler newHandler =
77+
new ZipkinExporterHandler(
78+
configuration.getEncoder(),
79+
sender,
80+
configuration.getServiceName(),
81+
configuration.getDeadline());
82+
handler = newHandler;
83+
register(Tracing.getExportComponent().getSpanExporter(), newHandler);
84+
}
85+
}
86+
5787
/**
5888
* Creates and registers the Zipkin Trace exporter to the OpenCensus library. Only one Zipkin
5989
* exporter can be registered at any point.
@@ -62,9 +92,12 @@ private ZipkinTraceExporter() {}
6292
* @param serviceName the {@link Span#localServiceName() local service name} of the process.
6393
* @throws IllegalStateException if a Zipkin exporter is already registered.
6494
* @since 0.12
95+
* @deprecated in favor of {@link #createAndRegister(ZipkinExporterConfiguration)}.
6596
*/
97+
@Deprecated
6698
public static void createAndRegister(String v2Url, String serviceName) {
67-
createAndRegister(SpanBytesEncoder.JSON_V2, URLConnectionSender.create(v2Url), serviceName);
99+
createAndRegister(
100+
ZipkinExporterConfiguration.builder().setV2Url(v2Url).setServiceName(serviceName).build());
68101
}
69102

70103
/**
@@ -76,15 +109,17 @@ public static void createAndRegister(String v2Url, String serviceName) {
76109
* @param serviceName the {@link Span#localServiceName() local service name} of the process.
77110
* @throws IllegalStateException if a Zipkin exporter is already registered.
78111
* @since 0.12
112+
* @deprecated in favor of {@link #createAndRegister(ZipkinExporterConfiguration)}.
79113
*/
114+
@Deprecated
80115
public static void createAndRegister(
81116
SpanBytesEncoder encoder, Sender sender, String serviceName) {
82-
synchronized (monitor) {
83-
checkState(handler == null, "Zipkin exporter is already registered.");
84-
Handler newHandler = new ZipkinExporterHandler(encoder, sender, serviceName);
85-
handler = newHandler;
86-
register(Tracing.getExportComponent().getSpanExporter(), newHandler);
87-
}
117+
createAndRegister(
118+
ZipkinExporterConfiguration.builder()
119+
.setSender(sender)
120+
.setEncoder(encoder)
121+
.setServiceName(serviceName)
122+
.build());
88123
}
89124

90125
/**

0 commit comments

Comments
 (0)