001package io.prometheus.metrics.model.snapshots; 002 003/** Immutable container for metric metadata: name, help, unit. */ 004public final class MetricMetadata { 005 006 /** 007 * Name without suffix. 008 * 009 * <p>For example, the name for a counter "http_requests_total" is "http_requests". The name of an 010 * info called "jvm_info" is "jvm". 011 * 012 * <p>We allow dots in label names. Dots are automatically replaced with underscores in Prometheus 013 * exposition formats. However, if metrics from this library are exposed in OpenTelemetry format 014 * dots are retained. 015 * 016 * <p>See {@link #MetricMetadata(String, String, Unit)} for more info on naming conventions. 017 */ 018 private final String name; 019 020 /** 021 * Same as name, except if name contains dots, then the prometheusName is {@code name.replace(".", 022 * "_")}. 023 */ 024 private final String prometheusName; 025 026 /** optional, may be {@code null}. */ 027 private final String help; 028 029 /** optional, may be {@code null}. */ 030 private final Unit unit; 031 032 /** See {@link #MetricMetadata(String, String, Unit)} */ 033 public MetricMetadata(String name) { 034 this(name, null, null); 035 } 036 037 /** See {@link #MetricMetadata(String, String, Unit)} */ 038 public MetricMetadata(String name, String help) { 039 this(name, help, null); 040 } 041 042 /** 043 * Constructor. 044 * 045 * @param name must not be {@code null}. {@link PrometheusNaming#isValidMetricName(String) 046 * isValidMetricName(name)} must be {@code true}. Use {@link 047 * PrometheusNaming#sanitizeMetricName(String)} to convert arbitrary strings into valid names. 048 * @param help optional. May be {@code null}. 049 * @param unit optional. May be {@code null}. 050 */ 051 public MetricMetadata(String name, String help, Unit unit) { 052 this.name = name; 053 this.help = help; 054 this.unit = unit; 055 validate(); 056 this.prometheusName = name.contains(".") ? PrometheusNaming.prometheusName(name) : name; 057 } 058 059 /** 060 * The name does not include the {@code _total} suffix for counter metrics or the {@code _info} 061 * suffix for Info metrics. 062 * 063 * <p>The name may contain dots. Use {@link #getPrometheusName()} to get the name in Prometheus 064 * format, i.e. with dots replaced by underscores. 065 */ 066 public String getName() { 067 return name; 068 } 069 070 /** 071 * Same as {@link #getName()} but with dots replaced by underscores. 072 * 073 * <p>This is used by Prometheus exposition formats. 074 */ 075 public String getPrometheusName() { 076 return prometheusName; 077 } 078 079 public String getHelp() { 080 return help; 081 } 082 083 public boolean hasUnit() { 084 return unit != null; 085 } 086 087 public Unit getUnit() { 088 return unit; 089 } 090 091 private void validate() { 092 if (name == null) { 093 throw new IllegalArgumentException("Missing required field: name is null"); 094 } 095 String error = PrometheusNaming.validateMetricName(name); 096 if (error != null) { 097 throw new IllegalArgumentException( 098 "'" 099 + name 100 + "': Illegal metric name. " 101 + error 102 + " Call " 103 + PrometheusNaming.class.getSimpleName() 104 + ".sanitizeMetricName(name) to avoid this error."); 105 } 106 if (hasUnit()) { 107 if (!name.endsWith("_" + unit) && !name.endsWith("." + unit)) { 108 throw new IllegalArgumentException( 109 "'" 110 + name 111 + "': Illegal metric name. If the unit is non-null, " 112 + "the name must end with the unit: _" 113 + unit 114 + "." 115 + " Call " 116 + PrometheusNaming.class.getSimpleName() 117 + ".sanitizeMetricName(name, unit) to avoid this error."); 118 } 119 } 120 } 121}