• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

prometheus/client_java: Prometheus instrumentation library for JVM applications

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

prometheus/client_java

开源软件地址:

https://github.com/prometheus/client_java

开源编程语言:

Java 100.0%

开源软件介绍:

Prometheus JVM Client

It supports Java, Clojure, Scala, JRuby, and anything else that runs on the JVM.

Build Status

Table of Contents

Using

Assets

If you use Maven, you can simply reference the assets below. The latest version can be found on in the maven repository for io.prometheus.

<!-- The client -->
<dependency>
  <groupId>io.prometheus</groupId>
  <artifactId>simpleclient</artifactId>
  <version>0.16.0</version>
</dependency>
<!-- Hotspot JVM metrics-->
<dependency>
  <groupId>io.prometheus</groupId>
  <artifactId>simpleclient_hotspot</artifactId>
  <version>0.16.0</version>
</dependency>
<!-- Exposition HTTPServer-->
<dependency>
  <groupId>io.prometheus</groupId>
  <artifactId>simpleclient_httpserver</artifactId>
  <version>0.16.0</version>
</dependency>
<!-- Pushgateway exposition-->
<dependency>
  <groupId>io.prometheus</groupId>
  <artifactId>simpleclient_pushgateway</artifactId>
  <version>0.16.0</version>
</dependency>

Javadocs

There are canonical examples defined in the class definition Javadoc of the client packages.

Documentation can be found at the Java Client Github Project Page.

Disabling _created metrics

By default, counters, histograms, and summaries export an additional series suffixed with _created and a value of the unix timestamp for when the metric was created. If this information is not helpful, it can be disabled by setting the environment variable PROMETHEUS_DISABLE_CREATED_SERIES=true.

Instrumenting

Four types of metrics are offered: Counter, Gauge, Summary and Histogram. See the documentation on metric types and instrumentation best practices on how to use them.

Counter

Counters go up, and reset when the process restarts.

import io.prometheus.client.Counter;
class YourClass {
  static final Counter requests = Counter.build()
     .name("requests_total").help("Total requests.").register();

  void processRequest() {
    requests.inc();
    // Your code here.
  }
}

Gauge

Gauges can go up and down.

class YourClass {
  static final Gauge inprogressRequests = Gauge.build()
     .name("inprogress_requests").help("Inprogress requests.").register();

  void processRequest() {
    inprogressRequests.inc();
    // Your code here.
    inprogressRequests.dec();
  }
}

There are utilities for common use cases:

gauge.setToCurrentTime(); // Set to current unixtime.

As an advanced use case, a Gauge can also take its value from a callback by using the setChild() method. Keep in mind that the default inc(), dec() and set() methods on Gauge take care of thread safety, so when using this approach ensure the value you are reporting accounts for concurrency.

Summary

Summaries and Histograms can both be used to monitor distributions, like latencies or request sizes.

An overview of when to use Summaries and when to use Histograms can be found on https://prometheus.io/docs/practices/histograms.

The following example shows how to measure latencies and request sizes:

class YourClass {

  private static final Summary requestLatency = Summary.build()
      .name("requests_latency_seconds")
      .help("request latency in seconds")
      .register();

  private static final Summary receivedBytes = Summary.build()
      .name("requests_size_bytes")
      .help("request size in bytes")
      .register();

  public void processRequest(Request req) {
    Summary.Timer requestTimer = requestLatency.startTimer();
    try {
      // Your code here.
    } finally {
      requestTimer.observeDuration();
      receivedBytes.observe(req.size());
    }
  }
}

The Summary class provides different utility methods for observing values, like observe(double), startTimer(); timer.observeDuration(), time(Callable), etc.

By default, Summary metrics provide the count and the sum. For example, if you measure latencies of a REST service, the count will tell you how often the REST service was called, and the sum will tell you the total aggregated response time. You can calculate the average response time using a Prometheus query dividing sum / count.

In addition to count and sum, you can configure a Summary to provide quantiles:

Summary requestLatency = Summary.build()
    .name("requests_latency_seconds")
    .help("Request latency in seconds.")
    .quantile(0.5, 0.01)    // 0.5 quantile (median) with 0.01 allowed error
    .quantile(0.95, 0.005)  // 0.95 quantile with 0.005 allowed error
    // ...
    .register();

As an example, a 0.95 quantile of 120ms tells you that 95% of the calls were faster than 120ms, and 5% of the calls were slower than 120ms.

Tracking exact quantiles require a large amount of memory, because all observations need to be stored in a sorted list. Therefore, we allow an error to significantly reduce memory usage.

In the example, the allowed error of 0.005 means that you will not get the exact 0.95 quantile, but anything between the 0.945 quantile and the 0.955 quantile.

Experiments show that the Summary typically needs to keep less than 100 samples to provide that precision, even if you have hundreds of millions of observations.

There are a few special cases:

  • You can set an allowed error of 0, but then the Summary will keep all observations in memory.
  • You can track the minimum value with .quantile(0, 0). This special case will not use additional memory even though the allowed error is 0.
  • You can track the maximum value with .quantile(1, 0). This special case will not use additional memory even though the allowed error is 0.

Typically, you don't want to have a Summary representing the entire runtime of the application, but you want to look at a reasonable time interval. Summary metrics implement a configurable sliding time window:

Summary requestLatency = Summary.build()
    .name("requests_latency_seconds")
    .help("Request latency in seconds.")
    .maxAgeSeconds(10 * 60)
    .ageBuckets(5)
    // ...
    .register();

The default is a time window of 10 minutes and 5 age buckets, i.e. the time window is 10 minutes wide, and * we slide it forward every 2 minutes.

Histogram

Like Summaries, Histograms can be used to monitor latencies (or other things like request sizes).

An overview of when to use Summaries and when to use Histograms can be found on https://prometheus.io/docs/practices/histograms.

class YourClass {
  static final Histogram requestLatency = Histogram.build()
     .name("requests_latency_seconds").help("Request latency in seconds.").register();

  void processRequest(Request req) {
    Histogram.Timer requestTimer = requestLatency.startTimer();
    try {
      // Your code here.
    } finally {
      requestTimer.observeDuration();
    }
  }
}

The default buckets are intended to cover a typical web/rpc request from milliseconds to seconds. They can be overridden with the buckets() method on the Histogram.Builder.

There are utilities for timing code:

class YourClass {
  static final Histogram requestLatency = Histogram.build()
     .name("requests_latency_seconds").help("Request latency in seconds.").register();

  void processRequest(Request req) {
    requestLatency.time(new Runnable() {
      public abstract void run() {
        // Your code here.
      }
    });


    // Or the Java 8 lambda equivalent
    requestLatency.time(() -> {
      // Your code here.
    });
  }
}

Labels

All metrics can have labels, allowing grouping of related time series.

See the best practices on naming and labels.

Taking a counter as an example:

class YourClass {
  static final Counter requests = Counter.build()
     .name("my_library_requests_total").help("Total requests.")
     .labelNames("method").register();

  void processGetRequest() {
    requests.labels("get").inc();
    // Your code here.
  }
}

Registering Metrics

The best way to register a metric is via a static final class variable as is common with loggers.

static final Counter requests = Counter.build()
   .name("my_library_requests_total").help("Total requests.").labelNames("path").register();

Using the default registry with variables that are static is ideal since registering a metric with the same name is not allowed and the default registry is also itself static. You can think of registering a metric, more like registering a definition (as in the TYPE and HELP sections). The metric 'definition' internally holds the samples that are reported and pulled out by Prometheus. Here is an example of registering a metric that has no labels.

class YourClass {
  static final Gauge activeTransactions = Gauge.build()
     .name("my_library_transactions_active")
     .help("Active transactions.")
     .register();

  void processThatCalculates(String key) {
    activeTransactions.inc();
    try {
        // Perform work.
    } finally{
        activeTransactions.dec();
    }
  }
}

To create timeseries with labels, include labelNames() with the builder. The labels() method looks up or creates the corresponding labelled timeseries. You might also consider storing the labelled timeseries as an instance variable if it is appropriate. It is thread safe and can be used multiple times, which can help performance.

class YourClass {
  static final Counter calculationsCounter = Counter.build()
     .name("my_library_calculations_total").help("Total calls.")
     .labelNames("key").register();

  void processThatCalculates(String key) {
    calculationsCounter.labels(key).inc();
    // Run calculations.
  }
}

Exemplars

Exemplars are a feature of the OpenMetrics format that allows applications to link metrics to example traces. In order to see exemplars, you need to set the Accept header for the OpenMetrics format like this:

curl -H 'Accept: application/openmetrics-text; version=1.0.0; charset=utf-8' http://localhost:8080/metrics

Exemplars are supported since client_java version 0.11.0. Exemplars are supported for Counter and Histogram metrics.

Global Exemplar Samplers

An ExemplarSampler is used implicitly under the hood to add exemplars to your metrics. The idea is that you get exemplars without changing your code.

The DefaultExemplarSampler comes with built-in support for OpenTelemetry tracing, see built-in support for tracing systems below.

You can disable the default exemplar samplers globally with:

ExemplarConfig.disableExemplars();

You can set your own custom implementation of ExemplarSampler as a global default like this:

ExemplarSampler myExemplarSampler = new MyExemplarSampler();
ExemplarConfig.setCounterExemplarSampler(myExemplarSampler);
ExemplarConfig.setHistogramExemplarSampler(myExemplarSampler);

Per Metric Exemplar Samplers

The metric builders for Counter and Histogram have methods for setting the exemplar sampler for that individual metric. This takes precedence over the global setting in ExemplarConfig.

The following calls enable the default exemplar sampler for individual metrics. This is useful if you disabled the exemplar sampler globally with ExemplarConfig.disableExemplars().

Counter myCounter = Counter.build()
    .name("number_of_events_total")
    .help("help")
    .withExemplars()
    ...
    .register();
Histogram myHistogram = Histogram.build()
    .name("my_latency")
    .help("help")
    .withExemplars()
    ...
    .register();

The following calls disable exemplars for individual metrics:

Counter myCounter = Counter.build()
    .name("number_of_events_total")
    .help("help")
    .withoutExemplars()
    ...
    .register();
Histogram myHistogram = Histogram.build()
    .name("my_latency")
    .help("help")
    .withoutExemplars()
    ...
    .register();

The following calls enable exemplars and set a custom MyExemplarSampler for individual metrics:

// CounterExemplarSampler
Counter myCounter = Counter.build()
    .name("number_of_events_total")
    .help("help")
    .withExemplarSampler(new MyExemplarSampler())
    ...
    .register();
// HistogramExemplarSampler
Histogram myHistogram = Histogram.build()
    .name("my_latency")
    .help("help")
    .withExemplarSampler(new MyExemplarSampler())
    ...
    .register();

Per Observation Exemplars

You can explicitly provide an exemplar for an individual observation. This takes precedence over the exemplar sampler configured with the metric.

The following call will increment a counter, and create an exemplar with the specified span_id and trace_id labels:

myCounter.incWithExemplar("span_id", "abcdef", "trace_id", "123456");

The following call will observe a value of 0.12 in a histogram, and create an exemplar with the specified span_id and trace_id labels:

myHistogram.observeWithExemplar(0.12, "span_id", "abcdef", "trace_id", "123456");

All methods for observing and incrementing values have ...withExemplar equivalents. There are versions taking the exemplar labels as a String... as shown in the example, as well as versions taking the exemplar labels as a Map<String, String>.

Built-in Support for Tracing Systems

The DefaultExemplarSampler detects if a tracing library is found on startup, and provides exemplars for that tracing library by default. Currently, only OpenTelemetry tracing is supported. If you are a tracing vendor, feel free to open a PR and add support for your tracing library.

Documentation of the individual tracer integrations:

Included Collectors

The Java client includes collectors for garbage collection, memory pools, classloading, and thread counts. These can be added individually or just use the DefaultExports to conveniently register them.

DefaultExports.initialize();

Logging

There are logging collectors for log4j, log4j2 and logback.

To register the Logback collector can be added to the root level like so:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml"/>

    <appender name="METRICS" class="io.prometheus.client.logback.InstrumentedAppender" />

    <root level="INFO">
        <appender-ref ref="METRICS" />
    </root>

</configuration>

To register the log4j collector at root level:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="METRICS" class="io.prometheus.client.log4j.InstrumentedAppender"/>
    <root>
        <priority value ="info" />
        <appender-ref ref="METRICS" />
    </root>
</log4j:configuration>

To register the log4j2 collector at root level:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration packages="io.prometheus.client.log4j2">
    <Appenders>
        <Prometheus name="METRICS"/>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="METRICS"/>
        </Root>
    </Loggers>
</Configuration>

See ./integration_tests/it_log4j2/ for a log4j2 example.

Caches

To register the Guava cache collector, be certain to add recordStats() when building the cache and adding it to the registered collector.

CacheMetricsCollector cacheMetrics = new CacheMetricsCollector().register();

Cache<String, String> cache = CacheBuilder.newBuilder().recordStats().build();
cacheMetrics.addCache("myCacheLabel", cache);

The Caffeine equivalent is nearly identical. Again, be certain to call recordStats() when building the cache so that metrics are collected.

CacheMetricsCollector cacheMetrics = new CacheMetricsCollector().register();

Cache<String, String> cache = Caffeine.newBuilder().recordStats().build();
cacheMetrics.addCache("myCacheLabel", cache);

Hibernate

There is a collector for Hibernate which allows to collect metrics from one or more SessionFactory instances.

If you want to collect metrics from a single SessionFactory, you can register the collector like this:

new HibernateStatisticsCollector(sessionFactory, "myapp").register();

In some situations you may want to collect metrics from multiple factories. In this case just call add() on the collector for each of them.

new HibernateStatisticsCollector()
    .add(sessionFactory1, "myapp1")
    .add(sessionFactory2, "myapp2")
    .register();

If you are using Hibernate in a JPA environment and only have access to the EntityManager or EntityManagerFactory, you can use this code to access the underlying SessionFactory:

SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);

respectively

SessionFactory sessionFactory = entityManager.unwrap(Session.class).getSessionFactory();

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap