源码分析一TextFormat
- TextFormat根据http请求头决定上报prometheus的数据报内容
- “text/plain; version=0.0.4; charset=utf-8” 请求头时不上报采样点信息
- “text/plain; version=0.0.4; charset=utf-8” 请求头时上报采样点信息exemplar
public class TextFormat {
public final static String CONTENT_TYPE_004 = "text/plain; version=0.0.4; charset=utf-8";
public final static String CONTENT_TYPE_OPENMETRICS_100 = "application/openmetrics-text; version=1.0.0; charset=utf-8";
根据http请求头决定输出格式是否包含exemplar
public static String chooseContentType(String acceptHeader) {
if (acceptHeader == null) {
return CONTENT_TYPE_004;
}
for (String accepts : acceptHeader.split(",")) {
if ("application/openmetrics-text".equals(accepts.split(";")[0].trim())) {
return CONTENT_TYPE_OPENMETRICS_100;
}
}
return CONTENT_TYPE_OPENMETRICS_100;
}
public static void writeFormat(String contentType, Writer writer, Enumeration<Collector.MetricFamilySamples> mfs) throws IOException {
0.0.4版本不会给prometheus等数据源提供exemplar信息
if (CONTENT_TYPE_004.equals(contentType)) {
write004(writer, mfs);
return;
}
1.0.0版本会给prometheus等数据源提供exemplar信息
if (CONTENT_TYPE_OPENMETRICS_100.equals(contentType)) {
writeOpenMetrics100(writer, mfs);
return;
}
throw new IllegalArgumentException("Unknown contentType " + contentType);
}
public static void write004(Writer writer, Enumeration<Collector.MetricFamilySamples> mfs) throws IOException {
Map<String, Collector.MetricFamilySamples> omFamilies = new TreeMap<String, Collector.MetricFamilySamples>();
while(mfs.hasMoreElements()) {
Collector.MetricFamilySamples metricFamilySamples = mfs.nextElement();
String name = metricFamilySamples.name;
段文式协议 help帮助信息
writer.write("# HELP ");
writer.write(name);
if (metricFamilySamples.type == Collector.Type.COUNTER) {
writer.write("_total");
}
if (metricFamilySamples.type == Collector.Type.INFO) {
writer.write("_info");
}
writer.write(' ');
writeEscapedHelp(writer, metricFamilySamples.help);
writer.write('\n');
段文式协议 metrics类型 Counter summary等
writer.write("# TYPE ");
writer.write(name);
if (metricFamilySamples.type == Collector.Type.COUNTER) {
writer.write("_total");
}
if (metricFamilySamples.type == Collector.Type.INFO) {
writer.write("_info");
}
writer.write(' ');
writer.write(typeString(metricFamilySamples.type));
writer.write('\n');
String createdName = name + "_created";
String gcountName = name + "_gcount";
String gsumName = name + "_gsum";
获取sample 进行输出
for (Collector.MetricFamilySamples.Sample sample: metricFamilySamples.samples) {
if (sample.name.equals(createdName)
|| sample.name.equals(gcountName)
|| sample.name.equals(gsumName)) {
Collector.MetricFamilySamples omFamily = omFamilies.get(sample.name);
if (omFamily == null) {
omFamily = new Collector.MetricFamilySamples(sample.name, Collector.Type.GAUGE, metricFamilySamples.help, new ArrayList<Collector.MetricFamilySamples.Sample>());
omFamilies.put(sample.name, omFamily);
}
omFamily.samples.add(sample);
continue;
}
writer.write(sample.name);
段文式协议 label键值对
if (sample.labelNames.size() > 0) {
writer.write('{');
for (int i = 0; i < sample.labelNames.size(); ++i) {
writer.write(sample.labelNames.get(i));
writer.write("=\"");
writeEscapedLabelValue(writer, sample.labelValues.get(i));
writer.write("\",");
}
writer.write('}');
}
writer.write(' ');
writer.write(Collector.doubleToGoString(sample.value));
if (sample.timestampMs != null){
writer.write(' ');
writer.write(sample.timestampMs.toString());
}
writer.write('\n');
}
}
}
public static void writeOpenMetrics100(Writer writer, Enumeration<Collector.MetricFamilySamples> mfs) throws IOException {
while(mfs.hasMoreElements()) {
Collector.MetricFamilySamples metricFamilySamples = mfs.nextElement();
String name = metricFamilySamples.name;
书写类型
writer.write("# TYPE ");
writer.write(name);
writer.write(' ');
writer.write(omTypeString(metricFamilySamples.type));
writer.write('\n');
单位 counter 和 gauge一般没有
if (!metricFamilySamples.unit.isEmpty()) {
writer.write("# UNIT ");
writer.write(name);
writer.write(' ');
writer.write(metricFamilySamples.unit);
writer.write('\n');
}
writer.write("# HELP ");
writer.write(name);
writer.write(' ');
writeEscapedLabelValue(writer, metricFamilySamples.help);
writer.write('\n');
for (Collector.MetricFamilySamples.Sample sample: metricFamilySamples.samples) {
writer.write(sample.name);
if (sample.labelNames.size() > 0) {
writer.write('{');
for (int i = 0; i < sample.labelNames.size(); ++i) {
if (i > 0) {
writer.write(",");
}
writer.write(sample.labelNames.get(i));
writer.write("=\"");
writeEscapedLabelValue(writer, sample.labelValues.get(i));
writer.write("\"");
}
writer.write('}');
}
writer.write(' ');
writer.write(Collector.doubleToGoString(sample.value));
if (sample.timestampMs != null){
writer.write(' ');
omWriteTimestamp(writer, sample.timestampMs);
}
书写exemplar 信息
例如: counter_total{label1=“val1”} 888.0 # {exemplar_key=“exemplar_val1”,exemplar_key2=“exemplar_val2”} 222.0 1663441608.646
if (sample.exemplar != null) {
writer.write(" # {");
for (int i=0; i<sample.exemplar.getNumberOfLabels(); i++) {
if (i > 0) {
writer.write(",");
}
writer.write(sample.exemplar.getLabelName(i));
writer.write("=\"");
writeEscapedLabelValue(writer, sample.exemplar.getLabelValue(i));
writer.write("\"");
}
writer.write("} ");
writer.write(Collector.doubleToGoString(sample.exemplar.getValue()));
if (sample.exemplar.getTimestampMs() != null) {
writer.write(' ');
omWriteTimestamp(writer, sample.exemplar.getTimestampMs());
}
}
writer.write('\n');
}
}
writer.write("# EOF\n");
}
}
总结
- TextFormat 在执行writeOpenMetrics100时,需要grafana7.4以上版本支持
- 至此,prometheus类框架设计主体介绍完毕,但prometheus在java领域并没有支持许多常见的中间件,下文我们引入micrometer,介绍micrometer如何集成prometheus以及采集Java创建中间件的指标