diff --git a/.github/workflows/bindings_ci.yml b/.github/workflows/bindings_ci.yml index e85c00a2169..1d47bb6edd5 100644 --- a/.github/workflows/bindings_ci.yml +++ b/.github/workflows/bindings_ci.yml @@ -26,6 +26,11 @@ jobs: - name: Checkout repo uses: actions/checkout@v2 + - name: Install Protoc + uses: arduino/setup-protoc@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Check xtask cache uses: actions/cache@v3 id: xtask-cache @@ -58,6 +63,11 @@ jobs: - name: Checkout uses: actions/checkout@v1 + - name: Install Protoc + uses: arduino/setup-protoc@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Install Rust uses: actions-rs/toolchain@v1 with: @@ -204,6 +214,11 @@ jobs: - name: Checkout repo uses: actions/checkout@v2 + - name: Install Protoc + uses: arduino/setup-protoc@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Check xtask cache uses: actions/cache@v3 id: xtask-cache @@ -236,6 +251,11 @@ jobs: - name: Checkout uses: actions/checkout@v1 + - name: Install Protoc + uses: arduino/setup-protoc@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Install Rust uses: actions-rs/toolchain@v1 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c48cb3b5b1..7795a8715eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,11 @@ jobs: - name: Checkout repo uses: actions/checkout@v2 + - name: Install Protoc + uses: arduino/setup-protoc@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Check xtask cache uses: actions/cache@v3 id: xtask-cache @@ -187,6 +192,11 @@ jobs: - name: Checkout uses: actions/checkout@v1 + - name: Install Protoc + uses: arduino/setup-protoc@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Install Rust uses: actions-rs/toolchain@v1 with: @@ -382,6 +392,11 @@ jobs: - name: Checkout the repo uses: actions/checkout@v3 + - name: Install Protoc + uses: arduino/setup-protoc@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Install Rust uses: actions-rs/toolchain@v1 with: diff --git a/Cargo.lock b/Cargo.lock index 7946958e073..22a5c59c7c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,6 +408,12 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "base64ct" version = "1.5.3" @@ -1680,6 +1686,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.0.25" @@ -2141,6 +2153,18 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -2887,6 +2911,7 @@ version = "0.2.0" dependencies = [ "android_logger", "anyhow", + "base64 0.21.0", "extension-trait", "futures-core", "futures-signals", @@ -2895,12 +2920,15 @@ dependencies = [ "matrix-sdk", "mime", "once_cell", + "opentelemetry", + "opentelemetry-otlp", "sanitize-filename-reader-friendly", "serde_json", "thiserror", "tokio", "tokio-stream", "tracing", + "tracing-opentelemetry", "tracing-subscriber", "uniffi", "uniffi_build", @@ -3152,6 +3180,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + [[package]] name = "napi" version = "2.10.5" @@ -3439,6 +3473,101 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "opentelemetry" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d6c3d7288a106c0a363e4b0e8d308058d56902adefb16f4936f417ffef086e" +dependencies = [ + "opentelemetry_api", + "opentelemetry_sdk", +] + +[[package]] +name = "opentelemetry-http" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc79add46364183ece1a4542592ca593e6421c60807232f5b8f7a31703825d" +dependencies = [ + "async-trait", + "bytes", + "http", + "opentelemetry_api", + "reqwest", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1c928609d087790fc936a1067bdc310ae702bdf3b090c3f281b713622c8bbde" +dependencies = [ + "async-trait", + "futures", + "futures-util", + "http", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "prost", + "reqwest", + "thiserror", + "tokio", + "tonic", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61a2f56df5574508dd86aaca016c917489e589ece4141df1b5e349af8d66c28" +dependencies = [ + "futures", + "futures-util", + "opentelemetry", + "prost", + "tonic", + "tonic-build", +] + +[[package]] +name = "opentelemetry_api" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c24f96e21e7acc813c7a8394ee94978929db2bcc46cf6b5014fc612bf7760c22" +dependencies = [ + "fnv", + "futures-channel", + "futures-util", + "indexmap", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca41c4933371b61c2a2f214bf16931499af4ec90543604ec828f7a625c09113" +dependencies = [ + "async-trait", + "crossbeam-channel", + "dashmap", + "fnv", + "futures-channel", + "futures-executor", + "futures-util", + "once_cell", + "opentelemetry_api", + "percent-encoding", + "rand 0.8.5", + "thiserror", + "tokio", + "tokio-stream", +] + [[package]] name = "os_str_bytes" version = "6.4.1" @@ -3540,6 +3669,16 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +[[package]] +name = "petgraph" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "pin-project" version = "1.0.12" @@ -3685,6 +3824,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettyplease" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro-crate" version = "1.2.1" @@ -3756,6 +3905,28 @@ dependencies = [ "prost-derive", ] +[[package]] +name = "prost-build" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5320c680de74ba083512704acb90fe00f28f79207286a848e730c45dd73ed6" +dependencies = [ + "bytes", + "heck", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", + "which", +] + [[package]] name = "prost-derive" version = "0.11.5" @@ -3769,6 +3940,16 @@ dependencies = [ "syn", ] +[[package]] +name = "prost-types" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "017f79637768cde62820bc2d4fe0e45daaa027755c323ad077767c6c5f173091" +dependencies = [ + "bytes", + "prost", +] + [[package]] name = "pulldown-cmark" version = "0.9.2" @@ -4957,6 +5138,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-macros" version = "1.8.2" @@ -5035,6 +5226,51 @@ dependencies = [ "serde", ] +[[package]] +name = "tonic" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.13.1", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "prost-derive", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tonic-build" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "quote", + "syn", +] + [[package]] name = "tower" version = "0.4.13" @@ -5043,9 +5279,13 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", + "indexmap", "pin-project", "pin-project-lite", + "rand 0.8.5", + "slab", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -5127,6 +5367,16 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + [[package]] name = "tracing-log" version = "0.1.3" @@ -5138,6 +5388,20 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-opentelemetry" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21ebb87a95ea13271332df069020513ab70bdb5637ca42d6e492dc3bbbad48de" +dependencies = [ + "once_cell", + "opentelemetry", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber", +] + [[package]] name = "tracing-subscriber" version = "0.3.16" @@ -5679,6 +5943,17 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +[[package]] +name = "which" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell", +] + [[package]] name = "wildmatch" version = "2.1.1" diff --git a/bindings/matrix-sdk-ffi/Cargo.toml b/bindings/matrix-sdk-ffi/Cargo.toml index 82d2e413557..b8a8af70067 100644 --- a/bindings/matrix-sdk-ffi/Cargo.toml +++ b/bindings/matrix-sdk-ffi/Cargo.toml @@ -17,6 +17,7 @@ uniffi_build = { workspace = true, features = ["builtin-bindgen"] } [dependencies] anyhow = { workspace = true } +base64 = "0.21" extension-trait = "1.0.1" futures-core = "0.3.17" futures-signals = { version = "0.3.30", default-features = false } @@ -25,9 +26,12 @@ mime = "0.3.16" # FIXME: we currently can't feature flag anything in the api.udl, therefore we must enforce experimental-sliding-sync being exposed here.. # see https://github.com/matrix-org/matrix-rust-sdk/issues/1014 once_cell = { workspace = true } +opentelemetry = { version = "0.18.0", features = ["rt-tokio"] } +opentelemetry-otlp = { version = "0.11.0", features = ["tokio", "reqwest-client", "http-proto"] } sanitize-filename-reader-friendly = "2.2.1" serde_json = { workspace = true } thiserror = { workspace = true } +tracing-opentelemetry = { version = "0.18.0" } tokio = { version = "1", features = ["rt-multi-thread", "macros"] } tokio-stream = "0.1.8" uniffi = { workspace = true } diff --git a/bindings/matrix-sdk-ffi/src/platform.rs b/bindings/matrix-sdk-ffi/src/platform.rs index b1ed9702b13..b506c47441c 100644 --- a/bindings/matrix-sdk-ffi/src/platform.rs +++ b/bindings/matrix-sdk-ffi/src/platform.rs @@ -1,9 +1,25 @@ +use std::collections::HashMap; + #[cfg(target_os = "android")] use android as platform_impl; +use base64::{engine::general_purpose::STANDARD, Engine}; +use futures_core::future::BoxFuture; #[cfg(target_os = "ios")] use ios as platform_impl; +use opentelemetry::{ + sdk::{ + trace::{BatchMessage, TraceRuntime, Tracer}, + util::tokio_interval_stream, + Resource, + }, + KeyValue, +}; +use opentelemetry_otlp::{Protocol, WithExportConfig}; #[cfg(not(any(target_os = "ios", target_os = "android")))] use other as platform_impl; +use tokio::runtime::Handle; + +use crate::RUNTIME; #[cfg(target_os = "android")] mod android { @@ -24,6 +40,75 @@ mod android { } } +#[derive(Clone, Debug)] +struct TracingRuntime { + runtime: Handle, +} + +impl opentelemetry::runtime::Runtime for TracingRuntime { + type Interval = tokio_stream::wrappers::IntervalStream; + type Delay = ::std::pin::Pin>; + + fn interval(&self, duration: std::time::Duration) -> Self::Interval { + let _guard = self.runtime.enter(); + tokio_interval_stream(duration) + } + + fn spawn(&self, future: BoxFuture<'static, ()>) { + #[allow(clippy::let_underscore_future)] + let _ = self.runtime.spawn(future); + } + + fn delay(&self, duration: std::time::Duration) -> Self::Delay { + let _guard = self.runtime.enter(); + Box::pin(tokio::time::sleep(duration)) + } +} + +impl TraceRuntime for TracingRuntime { + type Receiver = tokio_stream::wrappers::ReceiverStream; + type Sender = tokio::sync::mpsc::Sender; + + fn batch_message_channel(&self, capacity: usize) -> (Self::Sender, Self::Receiver) { + let (sender, receiver) = tokio::sync::mpsc::channel(capacity); + (sender, tokio_stream::wrappers::ReceiverStream::new(receiver)) + } +} + +pub fn create_otlp_tracer( + user: String, + password: String, + otlp_endpoint: String, + client_name: String, +) -> anyhow::Result { + let runtime = RUNTIME.handle().to_owned(); + + let auth = STANDARD.encode(format!("{user}:{password}")); + let headers = HashMap::from([("Authorization".to_owned(), format!("Basic {auth}"))]); + let http_client = matrix_sdk::reqwest::ClientBuilder::new().build()?; + + let exporter = opentelemetry_otlp::new_exporter() + .http() + .with_http_client(http_client) + .with_protocol(Protocol::HttpBinary) + .with_endpoint(otlp_endpoint) + .with_headers(headers); + + let tracer_runtime = TracingRuntime { runtime: runtime.to_owned() }; + + let _guard = runtime.enter(); + let tracer = opentelemetry_otlp::new_pipeline() + .tracing() + .with_exporter(exporter) + .with_trace_config( + opentelemetry::sdk::trace::config() + .with_resource(Resource::new(vec![KeyValue::new("service.name", client_name)])), + ) + .install_batch(tracer_runtime)?; + + Ok(tracer) +} + #[cfg(target_os = "ios")] mod ios { use std::io; @@ -35,6 +120,26 @@ mod ios { .with(fmt::layer().with_ansi(false).with_writer(io::stderr)) .init(); } + + pub fn setup_otlp_tracing( + configuration: String, + user: String, + password: String, + otlp_endpoint: String, + ) -> anyhow::Result<()> { + let otlp_tracer = + super::create_otlp_tracer(user, password, otlp_endpoint, "element-x-ios".to_owned())?; + + let otlp_layer = tracing_opentelemetry::layer().with_tracer(otlp_tracer); + + tracing_subscriber::registry() + .with(EnvFilter::new(configuration)) + .with(fmt::layer().with_ansi(false).with_writer(io::stderr)) + .with(otlp_layer) + .init(); + + Ok(()) + } } #[cfg(not(any(target_os = "ios", target_os = "android")))] @@ -55,3 +160,10 @@ mod other { pub fn setup_tracing(filter: String) { platform_impl::setup_tracing(filter) } + +#[cfg(target_os = "ios")] +#[uniffi::export] +pub fn setup_otlp_tracing(filter: String, user: String, password: String, otlp_endpoint: String) { + platform_impl::setup_otlp_tracing(filter, user, password, otlp_endpoint) + .expect("Couldn't configure the OpenTelemetry tracer") +}