Skip to content

Commit 695cec8

Browse files
Add tracing (#80)
* Add Noop tracer and trace calls * Add opencensus tracer * Unit test OpenCensusSpan * Some more tracing tests * Put tracing inside github call instead * Opencensus tracer test * Set status_code and message on request exceptions
1 parent 8b28851 commit 695cec8

File tree

11 files changed

+571
-5
lines changed

11 files changed

+571
-5
lines changed

pom.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
<immutables.version>2.8.3</immutables.version>
101101
<powermock.version>2.0.2</powermock.version>
102102
<objenesis.version>3.0.1</objenesis.version>
103+
<opencensus.version>0.26.0</opencensus.version>
103104

104105
<shade.id>${project.groupId}.githubclient.shade</shade.id>
105106
</properties>
@@ -170,6 +171,11 @@
170171
<artifactId>jjwt-api</artifactId>
171172
<version>0.10.5</version>
172173
</dependency>
174+
<dependency>
175+
<groupId>io.opencensus</groupId>
176+
<artifactId>opencensus-api</artifactId>
177+
<version>${opencensus.version}</version>
178+
</dependency>
173179
<dependency>
174180
<groupId>io.jsonwebtoken</groupId>
175181
<artifactId>jjwt-impl</artifactId>
@@ -218,6 +224,18 @@
218224
<version>${mockito-core.version}</version>
219225
<scope>test</scope>
220226
</dependency>
227+
<dependency>
228+
<groupId>io.opencensus</groupId>
229+
<artifactId>opencensus-testing</artifactId>
230+
<version>${opencensus.version}</version>
231+
<scope>test</scope>
232+
</dependency>
233+
<dependency>
234+
<groupId>io.opencensus</groupId>
235+
<artifactId>opencensus-impl</artifactId>
236+
<version>${opencensus.version}</version>
237+
<scope>test</scope>
238+
</dependency>
221239
<dependency>
222240
<groupId>com.squareup.okhttp3</groupId>
223241
<artifactId>mockwebserver</artifactId>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*-
2+
* -\-\-
3+
* github-api
4+
* --
5+
* Copyright (C) 2021 Spotify AB
6+
* --
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* -/-/-
19+
*/
20+
21+
package com.spotify.github;
22+
23+
public interface Span extends AutoCloseable {
24+
25+
Span success();
26+
27+
Span failure(Throwable t);
28+
29+
/** Close span. Must be called for any opened span. */
30+
@Override
31+
void close();
32+
}
33+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*-
2+
* -\-\-
3+
* github-api
4+
* --
5+
* Copyright (C) 2021 Spotify AB
6+
* --
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* -/-/-
19+
*/
20+
21+
package com.spotify.github;
22+
23+
import java.util.concurrent.CompletionStage;
24+
25+
public interface Tracer {
26+
27+
/** Create scoped span. Span will be closed when future completes. */
28+
Span span(
29+
String path, String method, CompletionStage<?> future);
30+
31+
}
32+
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*-
2+
* -\-\-
3+
* github-api
4+
* --
5+
* Copyright (C) 2016 - 2021 Spotify AB
6+
* --
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* -/-/-
19+
*/
20+
21+
package com.spotify.github.opencensus;
22+
import static java.util.Objects.requireNonNull;
23+
import com.spotify.github.Span;
24+
import com.spotify.github.v3.exceptions.RequestNotOkException;
25+
import io.opencensus.trace.AttributeValue;
26+
import io.opencensus.trace.Status;
27+
28+
class OpenCensusSpan implements Span {
29+
30+
public static final int NOT_FOUND = 404;
31+
public static final int INTERNAL_SERVER_ERROR = 500;
32+
private final io.opencensus.trace.Span span;
33+
34+
OpenCensusSpan(final io.opencensus.trace.Span span) {
35+
this.span = requireNonNull(span);
36+
}
37+
38+
@Override
39+
public Span success() {
40+
span.setStatus(Status.OK);
41+
return this;
42+
}
43+
44+
@Override
45+
public Span failure(final Throwable t) {
46+
if (t instanceof RequestNotOkException) {
47+
RequestNotOkException ex = (RequestNotOkException) t;
48+
span.putAttribute("http.status_code", AttributeValue.longAttributeValue(ex.statusCode()));
49+
span.putAttribute("message", AttributeValue.stringAttributeValue(ex.getMessage()));
50+
if (ex.statusCode() - INTERNAL_SERVER_ERROR >= 0) {
51+
span.putAttribute("error", AttributeValue.booleanAttributeValue(true));
52+
}
53+
}
54+
span.setStatus(Status.UNKNOWN);
55+
return this;
56+
}
57+
58+
@Override
59+
public void close() {
60+
span.end();
61+
}
62+
}
63+
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*-
2+
* -\-\-
3+
* github-api
4+
* --
5+
* Copyright (C) 2021 Spotify AB
6+
* --
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* -/-/-
19+
*/
20+
21+
package com.spotify.github.opencensus;
22+
23+
import com.spotify.github.Span;
24+
import com.spotify.github.Tracer;
25+
import io.opencensus.trace.Tracing;
26+
27+
import java.util.concurrent.CompletionStage;
28+
29+
import static io.opencensus.trace.AttributeValue.stringAttributeValue;
30+
import static io.opencensus.trace.Span.Kind.CLIENT;
31+
import static java.util.Objects.requireNonNull;
32+
33+
public class OpenCensusTracer implements Tracer {
34+
35+
private static final io.opencensus.trace.Tracer TRACER = Tracing.getTracer();
36+
37+
@Override
38+
public Span span(final String name, final String method, final CompletionStage<?> future) {
39+
return internalSpan(name, method, future);
40+
}
41+
42+
@SuppressWarnings("MustBeClosedChecker")
43+
private Span internalSpan(
44+
final String path,
45+
final String method,
46+
final CompletionStage<?> future) {
47+
requireNonNull(path);
48+
requireNonNull(future);
49+
50+
final io.opencensus.trace.Span ocSpan =
51+
TRACER.spanBuilder("GitHub Request").setSpanKind(CLIENT).startSpan();
52+
53+
ocSpan.putAttribute("component", stringAttributeValue("github-api-client"));
54+
ocSpan.putAttribute("peer.service", stringAttributeValue("github"));
55+
ocSpan.putAttribute("http.url", stringAttributeValue(path));
56+
ocSpan.putAttribute("method", stringAttributeValue(method));
57+
final Span span = new OpenCensusSpan(ocSpan);
58+
59+
future.whenComplete(
60+
(result, t) -> {
61+
if (t == null) {
62+
span.success();
63+
} else {
64+
span.failure(t);
65+
}
66+
span.close();
67+
});
68+
69+
return span;
70+
}
71+
}

src/main/java/com/spotify/github/v3/clients/GitHubClient.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import static okhttp3.MediaType.parse;
2525

2626
import com.fasterxml.jackson.core.type.TypeReference;
27+
import com.spotify.github.Tracer;
2728
import com.spotify.github.jackson.Json;
2829
import com.spotify.github.v3.checks.AccessToken;
2930
import com.spotify.github.v3.comment.Comment;
@@ -65,6 +66,8 @@
6566
*/
6667
public class GitHubClient {
6768

69+
private Tracer tracer = NoopTracer.INSTANCE;
70+
6871
static final Consumer<Response> IGNORE_RESPONSE_CONSUMER = (response) -> {
6972
if (response.body() != null) {
7073
response.body().close();
@@ -300,6 +303,11 @@ static String responseBodyUnchecked(final Response response) {
300303
}
301304
}
302305

306+
public GitHubClient withTracer(final Tracer tracer) {
307+
this.tracer = tracer;
308+
return this;
309+
}
310+
303311
public Optional<byte[]> getPrivateKey() {
304312
return Optional.ofNullable(privateKey);
305313
}
@@ -724,7 +732,7 @@ public void onResponse(final Call call, final Response response) {
724732
});
725733
}
726734
});
727-
735+
tracer.span(request.url().toString(), request.method(), future);
728736
return future;
729737
}
730738

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*-
2+
* -\-\-
3+
* github-api
4+
* --
5+
* Copyright (C) 2021 Spotify AB
6+
* --
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* -/-/-
19+
*/
20+
21+
package com.spotify.github.v3.clients;
22+
import com.spotify.github.Span;
23+
import com.spotify.github.Tracer;
24+
25+
import java.util.concurrent.CompletionStage;
26+
27+
public class NoopTracer implements Tracer {
28+
29+
public static final NoopTracer INSTANCE = new NoopTracer();
30+
private static final Span SPAN =
31+
new Span() {
32+
@Override
33+
public Span success() {
34+
return this;
35+
}
36+
37+
@Override
38+
public Span failure(final Throwable t) {
39+
return this;
40+
}
41+
42+
@Override
43+
public void close() {}
44+
};
45+
46+
private NoopTracer() {}
47+
48+
@Override
49+
public Span span(
50+
final String path,
51+
final String method,
52+
final CompletionStage<?> future) {
53+
return SPAN;
54+
}
55+
56+
}
57+

0 commit comments

Comments
 (0)