diff --git a/Cargo.lock b/Cargo.lock index 6b68633c9..0567810b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3264,6 +3264,7 @@ dependencies = [ "serde", "serde_json", "tokio", + "tonic-build", "tracing", "typeshare-core", "url", diff --git a/fpx/Cargo.toml b/fpx/Cargo.toml index c7d7a9220..d90c5e174 100644 --- a/fpx/Cargo.toml +++ b/fpx/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" authors = { workspace = true } license = { workspace = true } repository = { workspace = true } -build = "build.rs" [features] embed-studio = [] # When enabled it will embed Studio from frontend/dist diff --git a/fpx/build.rs b/fpx/build.rs deleted file mode 100644 index 48a0020bb..000000000 --- a/fpx/build.rs +++ /dev/null @@ -1,24 +0,0 @@ -fn main() -> Result<(), Box> { - // tonic_build::compile_protos( - // "../opentelemetry-proto/opentelemetry/proto/collector/trace/v1/trace_service.proto", - // )?; - - // tonic_build::configure() - // .build_server(true) - // .build_client(false) - // .emit_rerun_if_changed(true) - // .include_file("../open-telemetry-proto/opentelemetry/proto") - // .proto_path("../opentelemetry-proto/opentelemetry/proto/collector/trace/v1/trace_service.proto") - // .comp - - tonic_build::configure() - .include_file("opentelemetry_proto.rs") - .build_client(false) - .build_server(true) - .compile( - &["../opentelemetry-proto/opentelemetry/proto/collector/trace/v1/trace_service.proto"], - &["../opentelemetry-proto"], - )?; - - Ok(()) -} diff --git a/fpx/src/api/grpc.rs b/fpx/src/api/grpc.rs index bfad1a12c..dbd223b3a 100644 --- a/fpx/src/api/grpc.rs +++ b/fpx/src/api/grpc.rs @@ -1,14 +1,10 @@ use crate::data::{DbError, Store}; use crate::events::ServerEvents; -use crate::models::TraceAdded; -use protos::opentelemetry::proto::collector::trace::v1::trace_service_server::TraceService; -use protos::opentelemetry::proto::collector::trace::v1::{ +use crate::models::otel::opentelemetry::proto::collector::trace::v1::trace_service_server::TraceService; +use crate::models::otel::opentelemetry::proto::collector::trace::v1::{ ExportTraceServiceRequest, ExportTraceServiceResponse, }; - -pub mod protos { - tonic::include_proto!("opentelemetry_proto"); -} +use crate::models::TraceAdded; #[derive(Clone)] pub struct GrpcService { diff --git a/fpx/src/commands/dev.rs b/fpx/src/commands/dev.rs index 6faaded50..5d45877ff 100644 --- a/fpx/src/commands/dev.rs +++ b/fpx/src/commands/dev.rs @@ -1,10 +1,10 @@ -use crate::api::grpc::protos::opentelemetry::proto::collector::trace::v1::trace_service_server::TraceServiceServer; use crate::api::grpc::GrpcService; use crate::api::{self}; use crate::data::migrations::migrate; use crate::data::{DataPath, Store}; use crate::events::Events; use crate::initialize_fpx_dir; +use crate::models::otel::opentelemetry::proto::collector::trace::v1::trace_service_server::TraceServiceServer; use anyhow::{Context, Result}; use std::future::IntoFuture; use std::{path::PathBuf, process::exit}; diff --git a/fpx/src/models.rs b/fpx/src/models.rs index 2d727b764..64434bcd3 100644 --- a/fpx/src/models.rs +++ b/fpx/src/models.rs @@ -1,9 +1,10 @@ +use rand::Rng; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; - -use rand::Rng; use std::collections::BTreeMap; +pub mod otel; + pub const FPX_WEBSOCKET_ID_HEADER: &str = "fpx-websocket-id"; /// Messages that are send from the server to the client. diff --git a/fpx/src/models/otel/mod.rs b/fpx/src/models/otel/mod.rs new file mode 100644 index 000000000..1bc7bd35b --- /dev/null +++ b/fpx/src/models/otel/mod.rs @@ -0,0 +1,27 @@ +// This file is @generated by prost-build. +pub mod opentelemetry { + pub mod proto { + pub mod collector { + pub mod trace { + pub mod v1 { + include!("opentelemetry.proto.collector.trace.v1.rs"); + } + } + } + pub mod common { + pub mod v1 { + include!("opentelemetry.proto.common.v1.rs"); + } + } + pub mod resource { + pub mod v1 { + include!("opentelemetry.proto.resource.v1.rs"); + } + } + pub mod trace { + pub mod v1 { + include!("opentelemetry.proto.trace.v1.rs"); + } + } + } +} diff --git a/fpx/src/models/otel/opentelemetry.proto.collector.trace.v1.rs b/fpx/src/models/otel/opentelemetry.proto.collector.trace.v1.rs new file mode 100644 index 000000000..a1701d739 --- /dev/null +++ b/fpx/src/models/otel/opentelemetry.proto.collector.trace.v1.rs @@ -0,0 +1,240 @@ +// This file is @generated by prost-build. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExportTraceServiceRequest { + /// An array of ResourceSpans. + /// For data coming from a single resource this array will typically contain one + /// element. Intermediary nodes (such as OpenTelemetry Collector) that receive + /// data from multiple origins typically batch the data before forwarding further and + /// in that case this array will contain multiple elements. + #[prost(message, repeated, tag = "1")] + pub resource_spans: ::prost::alloc::vec::Vec< + super::super::super::trace::v1::ResourceSpans, + >, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExportTraceServiceResponse { + /// The details of a partially successful export request. + /// + /// If the request is only partially accepted + /// (i.e. when the server accepts only parts of the data and rejects the rest) + /// the server MUST initialize the `partial_success` field and MUST + /// set the `rejected_` with the number of items it rejected. + /// + /// Servers MAY also make use of the `partial_success` field to convey + /// warnings/suggestions to senders even when the request was fully accepted. + /// In such cases, the `rejected_` MUST have a value of `0` and + /// the `error_message` MUST be non-empty. + /// + /// A `partial_success` message with an empty value (rejected_ = 0 and + /// `error_message` = "") is equivalent to it not being set/present. Senders + /// SHOULD interpret it the same way as in the full success case. + #[prost(message, optional, tag = "1")] + pub partial_success: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExportTracePartialSuccess { + /// The number of rejected spans. + /// + /// A `rejected_` field holding a `0` value indicates that the + /// request was fully accepted. + #[prost(int64, tag = "1")] + pub rejected_spans: i64, + /// A developer-facing human-readable message in English. It should be used + /// either to explain why the server rejected parts of the data during a partial + /// success or to convey warnings/suggestions during a full success. The message + /// should offer guidance on how users can address such issues. + /// + /// error_message is an optional field. An error_message with an empty value + /// is equivalent to it not being set. + #[prost(string, tag = "2")] + pub error_message: ::prost::alloc::string::String, +} +/// Generated server implementations. +pub mod trace_service_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with TraceServiceServer. + #[async_trait] + pub trait TraceService: Send + Sync + 'static { + /// For performance reasons, it is recommended to keep this RPC + /// alive for the entire life of the application. + async fn export( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + /// Service that can be used to push spans between one Application instrumented with + /// OpenTelemetry and a collector, or between a collector and a central collector (in this + /// case spans are sent/received to/from multiple Applications). + #[derive(Debug)] + pub struct TraceServiceServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl TraceServiceServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for TraceServiceServer + where + T: TraceService, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/opentelemetry.proto.collector.trace.v1.TraceService/Export" => { + #[allow(non_camel_case_types)] + struct ExportSvc(pub Arc); + impl< + T: TraceService, + > tonic::server::UnaryService + for ExportSvc { + type Response = super::ExportTraceServiceResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::export(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ExportSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for TraceServiceServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for TraceServiceServer { + const NAME: &'static str = "opentelemetry.proto.collector.trace.v1.TraceService"; + } +} diff --git a/fpx/src/models/otel/opentelemetry.proto.common.v1.rs b/fpx/src/models/otel/opentelemetry.proto.common.v1.rs new file mode 100644 index 000000000..4fbfaf954 --- /dev/null +++ b/fpx/src/models/otel/opentelemetry.proto.common.v1.rs @@ -0,0 +1,87 @@ +// This file is @generated by prost-build. +/// AnyValue is used to represent any type of attribute value. AnyValue may contain a +/// primitive value such as a string or integer or it may contain an arbitrary nested +/// object containing arrays, key-value lists and primitives. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AnyValue { + /// The value is one of the listed fields. It is valid for all values to be unspecified + /// in which case this AnyValue is considered to be "empty". + #[prost(oneof = "any_value::Value", tags = "1, 2, 3, 4, 5, 6, 7")] + pub value: ::core::option::Option, +} +/// Nested message and enum types in `AnyValue`. +pub mod any_value { + /// The value is one of the listed fields. It is valid for all values to be unspecified + /// in which case this AnyValue is considered to be "empty". + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Value { + #[prost(string, tag = "1")] + StringValue(::prost::alloc::string::String), + #[prost(bool, tag = "2")] + BoolValue(bool), + #[prost(int64, tag = "3")] + IntValue(i64), + #[prost(double, tag = "4")] + DoubleValue(f64), + #[prost(message, tag = "5")] + ArrayValue(super::ArrayValue), + #[prost(message, tag = "6")] + KvlistValue(super::KeyValueList), + #[prost(bytes, tag = "7")] + BytesValue(::prost::alloc::vec::Vec), + } +} +/// ArrayValue is a list of AnyValue messages. We need ArrayValue as a message +/// since oneof in AnyValue does not allow repeated fields. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ArrayValue { + /// Array of values. The array may be empty (contain 0 elements). + #[prost(message, repeated, tag = "1")] + pub values: ::prost::alloc::vec::Vec, +} +/// KeyValueList is a list of KeyValue messages. We need KeyValueList as a message +/// since `oneof` in AnyValue does not allow repeated fields. Everywhere else where we need +/// a list of KeyValue messages (e.g. in Span) we use `repeated KeyValue` directly to +/// avoid unnecessary extra wrapping (which slows down the protocol). The 2 approaches +/// are semantically equivalent. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct KeyValueList { + /// A collection of key/value pairs of key-value pairs. The list may be empty (may + /// contain 0 elements). + /// The keys MUST be unique (it is not allowed to have more than one + /// value with the same key). + #[prost(message, repeated, tag = "1")] + pub values: ::prost::alloc::vec::Vec, +} +/// KeyValue is a key-value pair that is used to store Span attributes, Link +/// attributes, etc. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct KeyValue { + #[prost(string, tag = "1")] + pub key: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub value: ::core::option::Option, +} +/// InstrumentationScope is a message representing the instrumentation scope information +/// such as the fully qualified name and version. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct InstrumentationScope { + /// An empty instrumentation scope name means the name is unknown. + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub version: ::prost::alloc::string::String, + /// Additional attributes that describe the scope. \[Optional\]. + /// Attribute keys MUST be unique (it is not allowed to have more than one + /// attribute with the same key). + #[prost(message, repeated, tag = "3")] + pub attributes: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "4")] + pub dropped_attributes_count: u32, +} diff --git a/fpx/src/models/otel/opentelemetry.proto.resource.v1.rs b/fpx/src/models/otel/opentelemetry.proto.resource.v1.rs new file mode 100644 index 000000000..ce317b666 --- /dev/null +++ b/fpx/src/models/otel/opentelemetry.proto.resource.v1.rs @@ -0,0 +1,15 @@ +// This file is @generated by prost-build. +/// Resource information. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Resource { + /// Set of attributes that describe the resource. + /// Attribute keys MUST be unique (it is not allowed to have more than one + /// attribute with the same key). + #[prost(message, repeated, tag = "1")] + pub attributes: ::prost::alloc::vec::Vec, + /// dropped_attributes_count is the number of dropped attributes. If the value is 0, then + /// no attributes were dropped. + #[prost(uint32, tag = "2")] + pub dropped_attributes_count: u32, +} diff --git a/fpx/src/models/otel/opentelemetry.proto.trace.v1.rs b/fpx/src/models/otel/opentelemetry.proto.trace.v1.rs new file mode 100644 index 000000000..c80917c2c --- /dev/null +++ b/fpx/src/models/otel/opentelemetry.proto.trace.v1.rs @@ -0,0 +1,438 @@ +// This file is @generated by prost-build. +/// TracesData represents the traces data that can be stored in a persistent storage, +/// OR can be embedded by other protocols that transfer OTLP traces 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. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TracesData { + /// An array of ResourceSpans. + /// For data coming from a single resource this array will typically contain + /// one element. Intermediary nodes that receive data from multiple origins + /// typically batch the data before forwarding further and in that case this + /// array will contain multiple elements. + #[prost(message, repeated, tag = "1")] + pub resource_spans: ::prost::alloc::vec::Vec, +} +/// A collection of ScopeSpans from a Resource. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ResourceSpans { + /// The resource for the spans 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 ScopeSpans that originate from a resource. + #[prost(message, repeated, tag = "2")] + pub scope_spans: ::prost::alloc::vec::Vec, + /// The Schema URL, if known. This is the identifier of the Schema that the resource data + /// is recorded in. 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_spans" field which have their own schema_url field. + #[prost(string, tag = "3")] + pub schema_url: ::prost::alloc::string::String, +} +/// A collection of Spans produced by an InstrumentationScope. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ScopeSpans { + /// The instrumentation scope information for the spans 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 Spans that originate from an instrumentation scope. + #[prost(message, repeated, tag = "2")] + pub spans: ::prost::alloc::vec::Vec, + /// The Schema URL, if known. This is the identifier of the Schema that the span data + /// is recorded in. To learn more about Schema URL see + /// + /// This schema_url applies to all spans and span events in the "spans" field. + #[prost(string, tag = "3")] + pub schema_url: ::prost::alloc::string::String, +} +/// A Span represents a single operation performed by a single component of the system. +/// +/// The next available field id is 17. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Span { + /// A unique identifier for a trace. All spans from the same trace share + /// the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR + /// of length other than 16 bytes is considered invalid (empty string in OTLP/JSON + /// is zero-length and thus is also invalid). + /// + /// This field is required. + #[prost(bytes = "vec", tag = "1")] + pub trace_id: ::prost::alloc::vec::Vec, + /// A unique identifier for a span within a trace, assigned when the span + /// is created. The ID is an 8-byte array. An ID with all zeroes OR of length + /// other than 8 bytes is considered invalid (empty string in OTLP/JSON + /// is zero-length and thus is also invalid). + /// + /// This field is required. + #[prost(bytes = "vec", tag = "2")] + pub span_id: ::prost::alloc::vec::Vec, + /// trace_state conveys information about request position in multiple distributed tracing graphs. + /// It is a trace_state in w3c-trace-context format: + /// See also for more details about this field. + #[prost(string, tag = "3")] + pub trace_state: ::prost::alloc::string::String, + /// The `span_id` of this span's parent span. If this is a root span, then this + /// field must be empty. The ID is an 8-byte array. + #[prost(bytes = "vec", tag = "4")] + pub parent_span_id: ::prost::alloc::vec::Vec, + /// Flags, a bit field. + /// + /// Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace + /// Context specification. To read the 8-bit W3C trace flag, use + /// `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. + /// + /// See for the flag definitions. + /// + /// Bits 8 and 9 represent the 3 states of whether a span's parent + /// is remote. The states are (unknown, is not remote, is remote). + /// To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. + /// To read whether the span is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. + /// + /// When creating span messages, if the message is logically forwarded from another source + /// with an equivalent flags fields (i.e., usually another OTLP span message), the field SHOULD + /// be copied as-is. If creating from a source that does not have an equivalent flags field + /// (such as a runtime representation of an OpenTelemetry span), the high 22 bits MUST + /// be set to zero. + /// Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. + /// + /// \[Optional\]. + #[prost(fixed32, tag = "16")] + pub flags: u32, + /// A description of the span's operation. + /// + /// For example, the name can be a qualified method name or a file name + /// and a line number where the operation is called. A best practice is to use + /// the same display name at the same call point in an application. + /// This makes it easier to correlate spans in different traces. + /// + /// This field is semantically required to be set to non-empty string. + /// Empty value is equivalent to an unknown span name. + /// + /// This field is required. + #[prost(string, tag = "5")] + pub name: ::prost::alloc::string::String, + /// Distinguishes between spans generated in a particular context. For example, + /// two spans with the same name may be distinguished using `CLIENT` (caller) + /// and `SERVER` (callee) to identify queueing latency associated with the span. + #[prost(enumeration = "span::SpanKind", tag = "6")] + pub kind: i32, + /// start_time_unix_nano is the start time of the span. On the client side, this is the time + /// kept by the local machine where the span execution starts. On the server side, this + /// is the time when the server's application handler starts running. + /// Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + /// + /// This field is semantically required and it is expected that end_time >= start_time. + #[prost(fixed64, tag = "7")] + pub start_time_unix_nano: u64, + /// end_time_unix_nano is the end time of the span. On the client side, this is the time + /// kept by the local machine where the span execution ends. On the server side, this + /// is the time when the server application handler stops running. + /// Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + /// + /// This field is semantically required and it is expected that end_time >= start_time. + #[prost(fixed64, tag = "8")] + pub end_time_unix_nano: u64, + /// attributes 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 + /// "example.com/myattribute": true + /// "example.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(message, repeated, tag = "9")] + pub attributes: ::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 = "10")] + pub dropped_attributes_count: u32, + /// events is a collection of Event items. + #[prost(message, repeated, tag = "11")] + pub events: ::prost::alloc::vec::Vec, + /// dropped_events_count is the number of dropped events. If the value is 0, then no + /// events were dropped. + #[prost(uint32, tag = "12")] + pub dropped_events_count: u32, + /// links is a collection of Links, which are references from this span to a span + /// in the same or different trace. + #[prost(message, repeated, tag = "13")] + pub links: ::prost::alloc::vec::Vec, + /// dropped_links_count is the number of dropped links after the maximum size was + /// enforced. If this value is 0, then no links were dropped. + #[prost(uint32, tag = "14")] + pub dropped_links_count: u32, + /// An optional final status for this span. Semantically when Status isn't set, it means + /// span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0). + #[prost(message, optional, tag = "15")] + pub status: ::core::option::Option, +} +/// Nested message and enum types in `Span`. +pub mod span { + /// Event is a time-stamped annotation of the span, consisting of user-supplied + /// text description and key-value pairs. + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Event { + /// time_unix_nano is the time the event occurred. + #[prost(fixed64, tag = "1")] + pub time_unix_nano: u64, + /// name of the event. + /// This field is semantically required to be set to non-empty string. + #[prost(string, tag = "2")] + pub name: ::prost::alloc::string::String, + /// attributes is a collection of attribute key/value pairs on the event. + /// Attribute keys MUST be unique (it is not allowed to have more than one + /// attribute with the same key). + #[prost(message, repeated, tag = "3")] + pub attributes: ::prost::alloc::vec::Vec< + super::super::super::common::v1::KeyValue, + >, + /// dropped_attributes_count is the number of dropped attributes. If the value is 0, + /// then no attributes were dropped. + #[prost(uint32, tag = "4")] + pub dropped_attributes_count: u32, + } + /// A pointer from the current span to another span in the same trace or in a + /// different trace. For example, this can be used in batching operations, + /// where a single batch handler processes multiple requests from different + /// traces or when the handler receives a request from a different project. + #[allow(clippy::derive_partial_eq_without_eq)] + #[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, + /// The trace_state associated with the link. + #[prost(string, tag = "3")] + pub trace_state: ::prost::alloc::string::String, + /// attributes is a collection of attribute key/value pairs on the link. + /// Attribute keys MUST be unique (it is not allowed to have more than one + /// attribute with the same key). + #[prost(message, repeated, tag = "4")] + pub attributes: ::prost::alloc::vec::Vec< + super::super::super::common::v1::KeyValue, + >, + /// dropped_attributes_count is the number of dropped attributes. If the value is 0, + /// then no attributes were dropped. + #[prost(uint32, tag = "5")] + pub dropped_attributes_count: u32, + /// Flags, a bit field. + /// + /// Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace + /// Context specification. To read the 8-bit W3C trace flag, use + /// `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. + /// + /// See for the flag definitions. + /// + /// Bits 8 and 9 represent the 3 states of whether the link is remote. + /// The states are (unknown, is not remote, is remote). + /// To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. + /// To read whether the link is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. + /// + /// Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. + /// When creating new spans, bits 10-31 (most-significant 22-bits) MUST be zero. + /// + /// \[Optional\]. + #[prost(fixed32, tag = "6")] + pub flags: u32, + } + /// SpanKind is the type of span. Can be used to specify additional relationships between spans + /// in addition to a parent/child relationship. + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum SpanKind { + /// Unspecified. Do NOT use as default. + /// Implementations MAY assume SpanKind to be INTERNAL when receiving UNSPECIFIED. + Unspecified = 0, + /// Indicates that the span represents an internal operation within an application, + /// as opposed to an operation happening at the boundaries. Default value. + Internal = 1, + /// Indicates that the span covers server-side handling of an RPC or other + /// remote network request. + Server = 2, + /// Indicates that the span describes a request to some remote service. + Client = 3, + /// Indicates that the span describes a producer sending a message to a broker. + /// Unlike CLIENT and SERVER, there is often no direct critical path latency relationship + /// between producer and consumer spans. A PRODUCER span ends when the message was accepted + /// by the broker while the logical processing of the message might span a much longer time. + Producer = 4, + /// Indicates that the span describes consumer receiving a message from a broker. + /// Like the PRODUCER kind, there is often no direct critical path latency relationship + /// between producer and consumer spans. + Consumer = 5, + } + impl SpanKind { + /// 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 { + SpanKind::Unspecified => "SPAN_KIND_UNSPECIFIED", + SpanKind::Internal => "SPAN_KIND_INTERNAL", + SpanKind::Server => "SPAN_KIND_SERVER", + SpanKind::Client => "SPAN_KIND_CLIENT", + SpanKind::Producer => "SPAN_KIND_PRODUCER", + SpanKind::Consumer => "SPAN_KIND_CONSUMER", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SPAN_KIND_UNSPECIFIED" => Some(Self::Unspecified), + "SPAN_KIND_INTERNAL" => Some(Self::Internal), + "SPAN_KIND_SERVER" => Some(Self::Server), + "SPAN_KIND_CLIENT" => Some(Self::Client), + "SPAN_KIND_PRODUCER" => Some(Self::Producer), + "SPAN_KIND_CONSUMER" => Some(Self::Consumer), + _ => None, + } + } + } +} +/// The Status type defines a logical error model that is suitable for different +/// programming environments, including REST APIs and RPC APIs. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Status { + /// A developer-facing human readable error message. + #[prost(string, tag = "2")] + pub message: ::prost::alloc::string::String, + /// The status code. + #[prost(enumeration = "status::StatusCode", tag = "3")] + pub code: i32, +} +/// Nested message and enum types in `Status`. +pub mod status { + /// For the semantics of status codes see + /// + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum StatusCode { + /// The default status. + Unset = 0, + /// The Span has been validated by an Application developer or Operator to + /// have completed successfully. + Ok = 1, + /// The Span contains an error. + Error = 2, + } + impl StatusCode { + /// 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 { + StatusCode::Unset => "STATUS_CODE_UNSET", + StatusCode::Ok => "STATUS_CODE_OK", + StatusCode::Error => "STATUS_CODE_ERROR", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "STATUS_CODE_UNSET" => Some(Self::Unset), + "STATUS_CODE_OK" => Some(Self::Ok), + "STATUS_CODE_ERROR" => Some(Self::Error), + _ => None, + } + } + } +} +/// SpanFlags represents constants used to interpret the +/// Span.flags field, which is protobuf 'fixed32' type and is to +/// be used as bit-fields. Each non-zero value defined in this enum is +/// a bit-mask. To extract the bit-field, for example, use an +/// expression like: +/// +/// (span.flags & SPAN_FLAGS_TRACE_FLAGS_MASK) +/// +/// See for the flag definitions. +/// +/// Note that Span flags were introduced in version 1.1 of the +/// OpenTelemetry protocol. Older Span producers do not set this +/// field, consequently consumers should not rely on the absence of a +/// particular flag bit to indicate the presence of a particular feature. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum SpanFlags { + /// The zero value for the enum. Should not be used for comparisons. + /// Instead use bitwise "and" with the appropriate mask as shown above. + DoNotUse = 0, + /// Bits 0-7 are used for trace flags. + TraceFlagsMask = 255, + /// Bits 8 and 9 are used to indicate that the parent span or link span is remote. + /// Bit 8 (`HAS_IS_REMOTE`) indicates whether the value is known. + /// Bit 9 (`IS_REMOTE`) indicates whether the span or link is remote. + ContextHasIsRemoteMask = 256, + ContextIsRemoteMask = 512, +} +impl SpanFlags { + /// 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 { + SpanFlags::DoNotUse => "SPAN_FLAGS_DO_NOT_USE", + SpanFlags::TraceFlagsMask => "SPAN_FLAGS_TRACE_FLAGS_MASK", + SpanFlags::ContextHasIsRemoteMask => "SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK", + SpanFlags::ContextIsRemoteMask => "SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SPAN_FLAGS_DO_NOT_USE" => Some(Self::DoNotUse), + "SPAN_FLAGS_TRACE_FLAGS_MASK" => Some(Self::TraceFlagsMask), + "SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK" => Some(Self::ContextHasIsRemoteMask), + "SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK" => Some(Self::ContextIsRemoteMask), + _ => None, + } + } +} diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index b99e7f12e..566217ca0 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -17,3 +17,4 @@ schemars = { workspace = true } fpx = { version = "0.1.0", path = "../fpx" } serde = { workspace = true } serde_json = { workspace = true } +tonic-build = { version = "0.11" } diff --git a/xtask/src/commands.rs b/xtask/src/commands.rs index 1b559ddb7..404ecf39f 100644 --- a/xtask/src/commands.rs +++ b/xtask/src/commands.rs @@ -1,7 +1,8 @@ use anyhow::Result; use clap::{Parser, Subcommand}; -pub mod schemas; +mod otel_types; +mod schemas; /// FPX - Super-charge your local development. #[derive(Parser, Debug)] @@ -13,11 +14,13 @@ pub struct Args { #[derive(Subcommand, Debug)] pub enum Command { + GenerateOtelTypes(otel_types::Args), GenerateSchemas(schemas::Args), } pub async fn handle_command(args: Args) -> Result<()> { match args.command { + Command::GenerateOtelTypes(args) => otel_types::handle_command(args).await, Command::GenerateSchemas(args) => schemas::handle_command(args).await, } } diff --git a/xtask/src/commands/otel_types.rs b/xtask/src/commands/otel_types.rs new file mode 100644 index 000000000..405de3532 --- /dev/null +++ b/xtask/src/commands/otel_types.rs @@ -0,0 +1,40 @@ +use anyhow::{bail, Result}; + +#[derive(clap::Args, Debug)] +pub struct Args { + /// Input .proto files. + #[arg(short, long, env, default_values = ["opentelemetry-proto/opentelemetry/proto/collector/trace/v1/trace_service.proto"])] + pub proto_files: Vec, + + /// Include paths for other .proto files. + #[arg(short, long, env, default_values = ["opentelemetry-proto"])] + pub include_paths: Vec, + + /// Name of the include file. + #[arg(long, env, default_value = "mod.rs")] + pub include_file: String, + + /// Directory where the generated Rust files will be created. + #[arg(short, long, env, default_value = "fpx/src/models/otel")] + pub output_path: String, +} + +pub async fn handle_command(args: Args) -> Result<()> { + if args.include_paths.is_empty() { + bail!("No input paths were provided") + } + + tonic_build::configure() + .include_file(args.include_file) + .build_client(false) + .build_server(true) + .out_dir(args.output_path) + .compile( + &args.proto_files, + &args.include_paths, + // &["../opentelemetry-proto/opentelemetry/proto/collector/trace/v1/trace_service.proto"], + // &["../opentelemetry-proto"], + )?; + + Ok(()) +}