001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.http.prometheus; 019 020import com.google.errorprone.annotations.RestrictedApi; 021import java.io.IOException; 022import java.io.Writer; 023import java.util.Collection; 024import java.util.regex.Pattern; 025import javax.servlet.http.HttpServlet; 026import javax.servlet.http.HttpServletRequest; 027import javax.servlet.http.HttpServletResponse; 028import org.apache.commons.lang3.StringUtils; 029import org.apache.hadoop.metrics2.AbstractMetric; 030import org.apache.hadoop.metrics2.MetricType; 031import org.apache.hadoop.metrics2.MetricsRecord; 032import org.apache.hadoop.metrics2.MetricsTag; 033import org.apache.hadoop.metrics2.impl.MetricsExportHelper; 034import org.apache.yetus.audience.InterfaceAudience; 035 036@InterfaceAudience.Private 037public class PrometheusHadoopServlet extends HttpServlet { 038 private static final Pattern SPLIT_PATTERN = 039 Pattern.compile("(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=([A-Z][a-z]))|\\W|(_)+"); 040 041 @Override 042 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { 043 writeMetrics(resp.getWriter(), "true".equals(req.getParameter("description")), 044 req.getParameter("qry")); 045 } 046 047 static String toPrometheusName(String metricRecordName, String metricName) { 048 String baseName = metricRecordName + StringUtils.capitalize(metricName); 049 String[] parts = SPLIT_PATTERN.split(baseName); 050 return String.join("_", parts).toLowerCase(); 051 } 052 053 /* 054 * SimpleClient for Prometheus is not used, because the format is very easy to implement and this 055 * solution doesn't add any dependencies to the project. You can check the Prometheus format here: 056 * https://prometheus.io/docs/instrumenting/exposition_formats/ 057 */ 058 @RestrictedApi(explanation = "Should only be called in tests or self", link = "", 059 allowedOnPath = ".*/src/test/.*|.*/PrometheusHadoopServlet\\.java") 060 void writeMetrics(Writer writer, boolean descriptionEnabled, String queryParam) 061 throws IOException { 062 Collection<MetricsRecord> metricRecords = MetricsExportHelper.export(); 063 for (MetricsRecord metricsRecord : metricRecords) { 064 for (AbstractMetric metrics : metricsRecord.metrics()) { 065 if (metrics.type() == MetricType.COUNTER || metrics.type() == MetricType.GAUGE) { 066 067 String key = toPrometheusName(metricsRecord.name(), metrics.name()); 068 069 if (queryParam == null || key.contains(queryParam)) { 070 071 if (descriptionEnabled) { 072 String description = metrics.description(); 073 if (!description.isEmpty()) writer.append("# HELP ").append(description).append('\n'); 074 } 075 076 writer.append("# TYPE ").append(key).append(" ") 077 .append(metrics.type().toString().toLowerCase()).append('\n').append(key).append("{"); 078 079 /* add tags */ 080 String sep = ""; 081 for (MetricsTag tag : metricsRecord.tags()) { 082 String tagName = tag.name().toLowerCase(); 083 writer.append(sep).append(tagName).append("=\"").append(tag.value()).append("\""); 084 sep = ","; 085 } 086 writer.append("} "); 087 writer.append(metrics.value().toString()).append('\n'); 088 } 089 } 090 } 091 } 092 writer.flush(); 093 } 094}