From a4348fd9d81aca3da8e119d20558bac8c19676ab Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Sun, 18 May 2025 11:18:09 -0400 Subject: [PATCH 1/6] build profiles signal proto --- opentelemetry-proto/Cargo.toml | 1 + opentelemetry-proto/src/proto.rs | 8 ++++++++ opentelemetry-proto/src/transform/mod.rs | 3 +++ opentelemetry-proto/tests/grpc_build.rs | 5 +++++ 4 files changed, 17 insertions(+) diff --git a/opentelemetry-proto/Cargo.toml b/opentelemetry-proto/Cargo.toml index d4ee2ffc74..bc6bce3dbf 100644 --- a/opentelemetry-proto/Cargo.toml +++ b/opentelemetry-proto/Cargo.toml @@ -43,6 +43,7 @@ trace = ["opentelemetry/trace", "opentelemetry_sdk/trace"] metrics = ["opentelemetry/metrics", "opentelemetry_sdk/metrics"] logs = ["opentelemetry/logs", "opentelemetry_sdk/logs"] zpages = ["trace"] +profiles = [] testing = ["opentelemetry/testing"] # add ons diff --git a/opentelemetry-proto/src/proto.rs b/opentelemetry-proto/src/proto.rs index 13a28ce2a4..c74c102545 100644 --- a/opentelemetry-proto/src/proto.rs +++ b/opentelemetry-proto/src/proto.rs @@ -266,5 +266,13 @@ pub mod tonic { pub mod v1; } + /// Generated types used in zpages. + #[cfg(feature = "profiles")] + #[path = ""] + pub mod profiles { + #[path = "opentelemetry.proto.profiles.v1development.rs"] + pub mod v1; + } + pub use crate::transform::common::tonic::Attributes; } diff --git a/opentelemetry-proto/src/transform/mod.rs b/opentelemetry-proto/src/transform/mod.rs index cd80544714..f0b7b86d3d 100644 --- a/opentelemetry-proto/src/transform/mod.rs +++ b/opentelemetry-proto/src/transform/mod.rs @@ -11,3 +11,6 @@ pub mod logs; #[cfg(feature = "zpages")] pub mod tracez; + +#[cfg(feature = "profiles")] +pub mod profiles; diff --git a/opentelemetry-proto/tests/grpc_build.rs b/opentelemetry-proto/tests/grpc_build.rs index d09a13cd64..0ca9c5b24e 100644 --- a/opentelemetry-proto/tests/grpc_build.rs +++ b/opentelemetry-proto/tests/grpc_build.rs @@ -12,6 +12,7 @@ const TONIC_PROTO_FILES: &[&str] = &[ "src/proto/opentelemetry-proto/opentelemetry/proto/collector/metrics/v1/metrics_service.proto", "src/proto/opentelemetry-proto/opentelemetry/proto/logs/v1/logs.proto", "src/proto/opentelemetry-proto/opentelemetry/proto/collector/logs/v1/logs_service.proto", + "src/proto/opentelemetry-proto/opentelemetry/proto/profiles/v1development/profiles.proto", "src/proto/tracez.proto", ]; const TONIC_INCLUDES: &[&str] = &["src/proto/opentelemetry-proto", "src/proto"]; @@ -66,6 +67,7 @@ fn build_tonic() { "metrics.v1.Summary", "metrics.v1.NumberDataPoint", "metrics.v1.HistogramDataPoint", + "profiles.v1development.Function" ] { builder = builder.type_attribute( path, @@ -87,6 +89,7 @@ fn build_tonic() { "logs.v1.LogRecord.trace_id", "metrics.v1.Exemplar.span_id", "metrics.v1.Exemplar.trace_id", + "profiles.v1development.Profile.profile_id", ] { builder = builder .field_attribute(path, "#[cfg_attr(feature = \"with-serde\", serde(serialize_with = \"crate::proto::serializers::serialize_to_hex_string\", deserialize_with = \"crate::proto::serializers::deserialize_from_hex_string\"))]") @@ -106,6 +109,8 @@ fn build_tonic() { "metrics.v1.HistogramDataPoint.time_unix_nano", "metrics.v1.NumberDataPoint.start_time_unix_nano", "metrics.v1.NumberDataPoint.time_unix_nano", + "profiles.v1development.Sample.timestamps_unix_nano", + "profiles.v1development.Profile.time_nanos", ] { builder = builder .field_attribute(path, "#[cfg_attr(feature = \"with-serde\", serde(serialize_with = \"crate::proto::serializers::serialize_u64_to_string\", deserialize_with = \"crate::proto::serializers::deserialize_string_to_u64\"))]") From 47a3151f89982a487424294e9a9d0ee93535d13e Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Sun, 18 May 2025 11:19:10 -0400 Subject: [PATCH 2/6] build profiles signal proto --- opentelemetry-proto/tests/grpc_build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-proto/tests/grpc_build.rs b/opentelemetry-proto/tests/grpc_build.rs index 0ca9c5b24e..aa9d77993c 100644 --- a/opentelemetry-proto/tests/grpc_build.rs +++ b/opentelemetry-proto/tests/grpc_build.rs @@ -67,7 +67,7 @@ fn build_tonic() { "metrics.v1.Summary", "metrics.v1.NumberDataPoint", "metrics.v1.HistogramDataPoint", - "profiles.v1development.Function" + "profiles.v1development.Function", ] { builder = builder.type_attribute( path, From fb73a78845dd032c7a8ce7a263e72d7a705db319 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Sun, 18 May 2025 11:19:41 -0400 Subject: [PATCH 3/6] build profiles signal proto --- ...ntelemetry.proto.profiles.v1development.rs | 487 ++++++++++++++++++ opentelemetry-proto/src/transform/profiles.rs | 1 + 2 files changed, 488 insertions(+) create mode 100644 opentelemetry-proto/src/proto/tonic/opentelemetry.proto.profiles.v1development.rs create mode 100644 opentelemetry-proto/src/transform/profiles.rs diff --git a/opentelemetry-proto/src/proto/tonic/opentelemetry.proto.profiles.v1development.rs b/opentelemetry-proto/src/proto/tonic/opentelemetry.proto.profiles.v1development.rs new file mode 100644 index 0000000000..58e2646638 --- /dev/null +++ b/opentelemetry-proto/src/proto/tonic/opentelemetry.proto.profiles.v1development.rs @@ -0,0 +1,487 @@ +// This file is @generated by prost-build. +/// ProfilesData represents the profiles data that can be stored in persistent storage, +/// OR can be embedded by other protocols that transfer OTLP profiles data but do not +/// implement the OTLP protocol. +/// +/// The main difference between this message and collector protocol is that +/// in this message there will not be any "control" or "metadata" specific to +/// OTLP protocol. +/// +/// When new fields are added into this message, the OTLP request MUST be updated +/// as well. +#[cfg_attr(feature = "with-schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "with-serde", serde(rename_all = "camelCase"))] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProfilesData { + /// An array of ResourceProfiles. + /// For data coming from an SDK profiler, this array will typically contain one + /// element. Host-level profilers will usually create one ResourceProfile per + /// container, as well as one additional ResourceProfile grouping all samples + /// from non-containerized processes. + /// Other resource groupings are possible as well and clarified via + /// Resource.attributes and semantic conventions. + #[prost(message, repeated, tag = "1")] + pub resource_profiles: ::prost::alloc::vec::Vec, + /// Mappings from address ranges to the image/binary/library mapped + /// into that address range referenced by locations via Location.mapping_index. + #[prost(message, repeated, tag = "2")] + pub mapping_table: ::prost::alloc::vec::Vec, + /// Locations referenced by samples via Profile.location_indices. + #[prost(message, repeated, tag = "3")] + pub location_table: ::prost::alloc::vec::Vec, + /// Functions referenced by locations via Line.function_index. + #[prost(message, repeated, tag = "4")] + pub function_table: ::prost::alloc::vec::Vec, + /// Links referenced by samples via Sample.link_index. + #[prost(message, repeated, tag = "5")] + pub link_table: ::prost::alloc::vec::Vec, + /// A common table for strings referenced by various messages. + /// string_table\[0\] must always be "". + #[prost(string, repeated, tag = "6")] + pub string_table: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + /// A common table for attributes referenced by various messages. + #[prost(message, repeated, tag = "7")] + pub attribute_table: ::prost::alloc::vec::Vec, + /// Represents a mapping between Attribute Keys and Units. + #[prost(message, repeated, tag = "8")] + pub attribute_units: ::prost::alloc::vec::Vec, +} +/// A collection of ScopeProfiles from a Resource. +#[cfg_attr(feature = "with-schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "with-serde", serde(rename_all = "camelCase"))] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ResourceProfiles { + /// The resource for the profiles in this message. + /// If this field is not set then no resource info is known. + #[prost(message, optional, tag = "1")] + pub resource: ::core::option::Option, + /// A list of ScopeProfiles that originate from a resource. + #[prost(message, repeated, tag = "2")] + pub scope_profiles: ::prost::alloc::vec::Vec, + /// The Schema URL, if known. This is the identifier of the Schema that the resource data + /// is recorded in. Notably, the last part of the URL path is the version number of the + /// schema: http\[s\]://server\[:port\]/path/. To learn more about Schema URL see + /// + /// This schema_url applies to the data in the "resource" field. It does not apply + /// to the data in the "scope_profiles" field which have their own schema_url field. + #[prost(string, tag = "3")] + pub schema_url: ::prost::alloc::string::String, +} +/// A collection of Profiles produced by an InstrumentationScope. +#[cfg_attr(feature = "with-schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "with-serde", serde(rename_all = "camelCase"))] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ScopeProfiles { + /// The instrumentation scope information for the profiles in this message. + /// Semantically when InstrumentationScope isn't set, it is equivalent with + /// an empty instrumentation scope name (unknown). + #[prost(message, optional, tag = "1")] + pub scope: ::core::option::Option, + /// A list of Profiles that originate from an instrumentation scope. + #[prost(message, repeated, tag = "2")] + pub profiles: ::prost::alloc::vec::Vec, + /// The Schema URL, if known. This is the identifier of the Schema that the profile data + /// is recorded in. Notably, the last part of the URL path is the version number of the + /// schema: http\[s\]://server\[:port\]/path/. To learn more about Schema URL see + /// + /// This schema_url applies to all profiles in the "profiles" field. + #[prost(string, tag = "3")] + pub schema_url: ::prost::alloc::string::String, +} +/// Represents a complete profile, including sample types, samples, +/// mappings to binaries, locations, functions, string table, and additional metadata. +/// It modifies and annotates pprof Profile with OpenTelemetry specific fields. +/// +/// Note that whilst fields in this message retain the name and field id from pprof in most cases +/// for ease of understanding data migration, it is not intended that pprof:Profile and +/// OpenTelemetry:Profile encoding be wire compatible. +#[cfg_attr(feature = "with-schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "with-serde", serde(rename_all = "camelCase"))] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Profile { + /// A description of the samples associated with each Sample.value. + /// For a cpu profile this might be: + /// \[["cpu","nanoseconds"]\] or \[["wall","seconds"]\] or \[["syscall","count"]\] + /// For a heap profile, this might be: + /// \[["allocations","count"\], \["space","bytes"]\], + /// If one of the values represents the number of events represented + /// by the sample, by convention it should be at index 0 and use + /// sample_type.unit == "count". + #[prost(message, repeated, tag = "1")] + pub sample_type: ::prost::alloc::vec::Vec, + /// The set of samples recorded in this profile. + #[prost(message, repeated, tag = "2")] + pub sample: ::prost::alloc::vec::Vec, + /// References to locations in ProfilesData.location_table. + #[prost(int32, repeated, tag = "3")] + pub location_indices: ::prost::alloc::vec::Vec, + /// Time of collection (UTC) represented as nanoseconds past the epoch. + #[prost(int64, tag = "4")] + #[cfg_attr( + feature = "with-serde", + serde( + serialize_with = "crate::proto::serializers::serialize_u64_to_string", + deserialize_with = "crate::proto::serializers::deserialize_string_to_u64" + ) + )] + pub time_nanos: i64, + /// Duration of the profile, if a duration makes sense. + #[prost(int64, tag = "5")] + pub duration_nanos: i64, + /// The kind of events between sampled occurrences. + /// e.g \[ "cpu","cycles" \] or \[ "heap","bytes" \] + #[prost(message, optional, tag = "6")] + pub period_type: ::core::option::Option, + /// The number of events between sampled occurrences. + #[prost(int64, tag = "7")] + pub period: i64, + /// Free-form text associated with the profile. The text is displayed as is + /// to the user by the tools that read profiles (e.g. by pprof). This field + /// should not be used to store any machine-readable information, it is only + /// for human-friendly content. The profile must stay functional if this field + /// is cleaned. + /// + /// Indices into ProfilesData.string_table. + #[prost(int32, repeated, tag = "8")] + pub comment_strindices: ::prost::alloc::vec::Vec, + /// Index into the sample_type array to the default sample type. + #[prost(int32, tag = "9")] + pub default_sample_type_index: i32, + /// A globally unique identifier for a profile. The ID is a 16-byte array. An ID with + /// all zeroes is considered invalid. + /// + /// This field is required. + #[prost(bytes = "vec", tag = "10")] + #[cfg_attr( + feature = "with-serde", + serde( + serialize_with = "crate::proto::serializers::serialize_to_hex_string", + deserialize_with = "crate::proto::serializers::deserialize_from_hex_string" + ) + )] + pub profile_id: ::prost::alloc::vec::Vec, + /// dropped_attributes_count is the number of attributes that were discarded. Attributes + /// can be discarded because their keys are too long or because there are too many + /// attributes. If this value is 0, then no attributes were dropped. + #[prost(uint32, tag = "11")] + pub dropped_attributes_count: u32, + /// Specifies format of the original payload. Common values are defined in semantic conventions. \[required if original_payload is present\] + #[prost(string, tag = "12")] + pub original_payload_format: ::prost::alloc::string::String, + /// Original payload can be stored in this field. This can be useful for users who want to get the original payload. + /// Formats such as JFR are highly extensible and can contain more information than what is defined in this spec. + /// Inclusion of original payload should be configurable by the user. Default behavior should be to not include the original payload. + /// If the original payload is in pprof format, it SHOULD not be included in this field. + /// The field is optional, however if it is present then equivalent converted data should be populated in other fields + /// of this message as far as is practicable. + #[prost(bytes = "vec", tag = "13")] + pub original_payload: ::prost::alloc::vec::Vec, + /// References to attributes in attribute_table. \[optional\] + /// It is a collection of key/value pairs. Note, global attributes + /// like server name can be set using the resource API. Examples of attributes: + /// + /// "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" + /// "/http/server_latency": 300 + /// "abc.com/myattribute": true + /// "abc.com/score": 10.239 + /// + /// The OpenTelemetry API specification further restricts the allowed value types: + /// + /// Attribute keys MUST be unique (it is not allowed to have more than one + /// attribute with the same key). + #[prost(int32, repeated, tag = "14")] + pub attribute_indices: ::prost::alloc::vec::Vec, +} +/// Represents a mapping between Attribute Keys and Units. +#[cfg_attr(feature = "with-schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "with-serde", serde(rename_all = "camelCase"))] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct AttributeUnit { + /// Index into string table. + #[prost(int32, tag = "1")] + pub attribute_key_strindex: i32, + /// Index into string table. + #[prost(int32, tag = "2")] + pub unit_strindex: i32, +} +/// A pointer from a profile Sample to a trace Span. +/// Connects a profile sample to a trace span, identified by unique trace and span IDs. +#[cfg_attr(feature = "with-schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "with-serde", serde(rename_all = "camelCase"))] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Link { + /// A unique identifier of a trace that this linked span is part of. The ID is a + /// 16-byte array. + #[prost(bytes = "vec", tag = "1")] + pub trace_id: ::prost::alloc::vec::Vec, + /// A unique identifier for the linked span. The ID is an 8-byte array. + #[prost(bytes = "vec", tag = "2")] + pub span_id: ::prost::alloc::vec::Vec, +} +/// ValueType describes the type and units of a value, with an optional aggregation temporality. +#[cfg_attr(feature = "with-schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "with-serde", serde(rename_all = "camelCase"))] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct ValueType { + /// Index into ProfilesData.string_table. + #[prost(int32, tag = "1")] + pub type_strindex: i32, + /// Index into ProfilesData.string_table. + #[prost(int32, tag = "2")] + pub unit_strindex: i32, + #[prost(enumeration = "AggregationTemporality", tag = "3")] + pub aggregation_temporality: i32, +} +/// Each Sample records values encountered in some program +/// context. The program context is typically a stack trace, perhaps +/// augmented with auxiliary information like the thread-id, some +/// indicator of a higher level request being handled etc. +#[cfg_attr(feature = "with-schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "with-serde", serde(rename_all = "camelCase"))] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Sample { + /// locations_start_index along with locations_length refers to to a slice of locations in Profile.location_indices. + #[prost(int32, tag = "1")] + pub locations_start_index: i32, + /// locations_length along with locations_start_index refers to a slice of locations in Profile.location_indices. + /// Supersedes location_index. + #[prost(int32, tag = "2")] + pub locations_length: i32, + /// The type and unit of each value is defined by the corresponding + /// entry in Profile.sample_type. All samples must have the same + /// number of values, the same as the length of Profile.sample_type. + /// When aggregating multiple samples into a single sample, the + /// result has a list of values that is the element-wise sum of the + /// lists of the originals. + #[prost(int64, repeated, tag = "3")] + pub value: ::prost::alloc::vec::Vec, + /// References to attributes in ProfilesData.attribute_table. \[optional\] + #[prost(int32, repeated, tag = "4")] + pub attribute_indices: ::prost::alloc::vec::Vec, + /// Reference to link in ProfilesData.link_table. \[optional\] + #[prost(int32, optional, tag = "5")] + pub link_index: ::core::option::Option, + /// Timestamps associated with Sample represented in nanoseconds. These timestamps are expected + /// to fall within the Profile's time range. \[optional\] + #[prost(uint64, repeated, tag = "6")] + #[cfg_attr( + feature = "with-serde", + serde( + serialize_with = "crate::proto::serializers::serialize_u64_to_string", + deserialize_with = "crate::proto::serializers::deserialize_string_to_u64" + ) + )] + pub timestamps_unix_nano: ::prost::alloc::vec::Vec, +} +/// Describes the mapping of a binary in memory, including its address range, +/// file offset, and metadata like build ID +#[cfg_attr(feature = "with-schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "with-serde", serde(rename_all = "camelCase"))] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Mapping { + /// Address at which the binary (or DLL) is loaded into memory. + #[prost(uint64, tag = "1")] + pub memory_start: u64, + /// The limit of the address range occupied by this mapping. + #[prost(uint64, tag = "2")] + pub memory_limit: u64, + /// Offset in the binary that corresponds to the first mapped address. + #[prost(uint64, tag = "3")] + pub file_offset: u64, + /// The object this entry is loaded from. This can be a filename on + /// disk for the main binary and shared libraries, or virtual + /// abstractions like "\[vdso\]". + /// + /// Index into ProfilesData.string_table. + #[prost(int32, tag = "4")] + pub filename_strindex: i32, + /// References to attributes in ProfilesData.attribute_table. \[optional\] + #[prost(int32, repeated, tag = "5")] + pub attribute_indices: ::prost::alloc::vec::Vec, + /// The following fields indicate the resolution of symbolic info. + #[prost(bool, tag = "6")] + pub has_functions: bool, + #[prost(bool, tag = "7")] + pub has_filenames: bool, + #[prost(bool, tag = "8")] + pub has_line_numbers: bool, + #[prost(bool, tag = "9")] + pub has_inline_frames: bool, +} +/// Describes function and line table debug information. +#[cfg_attr(feature = "with-schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "with-serde", serde(rename_all = "camelCase"))] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Location { + /// Reference to mapping in ProfilesData.mapping_table. + /// It can be unset if the mapping is unknown or not applicable for + /// this profile type. + #[prost(int32, optional, tag = "1")] + pub mapping_index: ::core::option::Option, + /// The instruction address for this location, if available. It + /// should be within \[Mapping.memory_start...Mapping.memory_limit\] + /// for the corresponding mapping. A non-leaf address may be in the + /// middle of a call instruction. It is up to display tools to find + /// the beginning of the instruction if necessary. + #[prost(uint64, tag = "2")] + pub address: u64, + /// Multiple line indicates this location has inlined functions, + /// where the last entry represents the caller into which the + /// preceding entries were inlined. + /// + /// E.g., if memcpy() is inlined into printf: + /// line\[0\].function_name == "memcpy" + /// line\[1\].function_name == "printf" + #[prost(message, repeated, tag = "3")] + pub line: ::prost::alloc::vec::Vec, + /// Provides an indication that multiple symbols map to this location's + /// address, for example due to identical code folding by the linker. In that + /// case the line information above represents one of the multiple + /// symbols. This field must be recomputed when the symbolization state of the + /// profile changes. + #[prost(bool, tag = "4")] + pub is_folded: bool, + /// References to attributes in ProfilesData.attribute_table. \[optional\] + #[prost(int32, repeated, tag = "5")] + pub attribute_indices: ::prost::alloc::vec::Vec, +} +/// Details a specific line in a source code, linked to a function. +#[cfg_attr(feature = "with-schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "with-serde", serde(rename_all = "camelCase"))] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct Line { + /// Reference to function in ProfilesData.function_table. + #[prost(int32, tag = "1")] + pub function_index: i32, + /// Line number in source code. 0 means unset. + #[prost(int64, tag = "2")] + pub line: i64, + /// Column number in source code. 0 means unset. + #[prost(int64, tag = "3")] + pub column: i64, +} +/// Describes a function, including its human-readable name, system name, +/// source file, and starting line number in the source. +#[cfg_attr(feature = "with-schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "with-serde", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "with-serde", serde(default))] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct Function { + /// Function name. Empty string if not available. + #[prost(int32, tag = "1")] + pub name_strindex: i32, + /// Function name, as identified by the system. For instance, + /// it can be a C++ mangled name. Empty string if not available. + #[prost(int32, tag = "2")] + pub system_name_strindex: i32, + /// Source file containing the function. Empty string if not available. + #[prost(int32, tag = "3")] + pub filename_strindex: i32, + /// Line number in source file. 0 means unset. + #[prost(int64, tag = "4")] + pub start_line: i64, +} +/// Specifies the method of aggregating metric values, either DELTA (change since last report) +/// or CUMULATIVE (total since a fixed start time). +#[cfg_attr(feature = "with-schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "with-serde", serde(rename_all = "camelCase"))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum AggregationTemporality { + /// UNSPECIFIED is the default AggregationTemporality, it MUST not be used. + Unspecified = 0, + /// * DELTA is an AggregationTemporality for a profiler which reports + /// changes since last report time. Successive metrics contain aggregation of + /// values from continuous and non-overlapping intervals. + /// + /// The values for a DELTA metric are based only on the time interval + /// associated with one measurement cycle. There is no dependency on + /// previous measurements like is the case for CUMULATIVE metrics. + /// + /// For example, consider a system measuring the number of requests that + /// it receives and reports the sum of these requests every second as a + /// DELTA metric: + /// + /// 1. The system starts receiving at time=t_0. + /// 2. A request is received, the system measures 1 request. + /// 3. A request is received, the system measures 1 request. + /// 4. A request is received, the system measures 1 request. + /// 5. The 1 second collection cycle ends. A metric is exported for the + /// number of requests received over the interval of time t_0 to + /// t_0+1 with a value of 3. + /// 6. A request is received, the system measures 1 request. + /// 7. A request is received, the system measures 1 request. + /// 8. The 1 second collection cycle ends. A metric is exported for the + /// number of requests received over the interval of time t_0+1 to + /// t_0+2 with a value of 2. + Delta = 1, + /// * CUMULATIVE is an AggregationTemporality for a profiler which + /// reports changes since a fixed start time. This means that current values + /// of a CUMULATIVE metric depend on all previous measurements since the + /// start time. Because of this, the sender is required to retain this state + /// in some form. If this state is lost or invalidated, the CUMULATIVE metric + /// values MUST be reset and a new fixed start time following the last + /// reported measurement time sent MUST be used. + /// + /// For example, consider a system measuring the number of requests that + /// it receives and reports the sum of these requests every second as a + /// CUMULATIVE metric: + /// + /// 1. The system starts receiving at time=t_0. + /// 2. A request is received, the system measures 1 request. + /// 3. A request is received, the system measures 1 request. + /// 4. A request is received, the system measures 1 request. + /// 5. The 1 second collection cycle ends. A metric is exported for the + /// number of requests received over the interval of time t_0 to + /// t_0+1 with a value of 3. + /// 6. A request is received, the system measures 1 request. + /// 7. A request is received, the system measures 1 request. + /// 8. The 1 second collection cycle ends. A metric is exported for the + /// number of requests received over the interval of time t_0 to + /// t_0+2 with a value of 5. + /// 9. The system experiences a fault and loses state. + /// 10. The system recovers and resumes receiving at time=t_1. + /// 11. A request is received, the system measures 1 request. + /// 12. The 1 second collection cycle ends. A metric is exported for the + /// number of requests received over the interval of time t_1 to + /// t_1+1 with a value of 1. + /// + /// Note: Even though, when reporting changes since last report time, using + /// CUMULATIVE is valid, it is not recommended. + Cumulative = 2, +} +impl AggregationTemporality { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "AGGREGATION_TEMPORALITY_UNSPECIFIED", + Self::Delta => "AGGREGATION_TEMPORALITY_DELTA", + Self::Cumulative => "AGGREGATION_TEMPORALITY_CUMULATIVE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "AGGREGATION_TEMPORALITY_UNSPECIFIED" => Some(Self::Unspecified), + "AGGREGATION_TEMPORALITY_DELTA" => Some(Self::Delta), + "AGGREGATION_TEMPORALITY_CUMULATIVE" => Some(Self::Cumulative), + _ => None, + } + } +} diff --git a/opentelemetry-proto/src/transform/profiles.rs b/opentelemetry-proto/src/transform/profiles.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/opentelemetry-proto/src/transform/profiles.rs @@ -0,0 +1 @@ + From 695fb9f11e6bf4ff63bbf6321f7835a5932e60d0 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Sun, 18 May 2025 11:49:16 -0400 Subject: [PATCH 4/6] fix --- opentelemetry-proto/src/proto.rs | 26 ++++++++++++++++++- ...ntelemetry.proto.profiles.v1development.rs | 8 +++--- opentelemetry-proto/tests/grpc_build.rs | 15 +++++++++-- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/opentelemetry-proto/src/proto.rs b/opentelemetry-proto/src/proto.rs index c74c102545..a21f6cef70 100644 --- a/opentelemetry-proto/src/proto.rs +++ b/opentelemetry-proto/src/proto.rs @@ -6,7 +6,7 @@ pub(crate) mod serializers { use crate::tonic::common::v1::any_value::{self, Value}; use crate::tonic::common::v1::AnyValue; use serde::de::{self, MapAccess, Visitor}; - use serde::ser::{SerializeMap, SerializeStruct}; + use serde::ser::{SerializeMap, SerializeSeq, SerializeStruct}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; @@ -174,6 +174,30 @@ pub(crate) mod serializers { s.parse::().map_err(de::Error::custom) } + pub fn serialize_vec_u64_to_string(value: &[u64], serializer: S) -> Result + where + S: Serializer, + { + let s = value.iter() + .map(|v| v.to_string()) + .collect::>(); + let mut sq = serializer.serialize_seq(Some(s.len()))?; + for v in value { + sq.serialize_element(&v.to_string())?; + } + sq.end() + } + + pub fn deserialize_vec_string_to_vec_u64<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let s: Vec = Deserialize::deserialize(deserializer)?; + s.into_iter() + .map(|v| v.parse::().map_err(de::Error::custom)) + .collect() + } + pub fn serialize_i64_to_string(value: &i64, serializer: S) -> Result where S: Serializer, diff --git a/opentelemetry-proto/src/proto/tonic/opentelemetry.proto.profiles.v1development.rs b/opentelemetry-proto/src/proto/tonic/opentelemetry.proto.profiles.v1development.rs index 58e2646638..5851887f11 100644 --- a/opentelemetry-proto/src/proto/tonic/opentelemetry.proto.profiles.v1development.rs +++ b/opentelemetry-proto/src/proto/tonic/opentelemetry.proto.profiles.v1development.rs @@ -124,8 +124,8 @@ pub struct Profile { #[cfg_attr( feature = "with-serde", serde( - serialize_with = "crate::proto::serializers::serialize_u64_to_string", - deserialize_with = "crate::proto::serializers::deserialize_string_to_u64" + serialize_with = "crate::proto::serializers::serialize_i64_to_string", + deserialize_with = "crate::proto::serializers::deserialize_string_to_i64" ) )] pub time_nanos: i64, @@ -275,8 +275,8 @@ pub struct Sample { #[cfg_attr( feature = "with-serde", serde( - serialize_with = "crate::proto::serializers::serialize_u64_to_string", - deserialize_with = "crate::proto::serializers::deserialize_string_to_u64" + serialize_with = "crate::proto::serializers::serialize_vec_u64_to_string", + deserialize_with = "crate::proto::serializers::deserialize_vec_string_to_vec_u64" ) )] pub timestamps_unix_nano: ::prost::alloc::vec::Vec, diff --git a/opentelemetry-proto/tests/grpc_build.rs b/opentelemetry-proto/tests/grpc_build.rs index aa9d77993c..2c9acaf9c0 100644 --- a/opentelemetry-proto/tests/grpc_build.rs +++ b/opentelemetry-proto/tests/grpc_build.rs @@ -109,12 +109,23 @@ fn build_tonic() { "metrics.v1.HistogramDataPoint.time_unix_nano", "metrics.v1.NumberDataPoint.start_time_unix_nano", "metrics.v1.NumberDataPoint.time_unix_nano", - "profiles.v1development.Sample.timestamps_unix_nano", - "profiles.v1development.Profile.time_nanos", ] { builder = builder .field_attribute(path, "#[cfg_attr(feature = \"with-serde\", serde(serialize_with = \"crate::proto::serializers::serialize_u64_to_string\", deserialize_with = \"crate::proto::serializers::deserialize_string_to_u64\"))]") } + for path in [ + "profiles.v1development.Profile.time_nanos", + ] { + builder = builder + .field_attribute(path, "#[cfg_attr(feature = \"with-serde\", serde(serialize_with = \"crate::proto::serializers::serialize_i64_to_string\", deserialize_with = \"crate::proto::serializers::deserialize_string_to_i64\"))]") + } + for path in [ + "profiles.v1development.Sample.timestamps_unix_nano", + ] { + builder = builder + .field_attribute(path, "#[cfg_attr(feature = \"with-serde\", serde(serialize_with = \"crate::proto::serializers::serialize_vec_u64_to_string\", deserialize_with = \"crate::proto::serializers::deserialize_vec_string_to_vec_u64\"))]") + } + // special serializer and deserializer for value // The Value::value field must be hidden From 99cb92140f574cf4cb4bdf988f0f2b8653994eb4 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Mon, 19 May 2025 07:49:13 -0700 Subject: [PATCH 5/6] add changelog --- opentelemetry-proto/tests/grpc_build.rs | 9 ++------- opentelemetry-sdk/CHANGELOG.md | 2 ++ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/opentelemetry-proto/tests/grpc_build.rs b/opentelemetry-proto/tests/grpc_build.rs index 2c9acaf9c0..45d7faf4f9 100644 --- a/opentelemetry-proto/tests/grpc_build.rs +++ b/opentelemetry-proto/tests/grpc_build.rs @@ -113,20 +113,15 @@ fn build_tonic() { builder = builder .field_attribute(path, "#[cfg_attr(feature = \"with-serde\", serde(serialize_with = \"crate::proto::serializers::serialize_u64_to_string\", deserialize_with = \"crate::proto::serializers::deserialize_string_to_u64\"))]") } - for path in [ - "profiles.v1development.Profile.time_nanos", - ] { + for path in ["profiles.v1development.Profile.time_nanos"] { builder = builder .field_attribute(path, "#[cfg_attr(feature = \"with-serde\", serde(serialize_with = \"crate::proto::serializers::serialize_i64_to_string\", deserialize_with = \"crate::proto::serializers::deserialize_string_to_i64\"))]") } - for path in [ - "profiles.v1development.Sample.timestamps_unix_nano", - ] { + for path in ["profiles.v1development.Sample.timestamps_unix_nano"] { builder = builder .field_attribute(path, "#[cfg_attr(feature = \"with-serde\", serde(serialize_with = \"crate::proto::serializers::serialize_vec_u64_to_string\", deserialize_with = \"crate::proto::serializers::deserialize_vec_string_to_vec_u64\"))]") } - // special serializer and deserializer for value // The Value::value field must be hidden builder = builder diff --git a/opentelemetry-sdk/CHANGELOG.md b/opentelemetry-sdk/CHANGELOG.md index 55d4853042..2409f1bd97 100644 --- a/opentelemetry-sdk/CHANGELOG.md +++ b/opentelemetry-sdk/CHANGELOG.md @@ -2,6 +2,8 @@ ## vNext +- **Feature**: Added Rust code generation for profiles protos. [#2979](https://github.com/open-telemetry/opentelemetry-rust/pull/2979) + - **Feature**: Added context based telemetry suppression. [#2868](https://github.com/open-telemetry/opentelemetry-rust/pull/2868) - `SdkLogger`, `SdkTracer` modified to respect telemetry suppression based on `Context`. In other words, if the current context has telemetry suppression From a9930880a21fd6a24275ae2d21655c1fbac0305c Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Tue, 20 May 2025 10:26:38 -0700 Subject: [PATCH 6/6] move to proto changelog --- opentelemetry-proto/CHANGELOG.md | 1 + opentelemetry-sdk/CHANGELOG.md | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/opentelemetry-proto/CHANGELOG.md b/opentelemetry-proto/CHANGELOG.md index e53c832369..3c97586aed 100644 --- a/opentelemetry-proto/CHANGELOG.md +++ b/opentelemetry-proto/CHANGELOG.md @@ -2,6 +2,7 @@ ## vNext +- **Feature**: Added Rust code generation for profiles protos. [#2979](https://github.com/open-telemetry/opentelemetry-rust/pull/2979) - Update `tonic` dependency version to 0.13 - - Update proto definitions to v1.6.0. diff --git a/opentelemetry-sdk/CHANGELOG.md b/opentelemetry-sdk/CHANGELOG.md index 2409f1bd97..55d4853042 100644 --- a/opentelemetry-sdk/CHANGELOG.md +++ b/opentelemetry-sdk/CHANGELOG.md @@ -2,8 +2,6 @@ ## vNext -- **Feature**: Added Rust code generation for profiles protos. [#2979](https://github.com/open-telemetry/opentelemetry-rust/pull/2979) - - **Feature**: Added context based telemetry suppression. [#2868](https://github.com/open-telemetry/opentelemetry-rust/pull/2868) - `SdkLogger`, `SdkTracer` modified to respect telemetry suppression based on `Context`. In other words, if the current context has telemetry suppression