/**
* Copyright 2015-2016 The OpenZipkin Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package zipkin.autoconfigure.metrics;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.PublicMetrics;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collection;
import java.util.regex.Pattern;
@RestController
@RequestMapping("/prometheus")
public class PrometheusMetricsAutoConfiguration {
private static final Pattern SANITIZE_PREFIX_PATTERN = Pattern.compile("^[^a-zA-Z_]");
private static final Pattern SANITIZE_BODY_PATTERN = Pattern.compile("[^a-zA-Z0-9_]");
private final Collection<PublicMetrics> publicMetrics;
@Autowired
public PrometheusMetricsAutoConfiguration(Collection<PublicMetrics> publicMetrics) {
this.publicMetrics = publicMetrics;
}
private static String sanitizeMetricName(String metricName) {
return SANITIZE_BODY_PATTERN.matcher(
SANITIZE_PREFIX_PATTERN.matcher(metricName).replaceFirst("_")
).replaceAll("_");
}
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<String> prometheusMetrics() {
StringBuilder sb = new StringBuilder();
for (PublicMetrics publicMetrics : this.publicMetrics) {
for (Metric<?> metric : publicMetrics.metrics()) {
final String sanitizedName = sanitizeMetricName(metric.getName());
final String type = typeForName(sanitizedName);
final String metricName = metricName(sanitizedName, type);
double value = metric.getValue().doubleValue();
sb.append(String.format("#TYPE %s %s\n", metricName, type));
sb.append(String.format("#HELP %s %s\n", metricName, metricName));
sb.append(String.format("%s %s\n", metricName, prometheusDouble(value)));
}
}
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("text/plain; version=0.0.4; charset=utf-8"))
.body(sb.toString());
}
private String prometheusDouble(double value) {
if (value == Double.POSITIVE_INFINITY) {
return "+Inf";
} else if (value == Double.NEGATIVE_INFINITY) {
return "-Inf";
} else {
return String.valueOf(value);
}
}
private String metricName(String name, String type) {
switch (type) {
case "counter":
return name.replaceFirst("^counter_", "");
case "gauge":
return name.replaceFirst("^gauge_", "");
default:
return name;
}
}
private String typeForName(String name) {
if (name.startsWith("gauge")) {
return "gauge";
}
if (name.startsWith("counter")) {
return "counter";
}
return "gauge";
}
}