mirror of
https://github.com/prometheus/prometheus.git
synced 2025-08-06 14:17:12 +02:00
* feature: type-and-unit-labels (extended MetricIdentity) Experimental implementation of https://github.com/prometheus/proposals/pull/39 Previous (unmerged) experiments: * https://github.com/prometheus/prometheus/compare/main...dashpole:prometheus:type_and_unit_labels * https://github.com/prometheus/prometheus/pull/16025 Signed-off-by: bwplotka <bwplotka@gmail.com> feature: type-and-unit-labels (extended MetricIdentity) Experimental implementation of https://github.com/prometheus/proposals/pull/39 Previous (unmerged) experiments: * https://github.com/prometheus/prometheus/compare/main...dashpole:prometheus:type_and_unit_labels * https://github.com/prometheus/prometheus/pull/16025 Signed-off-by: bwplotka <bwplotka@gmail.com> * Fix compilation errors Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com> Lint Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com> Revert change made to protobuf 'Accept' header Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com> Fix compilation errors for 'dedupelabels' tag Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com> * Rectored into schema.Metadata Signed-off-by: bwplotka <bwplotka@gmail.com> * texparse: Added tests for PromParse Signed-off-by: bwplotka <bwplotka@gmail.com> * add OM tests. Signed-off-by: bwplotka <bwplotka@gmail.com> * add proto tests Signed-off-by: bwplotka <bwplotka@gmail.com> * Addressed comments. Signed-off-by: bwplotka <bwplotka@gmail.com> * add schema label tests. Signed-off-by: bwplotka <bwplotka@gmail.com> * addressed comments. Signed-off-by: bwplotka <bwplotka@gmail.com> * fix tests. Signed-off-by: bwplotka <bwplotka@gmail.com> * add promql tests. Signed-off-by: bwplotka <bwplotka@gmail.com> * lint Signed-off-by: bwplotka <bwplotka@gmail.com> * Addressed comments. Signed-off-by: bwplotka <bwplotka@gmail.com> --------- Signed-off-by: bwplotka <bwplotka@gmail.com> Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com> Co-authored-by: Arthur Silva Sens <arthursens2005@gmail.com>
158 lines
6.2 KiB
Go
158 lines
6.2 KiB
Go
// Copyright 2025 The Prometheus Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package schema
|
|
|
|
import (
|
|
"github.com/prometheus/common/model"
|
|
|
|
"github.com/prometheus/prometheus/model/labels"
|
|
)
|
|
|
|
const (
|
|
// Special label names and selectors for schema.Metadata fields.
|
|
// They are currently private to ensure __name__, __type__ and __unit__ are used
|
|
// together and remain extensible in Prometheus. See NewMetadataFromLabels and Metadata
|
|
// methods for the interactions with the labels package structs.
|
|
metricName = "__name__"
|
|
metricType = "__type__"
|
|
metricUnit = "__unit__"
|
|
)
|
|
|
|
// IsMetadataLabel returns true if the given label name is a special
|
|
// schema Metadata label.
|
|
func IsMetadataLabel(name string) bool {
|
|
return name == metricName || name == metricType || name == metricUnit
|
|
}
|
|
|
|
// Metadata represents the core metric schema/metadata elements that:
|
|
// * are describing and identifying the metric schema/shape (e.g. name, type and unit).
|
|
// * are contributing to the general metric/series identity.
|
|
// * with the type-and-unit feature, are stored as Prometheus labels.
|
|
//
|
|
// Historically, similar information was encoded in the labels.MetricName (suffixes)
|
|
// and in the separate metadata.Metadata structures. However, with the
|
|
// type-and-unit-label feature (PROM-39), this information can be now stored directly
|
|
// in the special schema metadata labels, which offers better reliability (e.g. atomicity),
|
|
// compatibility and, in many cases, efficiency.
|
|
//
|
|
// NOTE: Metadata in the current form is generally similar (yet different) to:
|
|
// - The MetricFamily definition in OpenMetrics (https://prometheus.io/docs/specs/om/open_metrics_spec/#metricfamily).
|
|
// However, there is a small and important distinction around the metric name semantics
|
|
// for the "classic" representation of complex metrics like histograms. The
|
|
// Metadata.Name follows the __name__ semantics. See Name for details.
|
|
// - Original metadata.Metadata entries. However, not all fields in that metadata
|
|
// are "identifiable", notably the help field, plus metadata does not contain Name.
|
|
type Metadata struct {
|
|
// Name represents the final metric name for a Prometheus series.
|
|
// NOTE(bwplotka): Prometheus scrape formats (e.g. OpenMetrics) define
|
|
// the "metric family name". The Metadata.Name (so __name__ label) is not
|
|
// always the same as the MetricFamily.Name e.g.:
|
|
// * OpenMetrics metric family name on scrape: "acme_http_router_request_seconds"
|
|
// * Resulting Prometheus metric name: "acme_http_router_request_seconds_sum"
|
|
//
|
|
// Empty string means nameless metric (e.g. result of the PromQL function).
|
|
Name string
|
|
// Type represents the metric type. Empty value ("") is equivalent to
|
|
// model.UnknownMetricType.
|
|
Type model.MetricType
|
|
// Unit represents the metric unit. Empty string means an unitless metric (e.g.
|
|
// result of the PromQL function).
|
|
//
|
|
// NOTE: Currently unit value is not strictly defined other than OpenMetrics
|
|
// recommendations: https://prometheus.io/docs/specs/om/open_metrics_spec/#units-and-base-units
|
|
// TODO(bwplotka): Consider a stricter validation and rules e.g. lowercase only or UCUM standard.
|
|
// Read more in https://github.com/prometheus/proposals/blob/main/proposals/2024-09-25_metadata-labels.md#more-strict-unit-and-type-value-definition
|
|
Unit string
|
|
}
|
|
|
|
// NewMetadataFromLabels returns the schema metadata from the labels.
|
|
func NewMetadataFromLabels(ls labels.Labels) Metadata {
|
|
typ := model.MetricTypeUnknown
|
|
if got := ls.Get(metricType); got != "" {
|
|
typ = model.MetricType(got)
|
|
}
|
|
return Metadata{
|
|
Name: ls.Get(metricName),
|
|
Type: typ,
|
|
Unit: ls.Get(metricUnit),
|
|
}
|
|
}
|
|
|
|
// IsTypeEmpty returns true if the metric type is empty (not set).
|
|
func (m Metadata) IsTypeEmpty() bool {
|
|
return m.Type == "" || m.Type == model.MetricTypeUnknown
|
|
}
|
|
|
|
// IsEmptyFor returns true if the Metadata field, represented by the given labelName
|
|
// is empty (not set). If the labelName in not representing any Metadata field,
|
|
// IsEmptyFor returns true.
|
|
func (m Metadata) IsEmptyFor(labelName string) bool {
|
|
switch labelName {
|
|
case metricName:
|
|
return m.Name == ""
|
|
case metricType:
|
|
return m.IsTypeEmpty()
|
|
case metricUnit:
|
|
return m.Unit == ""
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
// AddToLabels adds metric schema metadata as labels into the labels.ScratchBuilder.
|
|
// Empty Metadata fields will be ignored (not added).
|
|
func (m Metadata) AddToLabels(b *labels.ScratchBuilder) {
|
|
if m.Name != "" {
|
|
b.Add(metricName, m.Name)
|
|
}
|
|
if !m.IsTypeEmpty() {
|
|
b.Add(metricType, string(m.Type))
|
|
}
|
|
if m.Unit != "" {
|
|
b.Add(metricUnit, m.Unit)
|
|
}
|
|
}
|
|
|
|
// SetToLabels injects metric schema metadata as labels into the labels.Builder.
|
|
// It follows the labels.Builder.Set semantics, so empty Metadata fields will
|
|
// remove the corresponding existing labels if they were previously set.
|
|
func (m Metadata) SetToLabels(b *labels.Builder) {
|
|
b.Set(metricName, m.Name)
|
|
if m.Type == model.MetricTypeUnknown {
|
|
// Unknown equals empty semantically, so remove the label on unknown too as per
|
|
// method signature comment.
|
|
b.Set(metricType, "")
|
|
} else {
|
|
b.Set(metricType, string(m.Type))
|
|
}
|
|
b.Set(metricUnit, m.Unit)
|
|
}
|
|
|
|
// IgnoreOverriddenMetadataLabelsScratchBuilder is a wrapper over labels scratch builder
|
|
// that ignores label additions that would collide with non-empty Overwrite Metadata fields.
|
|
type IgnoreOverriddenMetadataLabelsScratchBuilder struct {
|
|
*labels.ScratchBuilder
|
|
|
|
Overwrite Metadata
|
|
}
|
|
|
|
// Add a name/value pair, unless it would collide with the non-empty Overwrite Metadata
|
|
// field. Note if you Add the same name twice you will get a duplicate label, which is invalid.
|
|
func (b IgnoreOverriddenMetadataLabelsScratchBuilder) Add(name, value string) {
|
|
if !b.Overwrite.IsEmptyFor(name) {
|
|
return
|
|
}
|
|
b.ScratchBuilder.Add(name, value)
|
|
}
|