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

Disabling the Instrument Name Validation under a new feature flag #2543

Merged
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1149685
Disabling all type of name checks except empty names and exceeding le…
anujnegi270 Jan 23, 2025
7c9f561
Merge branch 'main' into anujnegi/disable-name-check
cijothomas Jan 24, 2025
392a5ca
Merge branch 'main' into anujnegi/disable-name-check
anujnegi270 Jan 24, 2025
02f9d56
Added a unittest with the feature configuration
anujnegi270 Jan 24, 2025
905b95a
Merge branch 'main' into anujnegi/disable-name-check
lalitb Jan 24, 2025
f8eeb7f
Removing all instrument name check conditions under name validation d…
anujnegi270 Jan 24, 2025
13d6ffd
Merge branch 'main' into anujnegi/disable-name-check
anujnegi270 Jan 27, 2025
906e2b0
Merge branch 'main' into anujnegi/disable-name-check
lalitb Jan 27, 2025
2eae4d0
Removing commentted code; Change.md updated
anujnegi270 Jan 28, 2025
0602a08
Merge branch 'anujnegi/disable-name-check' of github.com:anujnegi270/…
anujnegi270 Jan 28, 2025
830aed2
Merge branch 'main' into anujnegi/disable-name-check
anujnegi270 Jan 28, 2025
44d5381
Merge branch 'main' into anujnegi/disable-name-check
anujnegi270 Jan 28, 2025
1099de4
Added Warning note to the feature
anujnegi270 Jan 28, 2025
c4377ac
Merge branch 'main' into anujnegi/disable-name-check
anujnegi270 Jan 31, 2025
dfd1076
Merge branch 'main' into anujnegi/disable-name-check
anujnegi270 Feb 3, 2025
7212519
Merge branch 'main' into anujnegi/disable-name-check
anujnegi270 Feb 4, 2025
c2f3da1
No Noops when feature flag is on
anujnegi270 Feb 4, 2025
8cf3ac0
lint correct
anujnegi270 Feb 4, 2025
655b815
Merge branch 'main' into anujnegi/disable-name-check
anujnegi270 Feb 4, 2025
95ec73f
Resolved nit errors & comments
anujnegi270 Feb 5, 2025
77d2535
Merge branch 'main' into anujnegi/disable-name-check
cijothomas Feb 5, 2025
d051a06
Changing the test name to a better one
anujnegi270 Feb 5, 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
1 change: 1 addition & 0 deletions opentelemetry-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ experimental_metrics_periodicreader_with_async_runtime = ["metrics"]
spec_unstable_metrics_views = ["metrics"]
experimental_logs_batch_log_processor_with_async_runtime = ["logs"]
experimental_trace_batch_span_processor_with_async_runtime = ["trace"]
experimental_metrics_disable_name_validation = ["metrics"]


[[bench]]
Expand Down
85 changes: 82 additions & 3 deletions opentelemetry-sdk/src/metrics/meter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,29 @@ use crate::metrics::{
use super::noop::NoopSyncInstrument;

// maximum length of instrument name
#[cfg(not(feature = "experimental_metrics_disable_name_validation"))]
anujnegi270 marked this conversation as resolved.
Show resolved Hide resolved
const INSTRUMENT_NAME_MAX_LENGTH: usize = 255;

// maximum length of instrument unit name
const INSTRUMENT_UNIT_NAME_MAX_LENGTH: usize = 63;

// Characters allowed in instrument name
#[cfg(not(feature = "experimental_metrics_disable_name_validation"))]
const INSTRUMENT_NAME_ALLOWED_NON_ALPHANUMERIC_CHARS: [char; 4] = ['_', '.', '-', '/'];

// instrument validation error strings
// instrument name validation error strings
#[cfg(not(feature = "experimental_metrics_disable_name_validation"))]
const INSTRUMENT_NAME_EMPTY: &str = "instrument name must be non-empty";
#[cfg(not(feature = "experimental_metrics_disable_name_validation"))]
const INSTRUMENT_NAME_LENGTH: &str = "instrument name must be less than 256 characters";
#[cfg(not(feature = "experimental_metrics_disable_name_validation"))]
const INSTRUMENT_NAME_INVALID_CHAR: &str =
"characters in instrument name must be ASCII and belong to the alphanumeric characters, '_', '.', '-' and '/'";
#[cfg(not(feature = "experimental_metrics_disable_name_validation"))]
const INSTRUMENT_NAME_FIRST_ALPHABETIC: &str =
"instrument name must start with an alphabetic character";

// instrument unit validation error strings
const INSTRUMENT_UNIT_LENGTH: &str = "instrument unit must be less than 64 characters";
const INSTRUMENT_UNIT_INVALID_CHAR: &str = "characters in instrument unit must be ASCII";

Expand Down Expand Up @@ -572,6 +583,21 @@ fn validate_bucket_boundaries(boundaries: &[f64]) -> MetricResult<()> {
Ok(())
}

// Regex pattern for Windows Performance Counter names
// #[cfg(feature = "experimental_metrics_disable_name_validation")]
// fn is_valid_windows_perf_counter_name(name: &str) -> bool {
// let pattern = r"^\\[A-Za-z0-9_ ]+\\[A-Za-z0-9_ /%-]+$";
// let re = Regex::new(pattern).unwrap();
// re.is_match(name)
// }
anujnegi270 marked this conversation as resolved.
Show resolved Hide resolved

#[cfg(feature = "experimental_metrics_disable_name_validation")]
anujnegi270 marked this conversation as resolved.
Show resolved Hide resolved
fn validate_instrument_name(_name: &str) -> MetricResult<()> {
// No name restrictions when name validation is disabled
Ok(())
}

#[cfg(not(feature = "experimental_metrics_disable_name_validation"))]
fn validate_instrument_name(name: &str) -> MetricResult<()> {
if name.is_empty() {
return Err(MetricError::InvalidInstrumentConfiguration(
Expand All @@ -583,6 +609,7 @@ fn validate_instrument_name(name: &str) -> MetricResult<()> {
INSTRUMENT_NAME_LENGTH,
));
}

if name.starts_with(|c: char| !c.is_ascii_alphabetic()) {
return Err(MetricError::InvalidInstrumentConfiguration(
INSTRUMENT_NAME_FIRST_ALPHABETIC,
Expand Down Expand Up @@ -676,12 +703,18 @@ mod tests {
use crate::metrics::MetricError;

use super::{
validate_instrument_name, validate_instrument_unit, INSTRUMENT_NAME_FIRST_ALPHABETIC,
INSTRUMENT_NAME_INVALID_CHAR, INSTRUMENT_NAME_LENGTH, INSTRUMENT_UNIT_INVALID_CHAR,
validate_instrument_name, validate_instrument_unit, INSTRUMENT_UNIT_INVALID_CHAR,
INSTRUMENT_UNIT_LENGTH,
};

#[cfg(not(feature = "experimental_metrics_disable_name_validation"))]
use super::{
INSTRUMENT_NAME_EMPTY, INSTRUMENT_NAME_FIRST_ALPHABETIC, INSTRUMENT_NAME_INVALID_CHAR,
INSTRUMENT_NAME_LENGTH,
};

#[test]
#[cfg(not(feature = "experimental_metrics_disable_name_validation"))]
fn instrument_name_validation() {
// (name, expected error)
let instrument_name_test_cases = vec![
Expand All @@ -694,6 +727,52 @@ mod tests {
("allow/slash", ""),
("allow_under_score", ""),
("allow.dots.ok", ""),
("", INSTRUMENT_NAME_EMPTY),
("\\allow\\slash /sec", INSTRUMENT_NAME_FIRST_ALPHABETIC),
("\\allow\\$$slash /sec", INSTRUMENT_NAME_FIRST_ALPHABETIC),
("Total $ Count", INSTRUMENT_NAME_INVALID_CHAR),
(
"\\test\\UsagePercent(Total) > 80%",
INSTRUMENT_NAME_FIRST_ALPHABETIC,
),
("/not / allowed", INSTRUMENT_NAME_FIRST_ALPHABETIC),
];
for (name, expected_error) in instrument_name_test_cases {
let assert = |result: Result<_, MetricError>| {
if expected_error.is_empty() {
assert!(result.is_ok());
} else {
assert!(matches!(
result.unwrap_err(),
MetricError::InvalidInstrumentConfiguration(msg) if msg == expected_error
));
}
};

assert(validate_instrument_name(name).map(|_| ()));
}
}

#[test]
#[cfg(feature = "experimental_metrics_disable_name_validation")]
fn instrument_name_validation() {
anujnegi270 marked this conversation as resolved.
Show resolved Hide resolved
// (name, expected error)
let instrument_name_test_cases = vec![
("validateName", ""),
("_startWithNoneAlphabet", ""),
("utf8char锈", ""),
("a".repeat(255).leak(), ""),
("a".repeat(256).leak(), ""),
("invalid name", ""),
("allow/slash", ""),
("allow_under_score", ""),
("allow.dots.ok", ""),
("", ""),
("\\allow\\slash /sec", ""),
("\\allow\\$$slash /sec", ""),
("Total $ Count", ""),
("\\test\\UsagePercent(Total) > 80%", ""),
("/not / allowed", ""),
];
for (name, expected_error) in instrument_name_test_cases {
let assert = |result: Result<_, MetricError>| {
Expand Down
71 changes: 69 additions & 2 deletions opentelemetry-sdk/src/metrics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,16 @@ mod tests {
use std::time::Duration;

// Run all tests in this mod
// cargo test metrics::tests --features=testing
// cargo test metrics::tests --features=testing,spec_unstable_metrics_views
// Note for all tests from this point onwards in this mod:
// "multi_thread" tokio flavor must be used else flush won't
// be able to make progress!

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
#[cfg(not(feature = "experimental_metrics_disable_name_validation"))]
async fn invalid_instrument_config_noops() {
// Run this test with stdout enabled to see output.
// cargo test invalid_instrument_config_noops --features=testing -- --nocapture
// cargo test invalid_instrument_config_noops --features=testing,spec_unstable_metrics_views -- --nocapture
anujnegi270 marked this conversation as resolved.
Show resolved Hide resolved
let invalid_instrument_names = vec![
"_startWithNoneAlphabet",
"utf8char锈",
Expand Down Expand Up @@ -215,6 +216,68 @@ mod tests {
}
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
#[cfg(feature = "experimental_metrics_disable_name_validation")]
async fn valid_instrument_config_noops_with_feature_experimental_metrics_disable_name_validation(
anujnegi270 marked this conversation as resolved.
Show resolved Hide resolved
) {
// Run this test with stdout enabled to see output.
// cargo test valid_instrument_config_noops_with_feature_experimental_metrics_disable_name_validation --features "testing,spec_unstable_metrics_views,experimental_metrics_disable_name_validation" -- --nocapture
let invalid_instrument_names = vec![
"_startWithNoneAlphabet",
"utf8char锈",
"",
"a".repeat(256).leak(),
"\\allow\\slash /sec",
"\\allow\\$$slash /sec",
"Total $ Count",
"\\test\\UsagePercent(Total) > 80%",
"invalid name",
];
for name in invalid_instrument_names {
let test_context = TestContext::new(Temporality::Cumulative);
let counter = test_context.meter().u64_counter(name).build();
counter.add(1, &[]);

let up_down_counter = test_context.meter().i64_up_down_counter(name).build();
up_down_counter.add(1, &[]);

let gauge = test_context.meter().f64_gauge(name).build();
gauge.record(1.9, &[]);

let histogram = test_context.meter().f64_histogram(name).build();
histogram.record(1.0, &[]);

let _observable_counter = test_context
.meter()
.u64_observable_counter(name)
.with_callback(move |observer| {
observer.observe(1, &[]);
})
.build();

let _observable_gauge = test_context
.meter()
.f64_observable_gauge(name)
.with_callback(move |observer| {
observer.observe(1.0, &[]);
})
.build();

let _observable_up_down_counter = test_context
.meter()
.i64_observable_up_down_counter(name)
.with_callback(move |observer| {
observer.observe(1, &[]);
})
.build();

test_context.flush_metrics();

// As instrument name is invalid, no metrics should be exported
anujnegi270 marked this conversation as resolved.
Show resolved Hide resolved
test_context.check_no_metrics();
}
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn counter_aggregation_delta() {
// Run this test with stdout enabled to see output.
Expand Down Expand Up @@ -2495,6 +2558,10 @@ mod tests {
.get_finished_metrics()
.expect("metrics expected to be exported"); // TODO: Need to fix InMemoryMetricExporter to return None.

#[cfg(feature = "experimental_metrics_disable_name_validation")]
assert!(!resource_metrics.is_empty(), "metrics should be exported"); // TODO: Update this when enhanced feature flag is added.

#[cfg(not(feature = "experimental_metrics_disable_name_validation"))]
assert!(resource_metrics.is_empty(), "no metrics should be exported");
}

Expand Down
Loading