Skip to content

Commit b1f7a89

Browse files
author
Eric Wiseblatt
authored
Merge pull request #46 from ewiseblatt/0_spectator
Added stackdriver writer.
2 parents 2397cd0 + 94cd0e3 commit b1f7a89

File tree

9 files changed

+2974
-1
lines changed

9 files changed

+2974
-1
lines changed
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2016 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+
dependencies {
18+
compile spinnaker.dependency('spectatorApi')
19+
20+
// googleMonitoring is not yet in spinnaker-dependencies
21+
// compile spinnaker.dependency('googleMonitoring')
22+
compile compile('com.google.apis:google-api-services-monitoring:v3-rev9-1.22.0')
23+
24+
testCompile 'org.mockito:mockito-core:2.+'
25+
testCompile 'junit:junit:4.+'
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
/*
2+
* Copyright 2016 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.netflix.spectator.stackdriver;
18+
19+
import com.netflix.spectator.api.Measurement;
20+
21+
import com.google.api.services.monitoring.v3.Monitoring;
22+
import com.google.api.services.monitoring.v3.MonitoringScopes;
23+
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
24+
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
25+
import com.google.api.client.http.HttpTransport;
26+
import com.google.api.client.json.JsonFactory;
27+
import com.google.api.client.json.jackson2.JacksonFactory;
28+
29+
import org.slf4j.Logger;
30+
import org.slf4j.LoggerFactory;
31+
32+
import java.io.FileInputStream;
33+
import java.io.IOException;
34+
35+
import java.util.function.Predicate;
36+
import java.util.Collections;
37+
import java.util.UUID;
38+
39+
40+
41+
/**
42+
* Factory to instantiate StackdriverWriter instance.
43+
*/
44+
public class ConfigParams {
45+
/**
46+
* Derived if not explicitly set.
47+
*/
48+
protected Monitoring monitoring;
49+
50+
/**
51+
* Required. The stackdriver project to store the data under.
52+
*/
53+
protected String projectName;
54+
55+
/**
56+
* Required.
57+
*/
58+
protected String customTypeNamespace;
59+
60+
/**
61+
* Required.
62+
*/
63+
protected String applicationName;
64+
65+
/**
66+
* Derived if not explicitly set.
67+
*/
68+
protected String instanceId;
69+
70+
/**
71+
* Optional.
72+
*/
73+
protected Predicate<Measurement> measurementFilter;
74+
75+
/**
76+
* Derived if not set.
77+
*/
78+
protected MetricDescriptorCache descriptorCache;
79+
80+
/**
81+
* Optional.
82+
*/
83+
protected long counterStartTime;
84+
85+
86+
/**
87+
* Optional.
88+
*/
89+
protected boolean uniqueMetricsPerApplication = true;
90+
91+
/**
92+
* Builds an instance of ConfigParams.
93+
*/
94+
public static class Builder extends ConfigParams {
95+
private String credentialsPath;
96+
97+
private String validateString(String value, String purpose) {
98+
if (value == null || value.isEmpty()) {
99+
throw new IllegalStateException(
100+
"The " + purpose + " has not been specified.");
101+
}
102+
return value;
103+
}
104+
105+
/**
106+
* Ensure the configuration parameters are complete and valid.
107+
*
108+
* If some required parameters are not yet explicitly set then this
109+
* may initialize them.
110+
*/
111+
public ConfigParams build() {
112+
ConfigParams result = new ConfigParams();
113+
114+
result.projectName
115+
= validateString(projectName, "stackdriver projectName");
116+
result.applicationName
117+
= validateString(applicationName, "applicationName");
118+
result.customTypeNamespace
119+
= validateString(customTypeNamespace,
120+
"stackdriver customTypeNamespace");
121+
122+
result.uniqueMetricsPerApplication = uniqueMetricsPerApplication;
123+
result.counterStartTime = counterStartTime;
124+
result.measurementFilter = measurementFilter;
125+
126+
result.instanceId = instanceId;
127+
result.monitoring = monitoring;
128+
result.descriptorCache = descriptorCache;
129+
130+
if (result.instanceId == null || result.instanceId.isEmpty()) {
131+
UUID uuid = UUID.randomUUID();
132+
byte[] uuidBytes = new byte[16];
133+
addLong(uuidBytes, 0, uuid.getLeastSignificantBits());
134+
addLong(uuidBytes, 8, uuid.getMostSignificantBits());
135+
result.instanceId
136+
= java.util.Base64.getEncoder().encodeToString(uuidBytes);
137+
}
138+
139+
if (result.monitoring == null) {
140+
try {
141+
HttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();
142+
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
143+
GoogleCredential credential = loadCredential(transport, jsonFactory,
144+
credentialsPath);
145+
result.monitoring
146+
= new Monitoring.Builder(transport, jsonFactory, credential)
147+
.setApplicationName("Spectator")
148+
.build();
149+
} catch (IOException | java.security.GeneralSecurityException e) {
150+
final Logger log = LoggerFactory.getLogger("StackdriverWriter");
151+
log.error("Caught exception initializing client: " + e);
152+
throw new IllegalStateException(e);
153+
}
154+
}
155+
156+
if (result.descriptorCache == null) {
157+
result.descriptorCache = new MetricDescriptorCache(result);
158+
}
159+
return result;
160+
}
161+
162+
/**
163+
* The Stackdriver namespace containing Custom Metric Descriptor types.
164+
*
165+
* This is intended to group a family of metric types together for a
166+
* particular system. (e.g. "spinnaker")
167+
*/
168+
public Builder setCustomTypeNamespace(String name) {
169+
customTypeNamespace = name;
170+
return this;
171+
}
172+
173+
/**
174+
* Sets the Google project name for Stackdriver to manage metrics under.
175+
*
176+
* This is a Google Cloud Platform project owned by the user deploying
177+
* the system using Spectator and used by Stackdriver to catalog our data
178+
* (e.g. "my-unique-project").
179+
*/
180+
public Builder setProjectName(String name) {
181+
projectName = name;
182+
return this;
183+
}
184+
185+
/**
186+
* The path to specific Google Credentials create the client stub with.
187+
*
188+
* This is not needed if injecting a specific Monitoring client stub.
189+
*/
190+
public Builder setCredentialsPath(String path) {
191+
credentialsPath = path;
192+
return this;
193+
}
194+
195+
/**
196+
* Sets the application name that we are associating these metrics with.
197+
*
198+
* This will be the APPLICATION_LABEL value for the metrics we store.
199+
* It distinguishes similar metrics within a system that are coming from
200+
* different services.
201+
* (e.g. "clouddriver")
202+
*/
203+
public Builder setApplicationName(String name) {
204+
applicationName = name;
205+
return this;
206+
}
207+
208+
/**
209+
* Sets the instance id that we are associating these metrics with.
210+
*
211+
* This will be the INSTANCE_LABEL value for the metrics we store.
212+
*/
213+
public Builder setInstanceId(String id) {
214+
instanceId = id;
215+
return this;
216+
}
217+
218+
/**
219+
* Sets the Spectator MeasurementFilter determining which metrics
220+
* to store in Stackdriver.
221+
*/
222+
public Builder setMeasurementFilter(Predicate<Measurement> filter) {
223+
measurementFilter = filter;
224+
return this;
225+
}
226+
227+
/**
228+
* Overrides the normal Stackdriver Monitoring client to use when
229+
* interacting with Stackdriver.
230+
*/
231+
public Builder setStackdriverStub(Monitoring stub) {
232+
monitoring = stub;
233+
return this;
234+
}
235+
236+
/**
237+
* Overrides the normal MetricDescriptorCache for the Stackdriver server.
238+
*/
239+
public Builder setDescriptorCache(MetricDescriptorCache cache) {
240+
descriptorCache = cache;
241+
return this;
242+
}
243+
244+
/**
245+
* Distinguish application using a distinct metric type, or a Time Series label.
246+
*/
247+
public Builder setUniqueMetricsPerApplication(boolean distinctTypes) {
248+
uniqueMetricsPerApplication = distinctTypes;
249+
return this;
250+
}
251+
252+
/**
253+
* Specifies the starting time interval for CUMULATIVE Stackdriver
254+
* metric descriptor types.
255+
*/
256+
public Builder setCounterStartTime(long millis) {
257+
counterStartTime = millis;
258+
return this;
259+
}
260+
261+
/**
262+
* Helper function to encode a value into buffer when constructing a UUID.
263+
*/
264+
private void addLong(byte[] buffer, int offset, long value) {
265+
for (int i = 0; i < 8; ++i) {
266+
buffer[i + offset] = (byte) (value >> (8 * i) & 0xff);
267+
}
268+
}
269+
270+
/**
271+
* Helper function for the validator that reads our credentials for
272+
* talking to Stackdriver.
273+
*/
274+
private static GoogleCredential loadCredential(
275+
HttpTransport transport, JsonFactory factory, String credentialsPath)
276+
throws IOException {
277+
final Logger log = LoggerFactory.getLogger("StackdriverWriter");
278+
279+
GoogleCredential credential;
280+
if (credentialsPath != null && !credentialsPath.isEmpty()) {
281+
FileInputStream stream = new FileInputStream(credentialsPath);
282+
try {
283+
credential = GoogleCredential.fromStream(stream, transport, factory)
284+
.createScoped(
285+
Collections.singleton(MonitoringScopes.MONITORING));
286+
log.info("Loaded credentials from from {}", credentialsPath);
287+
} finally {
288+
stream.close();
289+
}
290+
} else {
291+
log.info("spectator.stackdriver.monitoring.enabled without"
292+
+ " spectator.stackdriver.credentialsPath. "
293+
+ " Using default application credentials.");
294+
credential = GoogleCredential.getApplicationDefault();
295+
}
296+
return credential;
297+
}
298+
};
299+
300+
/**
301+
* The Stackdriver namespace containing Custom Metric Descriptor types.
302+
*/
303+
public String getCustomTypeNamespace() {
304+
return customTypeNamespace;
305+
}
306+
307+
/**
308+
* The Google project name for Stackdriver to manage metrics under.
309+
*/
310+
public String getProjectName() {
311+
return projectName;
312+
}
313+
314+
/**
315+
* The application name that we are associating these metrics with.
316+
*/
317+
public String getApplicationName() {
318+
return applicationName;
319+
}
320+
321+
/**
322+
* The instance id that we are associating these metrics with.
323+
*/
324+
public String getInstanceId() {
325+
return instanceId;
326+
}
327+
328+
/**
329+
* Determines which metrics to write into stackdriver.
330+
*/
331+
public Predicate<Measurement> getMeasurementFilter() {
332+
return measurementFilter;
333+
}
334+
335+
/**
336+
* The Stackdriver Monitoring client stub.
337+
*/
338+
public Monitoring getStackdriverStub() {
339+
return monitoring;
340+
}
341+
342+
/**
343+
* The MetricDescriptorCache.
344+
*/
345+
public MetricDescriptorCache getDescriptorCache() {
346+
return descriptorCache;
347+
}
348+
349+
/**
350+
* Whether metric type specifies the application, or Time Series data should.
351+
*/
352+
public boolean isMetricUniquePerApplication() {
353+
return uniqueMetricsPerApplication;
354+
}
355+
356+
/**
357+
* The TimeInterval start time for CUMULATIVE metric descriptor types.
358+
*/
359+
public long getCounterStartTime() {
360+
return counterStartTime;
361+
}
362+
};

0 commit comments

Comments
 (0)