diff --git a/crates/weaver_emit/src/lib.rs b/crates/weaver_emit/src/lib.rs index 3a5f675bd..3cf87b5dc 100644 --- a/crates/weaver_emit/src/lib.rs +++ b/crates/weaver_emit/src/lib.rs @@ -239,6 +239,7 @@ mod tests { body: None, entity_associations: vec![], annotations: None, + migration: None, }, ResolvedGroup { id: "test.updowncounter".to_owned(), @@ -261,6 +262,7 @@ mod tests { display_name: None, body: None, annotations: None, + migration: None, }, ResolvedGroup { id: "test.counter".to_owned(), @@ -283,6 +285,7 @@ mod tests { display_name: None, body: None, annotations: None, + migration: None, }, ResolvedGroup { id: "test.gauge".to_owned(), @@ -305,6 +308,7 @@ mod tests { display_name: None, body: None, annotations: None, + migration: None, }, ResolvedGroup { id: "test.histogram".to_owned(), @@ -327,6 +331,7 @@ mod tests { display_name: None, body: None, annotations: None, + migration: None, }, ResolvedGroup { id: "test.updowncounter.double".to_owned(), @@ -349,6 +354,7 @@ mod tests { display_name: None, body: None, annotations: None, + migration: None, }, ResolvedGroup { id: "test.counter.double".to_owned(), @@ -371,6 +377,7 @@ mod tests { display_name: None, body: None, annotations: None, + migration: None, }, ResolvedGroup { id: "test.gauge.double".to_owned(), @@ -393,6 +400,7 @@ mod tests { display_name: None, body: None, annotations: None, + migration: None, }, ResolvedGroup { id: "test.histogram.double".to_owned(), @@ -415,6 +423,7 @@ mod tests { display_name: None, body: None, annotations: None, + migration: None, }, ], }; diff --git a/crates/weaver_forge/src/registry.rs b/crates/weaver_forge/src/registry.rs index f44ec30cf..c4d7282d3 100644 --- a/crates/weaver_forge/src/registry.rs +++ b/crates/weaver_forge/src/registry.rs @@ -15,6 +15,7 @@ use weaver_resolved_schema::registry::{Group, Registry}; use weaver_semconv::any_value::AnyValueSpec; use weaver_semconv::deprecated::Deprecated; use weaver_semconv::group::{GroupType, InstrumentSpec, SpanKindSpec}; +use weaver_semconv::migration::MigrationSpec; use weaver_semconv::stability::Stability; use weaver_semconv::YamlValue; @@ -116,6 +117,9 @@ pub struct ResolvedGroup { #[serde(default)] #[serde(skip_serializing_if = "Vec::is_empty")] pub entity_associations: Vec, + /// Header for the namespace/page. + #[serde(skip_serializing_if = "Option::is_none")] + pub migration: Option, /// Annotations for the group. #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] @@ -172,6 +176,7 @@ impl ResolvedGroup { lineage, display_name: group.display_name.clone(), body: group.body.clone(), + migration: group.migration.clone(), entity_associations: group.entity_associations.clone(), annotations: group.annotations.clone(), }) @@ -233,6 +238,7 @@ impl ResolvedRegistry { lineage, display_name: group.display_name.clone(), body: group.body.clone(), + migration: group.migration.clone(), entity_associations: group.entity_associations.clone(), annotations: group.annotations.clone(), } diff --git a/crates/weaver_live_check/src/live_checker.rs b/crates/weaver_live_check/src/live_checker.rs index 1d2897726..aecd1b37c 100644 --- a/crates/weaver_live_check/src/live_checker.rs +++ b/crates/weaver_live_check/src/live_checker.rs @@ -455,6 +455,7 @@ mod tests { display_name: None, body: None, annotations: None, + migration: None, }], } } @@ -527,6 +528,7 @@ mod tests { display_name: Some("System Memory Attributes".to_owned()), body: None, annotations: None, + migration: None, }, // System uptime metric ResolvedGroup { @@ -550,6 +552,7 @@ mod tests { display_name: None, body: None, annotations: None, + migration: None, }, // System memory usage metric ResolvedGroup { @@ -591,6 +594,7 @@ mod tests { display_name: None, body: None, annotations: None, + migration: None, }, ], } @@ -642,6 +646,7 @@ mod tests { display_name: None, body: None, annotations: None, + migration: None, }], }; @@ -895,6 +900,7 @@ mod tests { display_name: None, body: None, annotations: None, + migration: None, }], }; diff --git a/crates/weaver_resolved_schema/src/registry.rs b/crates/weaver_resolved_schema/src/registry.rs index 52eff43d5..33060d52b 100644 --- a/crates/weaver_resolved_schema/src/registry.rs +++ b/crates/weaver_resolved_schema/src/registry.rs @@ -13,10 +13,11 @@ use crate::catalog::Catalog; use crate::error::{handle_errors, Error}; use crate::lineage::GroupLineage; use crate::registry::GroupStats::{ - AttributeGroup, Entity, Event, Metric, MetricGroup, Scope, Span, Undefined, + AttributeGroup, Entity, Event, Metric, MetricGroup, Scope, Span, Undefined, NameSpace }; use serde::{Deserialize, Serialize}; use weaver_semconv::deprecated::Deprecated; +use weaver_semconv::migration::MigrationSpec; use weaver_semconv::group::{GroupType, InstrumentSpec, SpanKindSpec}; use weaver_semconv::provenance::Provenance; use weaver_semconv::stability::Stability; @@ -127,6 +128,9 @@ pub struct Group { /// This fields is only used for event groups. #[serde(skip_serializing_if = "Option::is_none")] pub body: Option, + /// Migration details for the namespace. + #[serde(skip_serializing_if = "Option::is_none")] + pub migration: Option, /// Annotations for the group. #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] @@ -155,6 +159,7 @@ impl Group { GroupType::MetricGroup => None, GroupType::Entity => self.name.as_deref(), GroupType::Scope => None, + GroupType::NameSpace => self.name.as_deref(), GroupType::Undefined => None, } } @@ -218,6 +223,11 @@ pub enum GroupStats { /// Common statistics for this type of group. common_stats: CommonGroupStats, }, + /// Statistics for a namespace. + NameSpace { + /// Common statistics for this type of group. + common_stats: CommonGroupStats, + }, /// Statistics for a span. Span { /// Common statistics for this type of group. @@ -307,6 +317,9 @@ impl Registry { GroupType::Scope => Scope { common_stats: CommonGroupStats::default(), }, + GroupType::NameSpace => NameSpace { + common_stats: CommonGroupStats::default(), + }, GroupType::Span => Span { common_stats: CommonGroupStats::default(), span_kind_breakdown: HashMap::new(), @@ -359,6 +372,9 @@ impl Registry { Scope { common_stats } => { common_stats.update_stats(group); } + NameSpace { common_stats } => { + common_stats.update_stats(group); + } Span { common_stats, span_kind_breakdown, diff --git a/crates/weaver_resolver/src/registry.rs b/crates/weaver_resolver/src/registry.rs index 8123bf6f9..2aa012fa4 100644 --- a/crates/weaver_resolver/src/registry.rs +++ b/crates/weaver_resolver/src/registry.rs @@ -397,6 +397,7 @@ fn group_from_spec(group: GroupSpecWithProvenance) -> UnresolvedGroup { lineage: Some(GroupLineage::new(group.provenance.clone())), display_name: group.spec.display_name, body: group.spec.body, + migration: group.spec.migration, annotations: group.spec.annotations, entity_associations: group.spec.entity_associations, }, diff --git a/crates/weaver_semconv/data/gen-ai.yaml b/crates/weaver_semconv/data/gen-ai.yaml new file mode 100644 index 000000000..614f5a0a0 --- /dev/null +++ b/crates/weaver_semconv/data/gen-ai.yaml @@ -0,0 +1,11 @@ +groups: + - id: namespace.gen-ai + type: namespace + name: gen-ai + note: This is a description of the gen ai namespace + brief: This is a summary of the gen ai namespace + migration: + brief: Migration strategy + note: To protect personal information the following document should be followed. + targetVersion: 1.20.0 + link: ./migration-guide.md \ No newline at end of file diff --git a/crates/weaver_semconv/src/group.rs b/crates/weaver_semconv/src/group.rs index 831e1b232..b2b54ee8e 100644 --- a/crates/weaver_semconv/src/group.rs +++ b/crates/weaver_semconv/src/group.rs @@ -14,6 +14,7 @@ use crate::any_value::AnyValueSpec; use crate::attribute::{AttributeSpec, AttributeType, PrimitiveOrArrayTypeSpec}; use crate::deprecated::Deprecated; use crate::group::InstrumentSpec::{Counter, Gauge, Histogram, UpDownCounter}; +use crate::migration::MigrationSpec; use crate::provenance::Provenance; use crate::semconv::Imports; use crate::stability::Stability; @@ -109,6 +110,9 @@ pub struct GroupSpec { /// Note: only valid if type is event #[serde(skip_serializing_if = "Option::is_none")] pub body: Option, + /// Migration details for the namespace/. + #[serde(skip_serializing_if = "Option::is_none")] + pub migration: Option, /// Annotations for the group. #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] @@ -137,8 +141,8 @@ impl GroupSpec { }); } - // Field stability is required for all group types except attribute group. - if self.r#type != GroupType::AttributeGroup && self.stability.is_none() { + // Field stability is required for all group types except namespaces & attribute group. + if self.r#type != GroupType::AttributeGroup && self.r#type != GroupType::NameSpace && self.stability.is_none() { errors.push(Error::InvalidGroupStability { path_or_url: path_or_url.to_owned(), group_id: self.id.clone(), @@ -159,9 +163,10 @@ impl GroupSpec { // Groups should only reference attributes once. validate_duplicate_attribute_ref(&mut errors, &self.attributes, &self.id, path_or_url); - // All types, except metric and event, must have extends or attributes or both. + // All types, except namespaces, metric and event, must have extends or attributes or both. if self.r#type != GroupType::Metric && self.r#type != GroupType::Event + && self.r#type != GroupType::NameSpace && self.extends.is_none() && self.attributes.is_empty() { @@ -182,6 +187,7 @@ impl GroupSpec { .to_owned(), }); } + // TODO: this should become a warning as span events are deprecated if !self.events.is_empty() { errors.push(Error::InvalidGroup { path_or_url: path_or_url.to_owned(), @@ -545,7 +551,7 @@ fn validate_any_value( } } -/// The different types of groups: `attribute_group`, `span`, `event`, `metric`, `entity`, `scope`. +/// The different types of groups: `attribute_group`, `span`, `event`, `metric`, `entity`, `scope`, `namespace`. /// /// Note: The `resource` type is no longer used and is an alias for `entity`. #[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Hash, Clone, JsonSchema)] @@ -572,6 +578,8 @@ pub enum GroupType { Scope, /// Undefined group type. Undefined, + /// Namespace semantic convention. + NameSpace, } impl Default for GroupType { @@ -697,6 +705,7 @@ mod tests { name: None, display_name: None, body: None, + migration: None, annotations: None, entity_associations: Vec::new(), }; @@ -862,6 +871,7 @@ mod tests { name: None, display_name: None, body: None, + migration: None, annotations: None, entity_associations: Vec::new(), }; @@ -1159,6 +1169,7 @@ mod tests { ), }, }), + migration: None, annotations: None, entity_associations: Vec::new(), }; @@ -1374,6 +1385,7 @@ mod tests { ), }, }), + migration: None, annotations: None, entity_associations: Vec::new(), }; @@ -1518,6 +1530,7 @@ mod tests { name: None, display_name: None, body: None, + migration: None, annotations: None, entity_associations: Vec::new(), }; @@ -1688,6 +1701,7 @@ mod tests { name: None, display_name: None, body: None, + migration: None, annotations: None, entity_associations: Vec::new(), }; @@ -1840,6 +1854,7 @@ mod tests { name: None, display_name: None, body: None, + migration: None, annotations: None, entity_associations: Vec::new(), }; @@ -1900,6 +1915,7 @@ mod tests { name: None, display_name: None, body: None, + migration: None, annotations: None, entity_associations: vec!["test".to_owned()], }; @@ -1938,4 +1954,34 @@ mod tests { result ); } + + #[test] + fn test_validate_namespace(){ + let mut group = GroupSpec { + id: "test".to_owned(), + r#type: GroupType::NameSpace, + name: Some("test_namespace".to_owned()), + brief: "test".to_owned(), + note: "test".to_owned(), + prefix: "".to_owned(), + extends: None, + stability: None, + deprecated: None, + span_kind: None, + events: vec![], + metric_name: None, + instrument: None, + unit: None, + display_name: None, + attributes: vec![], + body: None, + migration: None, + annotations: None, + entity_associations: Vec::new(), + }; + assert!(group + .validate("") + .into_result_failing_non_fatal() + .is_ok()); + } } diff --git a/crates/weaver_semconv/src/lib.rs b/crates/weaver_semconv/src/lib.rs index 37b7c9cac..3fc132a03 100644 --- a/crates/weaver_semconv/src/lib.rs +++ b/crates/weaver_semconv/src/lib.rs @@ -17,6 +17,7 @@ pub mod any_value; pub mod attribute; pub mod deprecated; pub mod group; +pub mod migration; pub mod json_schema; pub mod manifest; pub mod metric; diff --git a/crates/weaver_semconv/src/migration.rs b/crates/weaver_semconv/src/migration.rs new file mode 100644 index 000000000..935e3e570 --- /dev/null +++ b/crates/weaver_semconv/src/migration.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: Apache-2.0 + +//! Stability specification. + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +/// A migration specification +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct MigrationSpec { + + /// An optional brief to be added alongside link to migration guide + pub brief: Option, + /// An optional note to be added to the migration guide + pub note: Option, + /// The version which the migration guide should guide a user to. + pub target: String, +} + +impl MigrationSpec { + /// returns the brief of the migration guide + #[must_use] + fn brief(&self) -> &Option { + &self.brief + } + /// returns the note of the migration guide + #[must_use] + fn note(&self) -> &Option { + &self.note + } + /// returns the target of the migration guide + #[must_use] + fn target(&self) -> &String { + &self.target + } +} diff --git a/crates/weaver_semconv/src/registry.rs b/crates/weaver_semconv/src/registry.rs index 9160b9498..6ad4fc6c6 100644 --- a/crates/weaver_semconv/src/registry.rs +++ b/crates/weaver_semconv/src/registry.rs @@ -357,6 +357,7 @@ mod tests { name: None, display_name: Some("Group 1".to_owned()), body: None, + migration: None, annotations: None, entity_associations: Vec::new(), }], @@ -384,6 +385,7 @@ mod tests { name: None, display_name: Some("Group 2".to_owned()), body: None, + migration: None, annotations: None, entity_associations: Vec::new(), }], diff --git a/src/registry/stats.rs b/src/registry/stats.rs index 4d922c3cc..94874e97d 100644 --- a/src/registry/stats.rs +++ b/src/registry/stats.rs @@ -117,6 +117,9 @@ fn display_schema_stats(schema: &ResolvedTelemetrySchema) { display_common_group_stats(group_type, common_stats); total_number_of_attributes += common_stats.total_attribute_count; } + GroupStats::NameSpace { common_stats } => { + display_common_group_stats(group_type, common_stats); + } GroupStats::Span { common_stats, span_kind_breakdown,