All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.prometheus.client.SimpleCollector Maven / Gradle / Ivy

package io.prometheus.client;

import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.Arrays;
import java.util.List;

/**
 * Common functionality for {@link Gauge}, {@link Counter}, {@link Summary} and {@link Histogram}.
 * 

* This class handles common initialization and label logic for the standard metrics. * You should never subclass this class. *

*

Initialization

* After calling build() on a subclass, {@link Builder#name(String) name}, * {@link SimpleCollector.Builder#help(String) help}, * {@link SimpleCollector.Builder#labelNames(String...) labelNames}, * {@link SimpleCollector.Builder#namespace(String) namespace} and * {@link SimpleCollector.Builder#subsystem(String) subsystem} can be called to configure the Collector. * These return this to allow calls to be chained. * Once configured, call {@link SimpleCollector.Builder#create create} * (which is also called by {@link SimpleCollector.Builder#register register}). *

* The fullname of the metric is namespace_subsystem_name, but only name is required. * *

Labels

* {@link SimpleCollector.Builder#labelNames labelNames} specifies which (if any) labels the metrics will have, and * {@link #labels} returns the Child of the metric that represents that particular set of labels. * {@link Gauge}, {@link Counter} and {@link Summary} all offer convenience methods to avoid needing to call * {@link #labels} for metrics with no labels. *

* {@link #remove} and {@link #clear} can be used to remove children. *

* Warning #1: Metrics that don't always export something are difficult to monitor, if you know in advance * what labels will be in use you should initialise them be calling {@link #labels}. * This is done for you for metrics with no labels. *

* Warning #2: While labels are very powerful, avoid overly granular metric labels. * The combinatorial explosion of breaking out a metric in many dimensions can produce huge numbers * of timeseries, which will then take longer and more resources to process. *

* As a rule of thumb aim to keep the cardinality of metrics below ten, and limit where the * cardinality exceeds that value. For example rather than breaking out latency * by customer and endpoint in one metric, you might have two metrics with one breaking out * by each. If the cardinality is in the hundreds, you may wish to consider removing the breakout * by one of the dimensions altogether. */ public abstract class SimpleCollector extends Collector { protected final String fullname; protected final String help; protected final List labelNames; protected final ConcurrentMap, Child> children = new ConcurrentHashMap, Child>(); protected Child noLabelsChild; /** * Return the Child with the given labels, creating it if needed. *

* Must be passed the same number of labels are were passed to {@link #labelNames}. */ public Child labels(String... labelValues) { if (labelValues.length != labelNames.size()) { throw new IllegalArgumentException("Incorrect number of labels."); } for (String label: labelValues) { if (label == null) { throw new IllegalArgumentException("Label cannot be null."); } } List key = Arrays.asList(labelValues); Child c = children.get(key); if (c != null) { return c; } Child c2 = newChild(); Child tmp = children.putIfAbsent(key, c2); return tmp == null ? c2 : tmp; } /** * Remove the Child with the given labels. *

* Any references to the Child are invalidated. */ public void remove(String... labelValues) { children.remove(Arrays.asList(labelValues)); initializeNoLabelsChild(); } /** * Remove all children. *

* Any references to any children are invalidated. */ public void clear() { children.clear(); initializeNoLabelsChild(); } /** * Initialize the child with no labels. */ protected void initializeNoLabelsChild() { // Initialize metric if it has no labels. if (labelNames.size() == 0) { noLabelsChild = labels(); } } /** * Replace the Child with the given labels. *

* This is intended for advanced uses, in particular proxying metrics * from another monitoring system. This allows for callbacks for returning * values for {@link Counter} and {@link Gauge} without having to implement * a full {@link Collector}. *

* An example with {@link Gauge}: *

   * {@code
   *   Gauge.build().name("current_time").help("Current unixtime.").create()
   *       .setChild(new Gauge.Child() {
   *         public double get() {
   *           return System.currentTimeMillis() / MILLISECONDS_PER_SECOND;
   *         }
   *       }).register();
   * }
   * 
*

* Any references any previous Child with these labelValues are invalidated. * A metric should be either all callbacks, or none. */ public T setChild(Child child, String... labelValues) { if (labelValues.length != labelNames.size()) { throw new IllegalArgumentException("Incorrect number of labels."); } children.put(Arrays.asList(labelValues), child); return (T)this; } /** * Return a new child, workaround for Java generics limitations. */ protected abstract Child newChild(); protected List familySamplesList(Collector.Type type, List samples) { MetricFamilySamples mfs = new MetricFamilySamples(fullname, type, help, samples); List mfsList = new ArrayList(1); mfsList.add(mfs); return mfsList; } protected SimpleCollector(Builder b) { if (b.name.isEmpty()) throw new IllegalStateException("Name hasn't been set."); String name = b.name; if (!b.subsystem.isEmpty()) { name = b.subsystem + '_' + name; } if (!b.namespace.isEmpty()) { name = b.namespace + '_' + name; } fullname = name; checkMetricName(fullname); if (b.help.isEmpty()) throw new IllegalStateException("Help hasn't been set."); help = b.help; labelNames = Arrays.asList(b.labelNames); for (String n: labelNames) { checkMetricLabelName(n); } if (!b.dontInitializeNoLabelsChild) { initializeNoLabelsChild(); } } /** * Builders let you configure and then create collectors. */ public abstract static class Builder, C extends SimpleCollector> { String namespace = ""; String subsystem = ""; String name = ""; String fullname = ""; String help = ""; String[] labelNames = new String[]{}; // Some metrics require additional setup before the initialization can be done. boolean dontInitializeNoLabelsChild; /** * Set the name of the metric. Required. */ public B name(String name) { this.name = name; return (B)this; } /** * Set the subsystem of the metric. Optional. */ public B subsystem(String subsystem) { this.subsystem = subsystem; return (B)this; } /** * Set the namespace of the metric. Optional. */ public B namespace(String namespace) { this.namespace = namespace; return (B)this; } /** * Set the help string of the metric. Required. */ public B help(String help) { this.help = help; return (B)this; } /** * Set the labelNames of the metric. Optional, defaults to no labels. */ public B labelNames(String... labelNames) { this.labelNames = labelNames; return (B)this; } /** * Return the constructed collector. *

* Abstract due to generics limitations. */ public abstract C create(); /** * Create and register the Collector with the default registry. */ public C register() { return register(CollectorRegistry.defaultRegistry); } /** * Create and register the Collector with the given registry. */ public C register(CollectorRegistry registry) { C sc = create(); registry.register(sc); return sc; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy