Skip to content

Commit 05589b8

Browse files
committed
Tracing: added OpenTelemetry tracing example.
Added OpenTelemetry and Zipkin connection configuration classes, along with docker-compose file, example run script and related changes in README.
1 parent 142b9af commit 05589b8

File tree

6 files changed

+360
-1
lines changed

6 files changed

+360
-1
lines changed

driver-examples/README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ Apache Cassandra.
55

66
## Usage
77

8-
Unless otherwise stated, all examples assume that you have a single-node Cassandra 3.0 cluster
8+
Unless otherwise stated, all examples assume that you have a single-node Cassandra 3.0 cluster
99
listening on localhost:9042.
1010

11+
Before running examples, make sure you installed repo artifacts (in root driver directory):
12+
```
13+
mvn install -q -Dmaven.test.skip=true
14+
```
15+
16+
To conveniently run the example showing OpenTelemetry integration (ZipkinConfiguration),
17+
you can use the provided ```docker-compose.yaml``` file (which runs both Scylla cluster and Zipkin instance):
18+
```
19+
docker-compose up
20+
./runOpenTelemetryExample.sh
21+
```

driver-examples/docker-compose.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
version: '3.4'
2+
3+
services:
4+
zipkin:
5+
image: openzipkin/zipkin
6+
ports:
7+
- "9411:9411"
8+
scylla_node:
9+
image: scylladb/scylla
10+
ports:
11+
- "9042:9042"
12+
command: "--smp 1 --skip-wait-for-gossip-to-settle 0"

driver-examples/pom.xml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@
4848
<optional>true</optional>
4949
</dependency>
5050

51+
<dependency>
52+
<groupId>com.scylladb</groupId>
53+
<artifactId>scylla-driver-opentelemetry</artifactId>
54+
</dependency>
55+
5156
<!--Jackson-->
5257

5358
<dependency>
@@ -134,6 +139,26 @@
134139
<artifactId>logback-classic</artifactId>
135140
</dependency>
136141

142+
<!-- OpenTelemetry -->
143+
144+
<dependency>
145+
<groupId>io.opentelemetry</groupId>
146+
<artifactId>opentelemetry-sdk</artifactId>
147+
<version>1.9.1</version>
148+
</dependency>
149+
150+
<dependency>
151+
<groupId>io.opentelemetry</groupId>
152+
<artifactId>opentelemetry-semconv</artifactId>
153+
<version>1.9.0-alpha</version>
154+
</dependency>
155+
156+
<dependency>
157+
<groupId>io.opentelemetry</groupId>
158+
<artifactId>opentelemetry-exporter-zipkin</artifactId>
159+
<version>1.9.1</version>
160+
</dependency>
161+
137162
</dependencies>
138163

139164
<build>
@@ -184,6 +209,15 @@
184209
</configuration>
185210
</plugin>
186211

212+
<plugin>
213+
<groupId>org.apache.maven.plugins</groupId>
214+
<artifactId>maven-compiler-plugin</artifactId>
215+
<configuration>
216+
<source>8</source>
217+
<target>8</target>
218+
</configuration>
219+
</plugin>
220+
187221
</plugins>
188222

189223
</build>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mvn -q exec:java -Dexec.mainClass="com.datastax.driver.examples.opentelemetry.ZipkinUsage"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (C) 2021 ScyllaDB
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 com.datastax.driver.examples.opentelemetry;
18+
19+
import io.opentelemetry.api.OpenTelemetry;
20+
import io.opentelemetry.api.common.Attributes;
21+
import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter;
22+
import io.opentelemetry.sdk.OpenTelemetrySdk;
23+
import io.opentelemetry.sdk.resources.Resource;
24+
import io.opentelemetry.sdk.trace.SdkTracerProvider;
25+
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
26+
import io.opentelemetry.sdk.trace.export.SpanExporter;
27+
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
28+
29+
/** Example showing how to configure OpenTelemetry for tracing with Scylla Java Driver. */
30+
class OpenTelemetryConfiguration {
31+
private static final String SERVICE_NAME = "Scylla Java driver";
32+
33+
public static OpenTelemetry initialize(SpanExporter spanExporter) {
34+
Resource serviceNameResource =
35+
Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, SERVICE_NAME));
36+
37+
// Set to process the spans by the spanExporter.
38+
final SdkTracerProvider tracerProvider =
39+
SdkTracerProvider.builder()
40+
.addSpanProcessor(SimpleSpanProcessor.create(spanExporter))
41+
.setResource(Resource.getDefault().merge(serviceNameResource))
42+
.build();
43+
OpenTelemetrySdk openTelemetry =
44+
OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).buildAndRegisterGlobal();
45+
46+
// Add a shutdown hook to shut down the SDK.
47+
Runtime.getRuntime()
48+
.addShutdownHook(
49+
new Thread(
50+
new Runnable() {
51+
@Override
52+
public void run() {
53+
tracerProvider.close();
54+
}
55+
}));
56+
57+
// Return the configured instance so it can be used for instrumentation.
58+
return openTelemetry;
59+
}
60+
61+
public static OpenTelemetry initializeForZipkin(String ip, int port) {
62+
String endpointPath = "/api/v2/spans";
63+
String httpUrl = String.format("http://%s:%s", ip, port);
64+
65+
SpanExporter exporter =
66+
ZipkinSpanExporter.builder().setEndpoint(httpUrl + endpointPath).build();
67+
68+
return initialize(exporter);
69+
}
70+
}
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
/*
2+
* Copyright DataStax, Inc.
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+
/*
18+
* Copyright (C) 2021 ScyllaDB
19+
*
20+
* Modified by ScyllaDB
21+
*/
22+
package com.datastax.driver.examples.opentelemetry;
23+
24+
import com.datastax.driver.core.BoundStatement;
25+
import com.datastax.driver.core.Cluster;
26+
import com.datastax.driver.core.PreparedStatement;
27+
import com.datastax.driver.core.Session;
28+
import com.datastax.driver.core.tracing.TracingInfoFactory;
29+
import com.datastax.driver.opentelemetry.OpenTelemetryTracingInfoFactory;
30+
import io.opentelemetry.api.OpenTelemetry;
31+
import io.opentelemetry.api.trace.Span;
32+
import io.opentelemetry.api.trace.Tracer;
33+
import io.opentelemetry.context.Scope;
34+
35+
/**
36+
* Creates a keyspace and tables, and loads some data into them. Sends OpenTelemetry tracing data to
37+
* Zipkin tracing backend
38+
*
39+
* <p>Preconditions: - a Scylla cluster is running and accessible through the contacts points
40+
* identified by CONTACT_POINTS and PORT and Zipkin backend is running and accessible through the
41+
* contacts points identified by ZIPKIN_CONTACT_POINT and ZIPKIN_PORT.
42+
*
43+
* <p>Side effects: - creates a new keyspace "simplex" in the cluster. If a keyspace with this name
44+
* already exists, it will be reused; - creates two tables "simplex.songs" and "simplex.playlists".
45+
* If they exist already, they will be reused; - inserts a row in each table.
46+
*/
47+
public class ZipkinUsage {
48+
private static final String CONTACT_POINT = "127.0.0.1";
49+
private static final int PORT = 9042;
50+
51+
private static final String ZIPKIN_CONTACT_POINT = "127.0.0.1";
52+
private static final int ZIPKIN_PORT = 9411;
53+
54+
private Cluster cluster;
55+
private Session session;
56+
57+
private Tracer tracer;
58+
59+
public static void main(String[] args) {
60+
// Workaround for setting ContextStorage to ThreadLocalContextStorage.
61+
System.setProperty("io.opentelemetry.context.contextStorageProvider", "default");
62+
63+
ZipkinUsage client = new ZipkinUsage();
64+
65+
try {
66+
client.connect();
67+
client.createSchema();
68+
client.loadData();
69+
client.querySchema();
70+
System.out.println(
71+
"All requests have been completed. Now you can visit Zipkin at "
72+
+ "http://"
73+
+ ZIPKIN_CONTACT_POINT
74+
+ ":"
75+
+ ZIPKIN_PORT
76+
+ " and examine the produced trace.");
77+
} finally {
78+
client.close();
79+
}
80+
}
81+
82+
/** Initiates a connection to the cluster. */
83+
public void connect() {
84+
cluster = Cluster.builder().addContactPoints(CONTACT_POINT).withPort(PORT).build();
85+
86+
System.out.printf("Connected to cluster: %s%n", cluster.getMetadata().getClusterName());
87+
88+
OpenTelemetry openTelemetry =
89+
OpenTelemetryConfiguration.initializeForZipkin(ZIPKIN_CONTACT_POINT, ZIPKIN_PORT);
90+
tracer = openTelemetry.getTracerProvider().get("this");
91+
TracingInfoFactory tracingInfoFactory = new OpenTelemetryTracingInfoFactory(tracer);
92+
cluster.setTracingInfoFactory(tracingInfoFactory);
93+
94+
session = cluster.connect();
95+
}
96+
97+
/** Creates the schema (keyspace) and tables for this example. */
98+
public void createSchema() {
99+
session.execute("DROP KEYSPACE IF EXISTS simplex;");
100+
Span parentSpan = tracer.spanBuilder("create schema").startSpan();
101+
try (Scope parentScope = parentSpan.makeCurrent()) {
102+
{
103+
Span span = tracer.spanBuilder("create simplex").startSpan();
104+
try (Scope scope = span.makeCurrent()) {
105+
session.execute(
106+
"CREATE KEYSPACE IF NOT EXISTS simplex WITH replication "
107+
+ "= {'class':'SimpleStrategy', 'replication_factor':1};");
108+
109+
} finally {
110+
span.end();
111+
}
112+
}
113+
{
114+
Span span = tracer.spanBuilder("create simplex.songs").startSpan();
115+
try (Scope scope = span.makeCurrent()) {
116+
session.executeAsync(
117+
"CREATE TABLE IF NOT EXISTS simplex.songs ("
118+
+ "id uuid,"
119+
+ "title text,"
120+
+ "album text,"
121+
+ "artist text,"
122+
+ "tags set<text>,"
123+
+ "data blob,"
124+
+ "PRIMARY KEY ((title, artist), album)"
125+
+ ");");
126+
} finally {
127+
span.end();
128+
}
129+
}
130+
{
131+
Span span = tracer.spanBuilder("create simplex.playlists").startSpan();
132+
try (Scope scope = span.makeCurrent()) {
133+
session.execute(
134+
"CREATE TABLE IF NOT EXISTS simplex.playlists ("
135+
+ "id uuid,"
136+
+ "title text,"
137+
+ "album text, "
138+
+ "artist text,"
139+
+ "song_id uuid,"
140+
+ "PRIMARY KEY (id, title, album, artist)"
141+
+ ");");
142+
143+
} finally {
144+
span.end();
145+
}
146+
}
147+
} finally {
148+
parentSpan.end();
149+
}
150+
}
151+
152+
/** Inserts data into the tables. */
153+
public void loadData() {
154+
Span parentSpan = tracer.spanBuilder("load data").startSpan();
155+
try (Scope parentScope = parentSpan.makeCurrent()) {
156+
157+
Span prepareSpan = tracer.spanBuilder("prepare").startSpan();
158+
PreparedStatement ps;
159+
try (Scope prepareScope = prepareSpan.makeCurrent()) {
160+
ps =
161+
session.prepare(
162+
"INSERT INTO simplex.songs (id, title, album, artist, tags) "
163+
+ "VALUES ("
164+
+ "756716f7-2e54-4715-9f00-91dcbea6cf50,"
165+
+ "?,"
166+
+ "?,"
167+
+ "?,"
168+
+ "{'jazz', '2013'})"
169+
+ ";");
170+
} finally {
171+
prepareSpan.end();
172+
}
173+
174+
Span bindSpan = tracer.spanBuilder("bind").startSpan();
175+
BoundStatement bound;
176+
try (Scope bindScope = bindSpan.makeCurrent()) {
177+
bound = ps.bind("La Petite Tonkinoise", "Bye Bye Blackbird", "Joséphine Baker");
178+
} finally {
179+
bindSpan.end();
180+
}
181+
182+
Span span = tracer.spanBuilder("insert simplex.songs").startSpan();
183+
try (Scope scope = span.makeCurrent()) {
184+
session.execute(bound);
185+
} finally {
186+
span.end();
187+
}
188+
189+
} finally {
190+
parentSpan.end();
191+
}
192+
}
193+
194+
public void querySchema() {
195+
Span parentSpan = tracer.spanBuilder("query schema").startSpan();
196+
try (Scope parentScope = parentSpan.makeCurrent()) {
197+
198+
Span prepareSpan = tracer.spanBuilder("prepare").startSpan();
199+
PreparedStatement ps;
200+
try (Scope prepareScope = prepareSpan.makeCurrent()) {
201+
ps = session.prepare("SELECT * FROM simplex.songs WHERE artist = ? AND title = ?;");
202+
} finally {
203+
prepareSpan.end();
204+
}
205+
206+
Span bindSpan = tracer.spanBuilder("bind").startSpan();
207+
BoundStatement bound;
208+
try (Scope bindScope = bindSpan.makeCurrent()) {
209+
bound = ps.bind("Joséphine Baker", "La Petite Tonkinoise");
210+
} finally {
211+
bindSpan.end();
212+
}
213+
214+
Span span = tracer.spanBuilder("query simplex.songs").startSpan();
215+
try (Scope scope = span.makeCurrent()) {
216+
session.execute(bound);
217+
} finally {
218+
span.end();
219+
}
220+
221+
} finally {
222+
parentSpan.end();
223+
}
224+
}
225+
226+
/** Closes the session and the cluster. */
227+
public void close() {
228+
session.close();
229+
cluster.close();
230+
}
231+
}

0 commit comments

Comments
 (0)