Skip to content

Commit f1809d6

Browse files
committed
Adding BigQuery + StackDriver Monitoring API Showcase sample.
1 parent 398f9c3 commit f1809d6

File tree

4 files changed

+368
-2
lines changed

4 files changed

+368
-2
lines changed

bigquery/cloud-client/README.md

+42
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,45 @@ documentation](https://cloud.google.com/bigquery/create-simple-app-api):
4646

4747
mvn exec:java -Dexec.mainClass=com.example.bigquery.SimpleApp
4848

49+
## BigQuery + Logging SimpleApp
50+
51+
This API Showcase demonstrates how to run a BigQuery query and then log some metrics to StackDriver Monitoring
52+
from the results, including the query runtime and number of rows returned.
53+
54+
### Clone the sample app
55+
56+
Copy the sample apps to your local machine, and cd to the bigquery/cloud-client directory:
57+
58+
```
59+
git clone https://github.com/GoogleCloudPlatform/java-docs-samples
60+
cd java-docs-samples/bigquery/cloud-client
61+
```
62+
63+
### Setup
64+
65+
- Make sure [`gcloud`](https://cloud.google.com/sdk/docs/) is installed and initialized:
66+
```
67+
gcloud init
68+
```
69+
- If this is the first time you are creating an App Engine project
70+
```
71+
gcloud app create
72+
```
73+
- For local development, [set up][set-up] authentication
74+
- Enable [BigQuery][bigquery-api] and [Monitoring][monitoring-api] APIs
75+
- If you have not already enabled your project for StackDriver, do so by following [these instructions][stackdriver-setup].
76+
77+
[set-up]: https://cloud.google.com/docs/authentication/getting-started
78+
[bigquery-api]: https://console.cloud.google.com/launcher/details/google/bigquery-json.googleapis.com
79+
[monitoring-api]: https://console.cloud.google.com/launcher/details/google/monitoring.googleapis.com
80+
[stackdriver-setup]: https://cloud.google.com/monitoring/accounts/tiers#not-enabled
81+
82+
### Run the SimpleApp
83+
84+
Build your project with:
85+
86+
mvn clean package -DskipTests
87+
88+
Provide the projectId as a system variable when running the sample.
89+
90+
mvn exec:java -Dexec.mainClass=com.example.bigquery_logging.SimpleApp -DprojectId=<your-project-id>

bigquery/cloud-client/pom.xml

+11-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
</parent>
3131

3232
<properties>
33-
<maven.compiler.target>1.7</maven.compiler.target>
34-
<maven.compiler.source>1.7</maven.compiler.source>
33+
<maven.compiler.target>1.8</maven.compiler.target>
34+
<maven.compiler.source>1.8</maven.compiler.source>
3535
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
3636
</properties>
3737

@@ -43,6 +43,15 @@
4343
<version>0.33.0-beta</version>
4444
</dependency>
4545
<!-- [END dependencies] -->
46+
47+
<!-- [START monitoring_dependencies ] -->
48+
<dependency>
49+
<groupId>com.google.cloud</groupId>
50+
<artifactId>google-cloud-monitoring</artifactId>
51+
<version>0.33.0-beta</version>
52+
</dependency>
53+
<!-- [END monitoring_dependencies ] -->
54+
4655
<dependency>
4756
<groupId>commons-cli</groupId>
4857
<artifactId>commons-cli</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2018 Google 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+
package com.example.bigquery_logging;
18+
19+
import com.google.api.MetricDescriptor;
20+
21+
import java.util.Objects;
22+
23+
public class RequiredMetric {
24+
private String shortName;
25+
private String name;
26+
private String description;
27+
private MetricDescriptor.MetricKind metricKind;
28+
private MetricDescriptor.ValueType valueType;
29+
30+
public RequiredMetric(String shortName, String description) {
31+
this.shortName = shortName;
32+
this.name = "custom.googleapis.com/" + shortName;
33+
this.description = description;
34+
this.metricKind = MetricDescriptor.MetricKind.GAUGE;
35+
this.valueType = MetricDescriptor.ValueType.INT64;
36+
}
37+
38+
public RequiredMetric(String shortName,
39+
String name,
40+
String description,
41+
MetricDescriptor.MetricKind metricKind,
42+
MetricDescriptor.ValueType valueType) {
43+
this.shortName = shortName;
44+
this.name = name;
45+
this.description = description;
46+
this.metricKind = metricKind;
47+
this.valueType = valueType;
48+
}
49+
50+
public String getShortName() {
51+
return shortName;
52+
}
53+
54+
public String getName() {
55+
return name;
56+
}
57+
58+
public String getDescription() {
59+
return description;
60+
}
61+
62+
public MetricDescriptor.MetricKind getMetricKind() {
63+
return metricKind;
64+
}
65+
66+
public MetricDescriptor.ValueType getValueType() {
67+
return valueType;
68+
}
69+
70+
@Override
71+
public boolean equals(Object o) {
72+
if (this == o) return true;
73+
if (o == null || getClass() != o.getClass()) return false;
74+
RequiredMetric that = (RequiredMetric) o;
75+
return Objects.equals(name, that.name) &&
76+
metricKind == that.metricKind &&
77+
valueType == that.valueType;
78+
}
79+
80+
@Override
81+
public int hashCode() {
82+
return Objects.hash(name, metricKind, valueType);
83+
}
84+
85+
86+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
/*
2+
* Copyright 2018 Google 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+
package com.example.bigquery_logging;
18+
19+
// [START bigquery_logging_simple_app_all]
20+
// [START bigquery_logging_simple_app_deps]
21+
22+
import com.google.api.Metric;
23+
import com.google.api.MetricDescriptor;
24+
import com.google.cloud.bigquery.*;
25+
import com.google.cloud.monitoring.v3.MetricServiceClient;
26+
import com.google.cloud.monitoring.v3.PagedResponseWrappers;
27+
import com.google.common.collect.ImmutableSet;
28+
import com.google.common.collect.Lists;
29+
import com.google.common.collect.Maps;
30+
import com.google.monitoring.v3.*;
31+
import com.google.protobuf.util.Timestamps;
32+
import org.joda.time.DateTime;
33+
import org.joda.time.Duration;
34+
35+
import java.io.IOException;
36+
import java.util.*;
37+
import java.util.concurrent.TimeUnit;
38+
// [END bigquery_logging_simple_app_deps]
39+
40+
public class SimpleApp {
41+
// [START bigquery_logging_simple_app_metrics]
42+
private static final RequiredMetric QUERY_DURATION_METRIC = new RequiredMetric(
43+
"queryDuration",
44+
"Time it took a query to run.");
45+
private static final RequiredMetric ROWS_RETURNED_METRIC = new RequiredMetric(
46+
"rowsReturned",
47+
"Total rows returned by the query result.");
48+
private static final Set<RequiredMetric> REQUIRED_METRICS = ImmutableSet.of(
49+
QUERY_DURATION_METRIC,
50+
ROWS_RETURNED_METRIC
51+
);
52+
// [END bigquery_logging_simple_app_metrics]
53+
private final MetricServiceClient client;
54+
private final String projectName;
55+
56+
private SimpleApp() throws IOException {
57+
this(MetricServiceClient.create());
58+
}
59+
60+
private SimpleApp(MetricServiceClient metricsClient) {
61+
client = metricsClient;
62+
projectName = String.format("projects/%s", System.getProperty("projectId"));
63+
}
64+
65+
public static void main(String... args) throws Exception {
66+
SimpleApp app = new SimpleApp();
67+
app.Run();
68+
}
69+
70+
private void Run() throws InterruptedException {
71+
// [START bigquery_logging_simple_app_client]
72+
BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();
73+
// [END bigquery_logging_simple_app_client]
74+
// [START bigquery_logging_simple_app_query]
75+
QueryJobConfiguration queryConfig =
76+
QueryJobConfiguration.newBuilder(
77+
"SELECT "
78+
+ "CONCAT('https://stackoverflow.com/questions/', CAST(id as STRING)) as url, "
79+
+ "view_count "
80+
+ "FROM `bigquery-public-data.stackoverflow.posts_questions` "
81+
+ "WHERE tags like '%google-bigquery%' "
82+
+ "ORDER BY favorite_count DESC LIMIT 10")
83+
// Use standard SQL syntax for queries.
84+
// See: https://cloud.google.com/bigquery/sql-reference/
85+
.setUseLegacySql(false)
86+
.build();
87+
88+
List<TimeSeries> timeSeriesList = new ArrayList<>();
89+
90+
DateTime queryStartTime = DateTime.now();
91+
92+
// Create a job ID so that we can safely retry.
93+
JobId jobId = JobId.of(UUID.randomUUID().toString());
94+
Job queryJob = bigquery.create(JobInfo.newBuilder(queryConfig).setJobId(jobId).build());
95+
96+
// Wait for the query to complete.
97+
queryJob = queryJob.waitFor();
98+
99+
// Check for errors
100+
if (queryJob == null) {
101+
throw new RuntimeException("Job no longer exists");
102+
} else if (queryJob.getStatus().getError() != null) {
103+
// You can also look at queryJob.getStatus().getExecutionErrors() for all
104+
// errors, not just the latest one.
105+
throw new RuntimeException(queryJob.getStatus().getError().toString());
106+
}
107+
// [END bigquery_logging_simple_app_query]
108+
109+
// [START bigquery_logging_simple_app_logmetrics]
110+
// Log the result metrics.
111+
TableResult result = queryJob.getQueryResults();
112+
113+
DateTime queryEndTime = DateTime.now();
114+
// Add query duration metric.
115+
Duration duration = new Duration(queryStartTime, queryEndTime);
116+
timeSeriesList.add(prepareMetric(QUERY_DURATION_METRIC, duration.getMillis()));
117+
118+
// Add rows returned metric.
119+
timeSeriesList.add(prepareMetric(ROWS_RETURNED_METRIC, result.getTotalRows()));
120+
121+
// [START bigquery_logging_simple_app_print]
122+
// Print all pages of the results.
123+
for (FieldValueList row : result.iterateAll()) {
124+
String url = row.get("url").getStringValue();
125+
long viewCount = row.get("view_count").getLongValue();
126+
System.out.printf("url: %s views: %d%n", url, viewCount);
127+
}
128+
// [END bigquery_logging_simple_app_print]
129+
130+
// Prepares the time series request
131+
CreateTimeSeriesRequest request = CreateTimeSeriesRequest.newBuilder()
132+
.setName(projectName)
133+
.addAllTimeSeries(timeSeriesList)
134+
.build();
135+
136+
createMetricsIfNeeded();
137+
client.createTimeSeries(request);
138+
System.out.println("Done writing metrics.");
139+
System.out.println("Shutting down MetricsServiceClient.");
140+
try {
141+
if (!client.awaitTermination(5, TimeUnit.SECONDS)) {
142+
client.shutdownNow();
143+
}
144+
} catch (InterruptedException e) {
145+
Thread.currentThread().interrupt();
146+
client.shutdownNow();
147+
}
148+
// [END bigquery_logging_simple_app_logmetrics]
149+
}
150+
151+
// Returns a metric time series with a single int64 data point.
152+
private TimeSeries prepareMetric(RequiredMetric requiredMetric, long metricValue) {
153+
TimeInterval interval = TimeInterval.newBuilder()
154+
.setEndTime(Timestamps.fromMillis(System.currentTimeMillis()))
155+
.build();
156+
TypedValue value = TypedValue
157+
.newBuilder()
158+
.setInt64Value(metricValue)
159+
.build();
160+
161+
Point point = Point.newBuilder()
162+
.setInterval(interval)
163+
.setValue(value)
164+
.build();
165+
166+
List<Point> pointList = Lists.newArrayList();
167+
pointList.add(point);
168+
169+
Metric metric = Metric.newBuilder()
170+
.setType(requiredMetric.getName())
171+
.build();
172+
173+
return TimeSeries.newBuilder()
174+
.setMetric(metric)
175+
.addAllPoints(pointList)
176+
.build();
177+
}
178+
179+
// [START bigquery_logging_list_and_create_metrics]
180+
private void createMetricsIfNeeded() {
181+
ListMetricDescriptorsRequest listMetricsRequest = ListMetricDescriptorsRequest
182+
.newBuilder()
183+
.setName(projectName)
184+
.setFilter("metric.type = starts_with(\"custom.googleapis.com/\")")
185+
.build();
186+
PagedResponseWrappers.ListMetricDescriptorsPagedResponse listMetricsResponse =
187+
client.listMetricDescriptors(listMetricsRequest);
188+
189+
Map<RequiredMetric, Boolean> existingMetrics = Maps.newHashMap(Maps.asMap(REQUIRED_METRICS,
190+
metric -> false));
191+
for (MetricDescriptor d : listMetricsResponse.iterateAll()) {
192+
RequiredMetric existingMetric = new RequiredMetric(
193+
d.getDisplayName(),
194+
d.getName(),
195+
d.getDescription(),
196+
d.getMetricKind(),
197+
d.getValueType());
198+
existingMetrics.put(existingMetric, true);
199+
}
200+
201+
existingMetrics.forEach((metric, exists) -> {
202+
if (!exists) {
203+
createMetric(metric);
204+
}
205+
});
206+
}
207+
// [END bigquery_logging_list_and_create_metrics]
208+
209+
// [START bigquery_logging_create_metric]
210+
private void createMetric(RequiredMetric newMetric) {
211+
MetricDescriptor descriptor = MetricDescriptor.newBuilder()
212+
.setType(newMetric.getName())
213+
.setName(newMetric.getName())
214+
.setDisplayName(newMetric.getShortName())
215+
.setDescription(newMetric.getDescription())
216+
.setMetricKind(newMetric.getMetricKind())
217+
.setValueType(newMetric.getValueType())
218+
.build();
219+
220+
CreateMetricDescriptorRequest request = CreateMetricDescriptorRequest.newBuilder()
221+
.setName(projectName)
222+
.setMetricDescriptor(descriptor)
223+
.build();
224+
225+
client.createMetricDescriptor(request);
226+
}
227+
// [END bigquery_logging_create_metric]
228+
}
229+
// [END bigquery_logging_simple_app_all]

0 commit comments

Comments
 (0)