Prometheus client提供了指标采集上报Exemplar的能力,用法:
注册
histogram = Histogram.builder()
.name("request_duration_seconds")
.help("request duration in seconds")
.unit(Unit.SECONDS)
.labelNames("http_status").withExemplars()
.register();
打点
histogram.labelValues("200").observeWithExemplar(nanosToSeconds(System.nanoTime() - start),
Labels.of("abc","aa"));
如果用了opentelemetry agent,则会自动添加trace_id和span_id
如果没用,就需要自行添加了
prometheus java client工程里自带了一个demo,在example-exemplars-tail-sampling目录里
项目里有个docker-compose.yaml文件,定义了一个完整的工程,
image.png
demo工程的部署:
1、先编译出jar包
2、cd example-exemplars-tail-sampling
docker-compose up
实际操作的时候发现两个java的容器起不来,报错找不到jar包
懒得去找原因了,写了2个dockerfile,手动执行docker run启动两个java容器
FROM openjdk:11-jre
COPY example-hello-world-app.jar /example-hello-world-app.jar
COPY opentelemetry-javaagent.jar /opentelemetry-javaagent.jar
ENV OTEL_TRACES_EXPORTER=otlp
ENV OTEL_METRICS_EXPORTER=none
ENV OTEL_LOGS_EXPORTER=none
ENTRYPOINT ["java","-javaagent:opentelemetry-javaagent.jar", "-jar","/example-hello-world-app.jar"]
然后把docker-compose.yaml里java容器部分删掉,启动剩余的容器
起来后的界面
image.png
看到exemplar里有个trace_id还有traceid,其中后者是我手动加的,前者是框架自动带的
点击按钮可以调到调用链
源码分析
trace_id的注入:ExemplarSampler类的updateCustomExemplar
if (!labels.contains(Exemplar.TRACE_ID) && !labels.contains(Exemplar.SPAN_ID)) {
labels = labels.merge(doSampleExemplar());
}
如果exemplar中没有trace_id就会尝试自动获取
try {
SpanContext spanContext = SpanContextSupplier.getSpanContext();
if (spanContext != null) {
if (spanContext.isCurrentSpanSampled()) { //只有采样的才上报样本
String spanId = spanContext.getCurrentSpanId();
String traceId = spanContext.getCurrentTraceId();
if (spanId != null && traceId != null) {
spanContext.markCurrentSpanAsExemplar();
return Labels.of(Exemplar.TRACE_ID, traceId, Exemplar.SPAN_ID, spanId);
}
}
}
这里是从SpanContextSupplier获取context,目前只支持opentelemetry的
try {
if (OpenTelemetrySpanContext.isAvailable()) {
spanContextRef.set(new OpenTelemetrySpanContext());
}
} catch (NoClassDefFoundError ignored) {
// tracer_otel dependency not found
} catch (UnsupportedClassVersionError ignored) {
// OpenTelemetry requires Java 8, but client_java might run on Java 6.
}
try {
if (OpenTelemetryAgentSpanContext.isAvailable()) {
spanContextRef.set(new OpenTelemetryAgentSpanContext());
}
写Exemplar的
/metrics接口的handler:PrometheusScrapeHandler
先调PrometheusRegistry的scrape接口获取数据,
遍历collector获取数据
for (Collector collector : collectors) {
MetricSnapshot snapshot = scrapeRequest == null ? collector.collect() : collector.collect(scrapeRequest);
if (snapshot != null) {
if (result.containsMetricName(snapshot.getMetadata().getName())) {
throw new IllegalStateException(snapshot.getMetadata().getPrometheusName() + ": duplicate metric name.");
}
result.metricSnapshot(snapshot);
}
}
再调OpenMetricsTextFormatWriter类的writeScrapeTimestampAndExemplar写返回报文














网友评论