Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(encoding): implement UTF-8 support in metric and label names #236

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f25f0f4
WIP Quote non-legacy metric names in descriptor
fedetorres93 Oct 1, 2024
9d1e518
Quote non-legacy label names and put non-legacy quoted metric names i…
fedetorres93 Oct 4, 2024
378d0e4
Add quoted metric and label names tests for text encoding
fedetorres93 Oct 4, 2024
b7f6396
Refactor metric and label names validation
fedetorres93 Oct 10, 2024
2339769
[WIP] Add content negotiation for non-legacy characters in metric and…
fedetorres93 Oct 18, 2024
4dc8859
Fix text encoding tests
fedetorres93 Oct 25, 2024
ce522cd
Move name validation functions to encoding
fedetorres93 Oct 29, 2024
81cd210
Add getters for name_validation_scheme and escaping_scheme
fedetorres93 Oct 29, 2024
9eee74c
Add RegistryBuilder
fedetorres93 Oct 29, 2024
e25fceb
Add documentation
fedetorres93 Oct 31, 2024
37ca910
Remove name validation scheme and escaping scheme constructors
fedetorres93 Oct 31, 2024
68d9ae9
Fix metric and label name escaping when UTF-8 validation is enabled
fedetorres93 Nov 5, 2024
c0b0b41
Remove commented code
fedetorres93 Nov 5, 2024
8b85cdb
Remove unused structs in axum UTF-8 example
fedetorres93 Nov 5, 2024
4b1ce00
Remove unnecessary escape_name calls
fedetorres93 Nov 5, 2024
8749a9c
Merge remote-tracking branch 'upstream/master' into ftorres/utf-8
fedetorres93 Nov 7, 2024
b0d7ae2
Formatting
fedetorres93 Nov 7, 2024
e62a619
Merge branch 'master' into ftorres/utf-8
fedetorres93 Dec 13, 2024
598fe97
Add more test cases
fedetorres93 Jan 16, 2025
813b459
Merge branch 'master' into ftorres/utf-8
fedetorres93 Jan 16, 2025
ca92109
Additional tests
fedetorres93 Jan 20, 2025
1ff883d
perf: improve perf of encoding with new UTF-8 validation
sd2k Jan 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add documentation
Signed-off-by: Federico Torres <federico.torres@grafana.com>
  • Loading branch information
fedetorres93 committed Oct 31, 2024
commit e25fceb536febaff9ca6339dfba1495a81c75261
16 changes: 16 additions & 0 deletions src/encoding.rs
Original file line number Diff line number Diff line change
@@ -748,10 +748,16 @@ impl<'a> From<protobuf::ExemplarValueEncoder<'a>> for ExemplarValueEncoder<'a> {
}
}

/// Enum for determining how metric and label names will
/// be validated.
#[derive(Debug, PartialEq, Default, Clone)]
pub enum ValidationScheme {
/// Setting that requires that metric and label names
/// conform to the original OpenMetrics character requirements.
#[default]
LegacyValidation,
/// Only requires that metric and label names be valid UTF-8
/// strings.
UTF8Validation,
}

@@ -798,12 +804,21 @@ fn is_quoted_label_name(name: &str, validation_scheme: &ValidationScheme) -> boo
*validation_scheme == ValidationScheme::UTF8Validation && !is_valid_legacy_label_name(name)
}

/// Enum for determining how metric and label names will
/// be escaped.
#[derive(Debug, Default, Clone)]
pub enum EscapingScheme {
/// Replaces all legacy-invalid characters with underscores.
#[default]
UnderscoreEscaping,
/// Similar to UnderscoreEscaping, except that dots are
/// converted to `_dot_` and pre-existing underscores are converted to `__`.
DotsEscaping,
/// Prepends the name with `U__` and replaces all invalid
/// characters with the Unicode value, surrounded by underscores. Single
/// underscores are replaced with double underscores.
ValueEncodingEscaping,
/// Indicates that a name will not be escaped.
NoEscaping,
}

@@ -860,6 +875,7 @@ fn escape_name(name: &str, scheme: &EscapingScheme) -> String {
escaped
}

/// Returns the escaping scheme to use based on the given header.
pub fn negotiate(header: &str) -> EscapingScheme {
if header.contains("allow-utf-8") {
return EscapingScheme::NoEscaping;
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![deny(dead_code)]
//#![deny(missing_docs)]
#![deny(missing_docs)]
#![deny(unused)]
#![forbid(unsafe_code)]
#![warn(missing_debug_implementations)]
28 changes: 27 additions & 1 deletion src/registry.rs
Original file line number Diff line number Diff line change
@@ -98,7 +98,7 @@ impl Registry {
..Default::default()
}
}

pub fn with_name_validation_scheme(
name_validation_scheme: ValidationScheme
) -> Self {
@@ -119,10 +119,12 @@ impl Registry {
}
}

/// Returns the given Registry's name validation scheme.
pub(crate) fn name_validation_scheme(&self) -> ValidationScheme {
self.name_validation_scheme.clone()
}

/// Returns the given Registry's escaping scheme.
pub(crate) fn escaping_scheme(&self) -> EscapingScheme {
self.escaping_scheme.clone()
}
@@ -343,6 +345,24 @@ impl Registry {
}
}

/// A builder for creating a [`Registry`].
///
/// This struct allows for a more flexible and readable way to construct
/// a [`Registry`] by providing methods to set various parameters such as
/// prefix, labels, name validation scheme, and escaping scheme.
///
/// ```
/// # use prometheus_client::encoding::EscapingScheme::UnderscoreEscaping;
/// # use prometheus_client::encoding::ValidationScheme::{LegacyValidation, UTF8Validation};
/// # use prometheus_client::registry::RegistryBuilder;
/// #
/// let registry = RegistryBuilder::new()
/// .with_prefix("my_prefix")
/// .with_labels(vec![("label1".into(), "value1".into())].into_iter())
/// .with_name_validation_scheme(LegacyValidation)
/// .with_escaping_scheme(UnderscoreEscaping)
/// .build();
/// ```
#[derive(Debug, Default)]
pub struct RegistryBuilder {
prefix: Option<Prefix>,
@@ -352,17 +372,20 @@ pub struct RegistryBuilder {
}

impl RegistryBuilder {
/// Creates a new default ['RegistryBuilder'].
pub fn new() -> Self {
Self {
..Default::default()
}
}

/// Sets the prefix for the [`RegistryBuilder`].
pub fn with_prefix(mut self, prefix: impl Into<String>) -> Self {
self.prefix = Some(Prefix(prefix.into()));
self
}

/// Sets the labels for the [`RegistryBuilder`].
pub fn with_labels(
mut self,
labels: impl Iterator<Item = (Cow<'static, str>, Cow<'static, str>)>,
@@ -371,16 +394,19 @@ impl RegistryBuilder {
self
}

/// Sets the name validation scheme for the [`RegistryBuilder`].
pub fn with_name_validation_scheme(mut self, name_validation_scheme: ValidationScheme) -> Self {
self.name_validation_scheme = name_validation_scheme;
self
}

/// Sets the escaping scheme for the [`RegistryBuilder`].
pub fn with_escaping_scheme(mut self, escaping_scheme: EscapingScheme) -> Self {
self.escaping_scheme = escaping_scheme;
self
}

/// Builds the [`Registry`] with the given parameters.
pub fn build(self) -> Registry {
Registry {
prefix: self.prefix,