From 5e05236b4987346697c7caae0567d3c50714c198 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira <118899497+lucasfernog-crabnebula@users.noreply.github.com> Date: Mon, 27 Nov 2023 11:56:46 -0300 Subject: [PATCH] feat(core): add tracing for vital functionality closes #5204 (#8289) * feat(core): add tracing for vital functionality * Update core/tauri-runtime-wry/src/lib.rs [skip ci] * Update Cargo.toml [skip ci] * tracing feature * wry 0.24.6 * add change tag * add tracing to CI test * enhance spans for update check * remove app from debug impl --- .changes/tracing.md | 7 + .github/workflows/test-core.yml | 2 +- core/tauri-macros/Cargo.toml | 3 +- core/tauri-macros/src/command/wrapper.rs | 63 +++++++- core/tauri-runtime-wry/Cargo.toml | 4 +- core/tauri-runtime-wry/src/lib.rs | 107 ++++++++++++++ core/tauri/Cargo.toml | 2 + core/tauri/src/app.rs | 35 ++--- core/tauri/src/command.rs | 4 + core/tauri/src/hooks.rs | 39 ++++- core/tauri/src/lib.rs | 17 +++ core/tauri/src/manager.rs | 13 +- core/tauri/src/plugin.rs | 63 +++++--- core/tauri/src/updater/core.rs | 176 +++++++++++++++++------ core/tauri/src/window.rs | 29 ++++ examples/api/src-tauri/Cargo.lock | 132 ++++++++++++++--- 16 files changed, 582 insertions(+), 114 deletions(-) create mode 100644 .changes/tracing.md diff --git a/.changes/tracing.md b/.changes/tracing.md new file mode 100644 index 000000000000..baee89fdbb1c --- /dev/null +++ b/.changes/tracing.md @@ -0,0 +1,7 @@ +--- +"tauri": patch:enhance +"tauri-runtime-wry": patch:enhance +"tauri-macros": patch:enhance +--- + +Added tracing for window startup, plugins, `Window::eval`, events, IPC, updater and custom protocol request handlers behind the `tracing` feature flag. diff --git a/.github/workflows/test-core.yml b/.github/workflows/test-core.yml index 382339ff3b6c..8a34e1411db9 100644 --- a/.github/workflows/test-core.yml +++ b/.github/workflows/test-core.yml @@ -56,7 +56,7 @@ jobs: key: api-all } - { - args: --features compression,wry,linux-protocol-headers,isolation,custom-protocol,api-all,cli,updater,system-tray,windows7-compat,http-multipart,test, + args: --features tracing,compression,wry,linux-protocol-headers,isolation,custom-protocol,api-all,cli,updater,system-tray,windows7-compat,http-multipart,test, key: all } diff --git a/core/tauri-macros/Cargo.toml b/core/tauri-macros/Cargo.toml index 4085f128610b..213ce8cf3a26 100644 --- a/core/tauri-macros/Cargo.toml +++ b/core/tauri-macros/Cargo.toml @@ -16,7 +16,7 @@ readme = "README.md" proc-macro = true [dependencies] -proc-macro2 = "1" +proc-macro2 = { version = "1", features = ["span-locations"] } quote = "1" syn = { version = "1", features = [ "full" ] } heck = "0.4" @@ -30,3 +30,4 @@ isolation = [ "tauri-codegen/isolation" ] shell-scope = [ "tauri-codegen/shell-scope" ] config-json5 = [ "tauri-codegen/config-json5", "tauri-utils/config-json5" ] config-toml = [ "tauri-codegen/config-toml", "tauri-utils/config-toml" ] +tracing = [] diff --git a/core/tauri-macros/src/command/wrapper.rs b/core/tauri-macros/src/command/wrapper.rs index e529d8fcdc8d..c50707089752 100644 --- a/core/tauri-macros/src/command/wrapper.rs +++ b/core/tauri-macros/src/command/wrapper.rs @@ -161,21 +161,51 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { } // body to the command wrapper or a `compile_error!` of an error occurred while parsing it. - let body = syn::parse::(attributes) + let (body, attributes) = syn::parse::(attributes) .map(|mut attrs| { if function.sig.asyncness.is_some() { attrs.execution_context = ExecutionContext::Async; } attrs }) - .and_then(|attrs| match attrs.execution_context { - ExecutionContext::Async => body_async(&function, &invoke, attrs.argument_case), - ExecutionContext::Blocking => body_blocking(&function, &invoke, attrs.argument_case), + .and_then(|attrs| { + let body = match attrs.execution_context { + ExecutionContext::Async => body_async(&function, &invoke, attrs.argument_case), + ExecutionContext::Blocking => body_blocking(&function, &invoke, attrs.argument_case), + }; + body.map(|b| (b, Some(attrs))) }) - .unwrap_or_else(syn::Error::into_compile_error); + .unwrap_or_else(|e| (syn::Error::into_compile_error(e), None)); let Invoke { message, resolver } = invoke; + let kind = match attributes.as_ref().map(|a| &a.execution_context) { + Some(ExecutionContext::Async) if function.sig.asyncness.is_none() => "sync_threadpool", + Some(ExecutionContext::Async) => "async", + Some(ExecutionContext::Blocking) => "sync", + _ => "sync", + }; + + let loc = function.span().start(); + let line = loc.line; + let col = loc.column; + + let maybe_span = if cfg!(feature = "tracing") { + quote!({ + let _span = tracing::debug_span!( + "ipc::request::handler", + cmd = #message.command(), + kind = #kind, + loc.line = #line, + loc.col = #col, + is_internal = false, + ) + .entered(); + }) + } else { + quote!() + }; + // Rely on rust 2018 edition to allow importing a macro from a path. quote!( #async_command_check @@ -193,6 +223,8 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { #[allow(unused_variables)] let ::tauri::Invoke { message: #message, resolver: #resolver } = $invoke; + #maybe_span + #body }}; } @@ -212,6 +244,20 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { fn body_async(function: &ItemFn, invoke: &Invoke, case: ArgumentCase) -> syn::Result { let Invoke { message, resolver } = invoke; parse_args(function, message, case).map(|args| { + #[cfg(feature = "tracing")] + quote! { + use tracing::Instrument; + + let span = tracing::debug_span!("ipc::request::run"); + #resolver.respond_async_serialized(async move { + let result = $path(#(#args?),*); + let kind = (&result).async_kind(); + kind.future(result).await + } + .instrument(span)); + } + + #[cfg(not(feature = "tracing"))] quote! { #resolver.respond_async_serialized(async move { let result = $path(#(#args?),*); @@ -241,7 +287,14 @@ fn body_blocking( Err(err) => return #resolver.invoke_error(err), }); + let maybe_span = if cfg!(feature = "tracing") { + quote!(let _span = tracing::debug_span!("ipc::request::run").entered();) + } else { + quote!() + }; + Ok(quote! { + #maybe_span let result = $path(#(match #args #match_body),*); let kind = (&result).blocking_kind(); kind.block(result, #resolver); diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index d13484dd5bdc..e3a33182b68b 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -13,12 +13,13 @@ exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" [dependencies] -wry = { version = "0.24.4", default-features = false, features = [ "file-drop", "protocol" ] } +wry = { version = "0.24.6", default-features = false, features = [ "file-drop", "protocol" ] } tauri-runtime = { version = "0.14.1", path = "../tauri-runtime" } tauri-utils = { version = "1.5.0", path = "../tauri-utils" } uuid = { version = "1", features = [ "v4" ] } rand = "0.8" raw-window-handle = "0.5" +tracing = { version = "0.1", optional = true } [target."cfg(windows)".dependencies] webview2-com = "0.19.1" @@ -48,3 +49,4 @@ objc-exception = [ "wry/objc-exception" ] global-shortcut = [ "tauri-runtime/global-shortcut" ] clipboard = [ "tauri-runtime/clipboard" ] linux-headers = [ "wry/linux-headers", "webkit2gtk/v2_36" ] +tracing = [ "dep:tracing", "wry/tracing" ] diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index 9a222c5cf691..27f5418387f5 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -244,6 +244,32 @@ impl Context { } } +#[cfg(feature = "tracing")] +#[derive(Debug, Clone, Default)] +pub struct ActiveTraceSpanStore(Rc>>); + +#[cfg(feature = "tracing")] +impl ActiveTraceSpanStore { + pub fn remove_window_draw(&self, window_id: WindowId) { + let mut store = self.0.borrow_mut(); + if let Some(index) = store + .iter() + .position(|t| matches!(t, ActiveTracingSpan::WindowDraw { id, span: _ } if id == &window_id)) + { + store.remove(index); + } + } +} + +#[cfg(feature = "tracing")] +#[derive(Debug)] +pub enum ActiveTracingSpan { + WindowDraw { + id: WindowId, + span: tracing::span::EnteredSpan, + }, +} + #[derive(Debug, Clone)] pub struct DispatcherMainThreadContext { pub window_target: EventLoopWindowTarget>, @@ -255,6 +281,8 @@ pub struct DispatcherMainThreadContext { pub windows: Rc>>, #[cfg(all(desktop, feature = "system-tray"))] system_tray_manager: SystemTrayManager, + #[cfg(feature = "tracing")] + pub active_tracing_spans: ActiveTraceSpanStore, } // SAFETY: we ensure this type is only used on the main thread. @@ -1135,7 +1163,10 @@ pub enum WindowMessage { #[derive(Debug, Clone)] pub enum WebviewMessage { + #[cfg(not(feature = "tracing"))] EvaluateScript(String), + #[cfg(feature = "tracing")] + EvaluateScript(String, Sender<()>, tracing::Span), #[allow(dead_code)] WebviewEvent(WebviewEvent), Print, @@ -1651,6 +1682,21 @@ impl Dispatch for WryDispatcher { ) } + #[cfg(feature = "tracing")] + fn eval_script>(&self, script: S) -> Result<()> { + // use a channel so the EvaluateScript task uses the current span as parent + let (tx, rx) = channel(); + getter!( + self, + rx, + Message::Webview( + self.window_id, + WebviewMessage::EvaluateScript(script.into(), tx, tracing::Span::current()), + ) + ) + } + + #[cfg(not(feature = "tracing"))] fn eval_script>(&self, script: S) -> Result<()> { send_user_message( &self.context, @@ -1962,6 +2008,8 @@ impl Wry { windows, #[cfg(all(desktop, feature = "system-tray"))] system_tray_manager, + #[cfg(feature = "tracing")] + active_tracing_spans: Default::default(), }, }; @@ -2165,6 +2213,9 @@ impl Runtime for Wry { #[cfg(all(desktop, feature = "system-tray"))] let system_tray_manager = self.context.main_thread.system_tray_manager.clone(); + #[cfg(feature = "tracing")] + let active_tracing_spans = self.context.main_thread.active_tracing_spans.clone(); + #[cfg(all(desktop, feature = "global-shortcut"))] let global_shortcut_manager = self.context.main_thread.global_shortcut_manager.clone(); #[cfg(all(desktop, feature = "global-shortcut"))] @@ -2202,6 +2253,8 @@ impl Runtime for Wry { clipboard_manager: clipboard_manager.clone(), #[cfg(all(desktop, feature = "system-tray"))] system_tray_manager: system_tray_manager.clone(), + #[cfg(feature = "tracing")] + active_tracing_spans: active_tracing_spans.clone(), }, web_context, ); @@ -2226,6 +2279,8 @@ impl Runtime for Wry { clipboard_manager: clipboard_manager.clone(), #[cfg(all(desktop, feature = "system-tray"))] system_tray_manager: system_tray_manager.clone(), + #[cfg(feature = "tracing")] + active_tracing_spans: active_tracing_spans.clone(), }, web_context, ); @@ -2240,6 +2295,9 @@ impl Runtime for Wry { let web_context = self.context.main_thread.web_context; let mut plugins = self.plugins; + #[cfg(feature = "tracing")] + let active_tracing_spans = self.context.main_thread.active_tracing_spans.clone(); + #[cfg(all(desktop, feature = "system-tray"))] let system_tray_manager = self.context.main_thread.system_tray_manager; @@ -2272,6 +2330,8 @@ impl Runtime for Wry { clipboard_manager: clipboard_manager.clone(), #[cfg(all(desktop, feature = "system-tray"))] system_tray_manager: system_tray_manager.clone(), + #[cfg(feature = "tracing")] + active_tracing_spans: active_tracing_spans.clone(), }, &web_context, ); @@ -2295,6 +2355,8 @@ impl Runtime for Wry { clipboard_manager: clipboard_manager.clone(), #[cfg(all(desktop, feature = "system-tray"))] system_tray_manager: system_tray_manager.clone(), + #[cfg(feature = "tracing")] + active_tracing_spans: active_tracing_spans.clone(), }, &web_context, ); @@ -2314,6 +2376,8 @@ pub struct EventLoopIterationContext<'a, T: UserEvent> { pub clipboard_manager: Arc>, #[cfg(all(desktop, feature = "system-tray"))] pub system_tray_manager: SystemTrayManager, + #[cfg(feature = "tracing")] + pub active_tracing_spans: ActiveTraceSpanStore, } struct UserMessageContext { @@ -2590,6 +2654,19 @@ fn handle_user_message( } } Message::Webview(id, webview_message) => match webview_message { + #[cfg(feature = "tracing")] + WebviewMessage::EvaluateScript(script, tx, span) => { + let _span = span.entered(); + if let Some(WindowHandle::Webview { inner: webview, .. }) = + windows.borrow().get(&id).and_then(|w| w.inner.as_ref()) + { + if let Err(e) = webview.evaluate_script(&script) { + debug_eprintln!("{}", e); + } + } + tx.send(()).unwrap(); + } + #[cfg(not(feature = "tracing"))] WebviewMessage::EvaluateScript(script) => { if let Some(WindowHandle::Webview { inner: webview, .. }) = windows.borrow().get(&id).and_then(|w| w.inner.as_ref()) @@ -2758,6 +2835,8 @@ fn handle_event_loop( clipboard_manager, #[cfg(all(desktop, feature = "system-tray"))] system_tray_manager, + #[cfg(feature = "tracing")] + active_tracing_spans, } = context; if *control_flow != ControlFlow::Exit { *control_flow = ControlFlow::Wait; @@ -2780,6 +2859,11 @@ fn handle_event_loop( callback(RunEvent::Exit); } + #[cfg(feature = "tracing")] + Event::RedrawRequested(id) => { + active_tracing_spans.remove_window_draw(id); + } + #[cfg(all(desktop, feature = "global-shortcut"))] Event::GlobalShortcutEvent(accelerator_id) => { for (id, handler) in &*global_shortcut_manager_handle.listeners.lock().unwrap() { @@ -3123,6 +3207,14 @@ fn create_webview( #[cfg(windows)] let proxy = context.proxy.clone(); + #[cfg(feature = "tracing")] + let _webview_create_span = tracing::debug_span!("wry::webview::create").entered(); + #[cfg(feature = "tracing")] + let window_draw_span = tracing::debug_span!("wry::window::draw").entered(); + #[cfg(feature = "tracing")] + let window_create_span = + tracing::debug_span!(parent: &window_draw_span, "wry::window::create").entered(); + let window_event_listeners = WindowEventListeners::default(); #[cfg(windows)] @@ -3157,6 +3249,21 @@ fn create_webview( let focused = window_builder.inner.window.focused; let window = window_builder.inner.build(event_loop).unwrap(); + #[cfg(feature = "tracing")] + { + drop(window_create_span); + + context + .main_thread + .active_tracing_spans + .0 + .borrow_mut() + .push(ActiveTracingSpan::WindowDraw { + id: window.id(), + span: window_draw_span, + }); + } + webview_id_map.insert(window.id(), window_id); if window_builder.center { diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 0ec045ae43b0..c7bb7f713630 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -94,6 +94,7 @@ png = { version = "0.17", optional = true } ico = { version = "0.2.0", optional = true } encoding_rs = "0.8.31" sys-locale = { version = "0.2.3", optional = true } +tracing = { version = "0.1", optional = true } [target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] rfd = { version = "0.10", optional = true, features = [ "gtk3", "common-controls-v6" ] } @@ -135,6 +136,7 @@ cargo_toml = "0.11" [features] default = [ "wry", "compression", "objc-exception" ] +tracing = [ "dep:tracing", "tauri-macros/tracing", "tauri-runtime-wry/tracing" ] test = [ ] compression = [ "tauri-macros/compression", "tauri-utils/compression" ] wry = [ "tauri-runtime-wry" ] diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index c0f7911fd1bd..93e47036493f 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -474,26 +474,14 @@ impl AppHandle { /// Ok(()) /// }); /// ``` - pub fn plugin + 'static>(&self, mut plugin: P) -> crate::Result<()> { - plugin - .initialize( - self, - self - .config() - .plugins - .0 - .get(plugin.name()) - .cloned() - .unwrap_or_default(), - ) - .map_err(|e| crate::Error::PluginInitialization(plugin.name().to_string(), e.to_string()))?; - self - .manager() - .inner - .plugins - .lock() - .unwrap() - .register(plugin); + #[cfg_attr(feature = "tracing", tracing::instrument(name = "app::plugin::register", skip(plugin), fields(name = plugin.name())))] + pub fn plugin + 'static>(&self, plugin: P) -> crate::Result<()> { + let mut plugin = Box::new(plugin) as Box>; + + let mut store = self.manager().inner.plugins.lock().unwrap(); + store.initialize(&mut plugin, self, &self.config().plugins)?; + store.register(plugin); + Ok(()) } @@ -913,6 +901,7 @@ impl App { /// } /// ``` #[cfg(desktop)] + #[cfg_attr(feature = "tracing", tracing::instrument(name = "app::run_iteration"))] pub fn run_iteration(&mut self) -> crate::runtime::RunIteration { let manager = self.manager.clone(); let app_handle = self.handle(); @@ -1206,7 +1195,7 @@ impl Builder { /// ``` #[must_use] pub fn plugin + 'static>(mut self, plugin: P) -> Self { - self.plugins.register(plugin); + self.plugins.register(Box::new(plugin)); self } @@ -1557,6 +1546,10 @@ impl Builder { /// Builds the application. #[allow(clippy::type_complexity)] + #[cfg_attr( + feature = "tracing", + tracing::instrument(name = "app::build", skip_all) + )] pub fn build(mut self, context: Context) -> crate::Result> { #[cfg(target_os = "macos")] if self.menu.is_none() && self.enable_macos_default_menu { diff --git a/core/tauri/src/command.rs b/core/tauri/src/command.rs index 7f392d480826..5c2aca148bca 100644 --- a/core/tauri/src/command.rs +++ b/core/tauri/src/command.rs @@ -52,6 +52,8 @@ impl<'de, D: Deserialize<'de>, R: Runtime> CommandArg<'de, R> for D { fn from_command(command: CommandItem<'de, R>) -> Result { let name = command.name; let arg = command.key; + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("ipc::request::deserialize_arg", arg = arg).entered(); Self::deserialize(command).map_err(|e| crate::Error::InvalidArgs(name, arg, e).into()) } } @@ -160,6 +162,8 @@ pub mod private { use serde::Serialize; use serde_json::Value; use std::future::Future; + #[cfg(feature = "tracing")] + pub use tracing; // ===== impl Serialize ===== diff --git a/core/tauri/src/hooks.rs b/core/tauri/src/hooks.rs index fb9dea03bca0..e4acd724bd39 100644 --- a/core/tauri/src/hooks.rs +++ b/core/tauri/src/hooks.rs @@ -11,6 +11,8 @@ use serde::{Deserialize, Serialize}; use serde_json::Value as JsonValue; use serialize_to_javascript::{default_template, Template}; use std::{future::Future, sync::Arc}; +#[cfg(feature = "tracing")] +use tracing::Instrument; use tauri_macros::default_runtime; @@ -181,9 +183,16 @@ impl InvokeResolver { T: Serialize, F: Future> + Send + 'static, { - crate::async_runtime::spawn(async move { + let task = async move { Self::return_task(self.window, task, self.callback, self.error).await; - }); + }; + #[cfg(feature = "tracing")] + { + let span = tracing::trace_span!("ipc::request::respond"); + crate::async_runtime::spawn(task.instrument(span)); + } + #[cfg(not(feature = "tracing"))] + crate::async_runtime::spawn(task); } /// Reply to the invoke promise with an async task which is already serialized. @@ -191,27 +200,40 @@ impl InvokeResolver { where F: Future> + Send + 'static, { - crate::async_runtime::spawn(async move { + let task = async move { let response = match task.await { Ok(ok) => InvokeResponse::Ok(ok), Err(err) => InvokeResponse::Err(err), }; Self::return_result(self.window, response, self.callback, self.error) - }); + }; + #[cfg(feature = "tracing")] + { + let span = tracing::trace_span!("ipc::request::respond"); + crate::async_runtime::spawn(task.instrument(span)); + } + #[cfg(not(feature = "tracing"))] + crate::async_runtime::spawn(task); } /// Reply to the invoke promise with a serializable value. pub fn respond(self, value: Result) { + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("ipc::request::respond").entered(); Self::return_result(self.window, value.into(), self.callback, self.error) } /// Resolve the invoke promise with a value. pub fn resolve(self, value: T) { + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("ipc::request::respond").entered(); Self::return_result(self.window, Ok(value).into(), self.callback, self.error) } /// Reject the invoke promise with a value. pub fn reject(self, value: T) { + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("ipc::request::respond").entered(); Self::return_result( self.window, Result::<(), _>::Err(value.into()).into(), @@ -222,6 +244,8 @@ impl InvokeResolver { /// Reject the invoke promise with an [`InvokeError`]. pub fn invoke_error(self, error: InvokeError) { + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("ipc::request::respond").entered(); Self::return_result(self.window, error.into(), self.callback, self.error) } @@ -230,7 +254,7 @@ impl InvokeResolver { /// /// If the Result `is_ok()`, the callback will be the `success_callback` function name and the argument will be the Ok value. /// If the Result `is_err()`, the callback will be the `error_callback` function name and the argument will be the Err value. - pub async fn return_task( + pub(crate) async fn return_task( window: Window, task: F, success_callback: CallbackFn, @@ -258,6 +282,9 @@ impl InvokeResolver { success_callback: CallbackFn, error_callback: CallbackFn, ) { + #[cfg(feature = "tracing")] + let _span = + tracing::trace_span!("ipc::request::response", response = format!("{response:?}")).entered(); (window.invoke_responder())(window, response, success_callback, error_callback); } } @@ -268,6 +295,8 @@ pub fn window_invoke_responder( success_callback: CallbackFn, error_callback: CallbackFn, ) { + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("ipc::request::eval_response").entered(); let callback_string = match format_callback_result(response.into_result(), success_callback, error_callback) { Ok(callback_string) => callback_string, diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index e1290bf97474..daa015f52ddc 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -11,6 +11,7 @@ //! The following are a list of [Cargo features](https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section) that can be enabled or disabled: //! //! - **wry** *(enabled by default)*: Enables the [wry](https://github.com/tauri-apps/wry) runtime. Only disable it if you want a custom runtime. +//! - **tracing**: Enables [`tracing`](https://docs.rs/tracing/latest/tracing) for window startup, plugins, `Window::eval`, events, IPC, updater and custom protocol request handlers. //! - **test**: Enables the [`test`] module exposing unit test helpers. //! - **dox**: Internal feature to generate Rust documentation without linking on Linux. //! - **objc-exception**: Wrap each msg_send! in a @try/@catch and panics if an exception is caught, preventing Objective-C from unwinding into Rust. @@ -625,6 +626,10 @@ pub trait Manager: sealed::ManagerBase { /// app.emit_all("synchronized", ()); /// } /// ``` + #[cfg_attr( + feature = "tracing", + tracing::instrument("app::emit::all", skip(self, payload)) + )] fn emit_all(&self, event: &str, payload: S) -> Result<()> { self.manager().emit_filter(event, None, payload, |_| true) } @@ -641,6 +646,10 @@ pub trait Manager: sealed::ManagerBase { /// app.emit_filter("synchronized", (), |w| w.label().starts_with("foo-")); /// } /// ``` + #[cfg_attr( + feature = "tracing", + tracing::instrument("app::emit::filter", skip(self, payload, filter)) + )] fn emit_filter(&self, event: &str, payload: S, filter: F) -> Result<()> where S: Serialize + Clone, @@ -664,6 +673,10 @@ pub trait Manager: sealed::ManagerBase { /// } /// } /// ``` + #[cfg_attr( + feature = "tracing", + tracing::instrument("app::emit::to", skip(self, payload)) + )] fn emit_to(&self, label: &str, event: &str, payload: S) -> Result<()> { self .manager() @@ -728,6 +741,10 @@ pub trait Manager: sealed::ManagerBase { /// } /// } /// ``` + #[cfg_attr( + feature = "tracing", + tracing::instrument("app::emit::rust", skip(self)) + )] fn trigger_global(&self, event: &str, data: Option) { self.manager().trigger(event, None, data) } diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 372ffa14b6cb..fd19adcd17a4 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -593,6 +593,10 @@ impl WindowManager { ) -> WebviewIpcHandler { let manager = self.clone(); Box::new(move |window, #[allow(unused_mut)] mut request| { + #[cfg(feature = "tracing")] + let _span = + tracing::trace_span!("ipc::request", kind = "post-message", request = request).entered(); + let window = Window::new(manager.clone(), window, app_handle.clone()); #[cfg(feature = "isolation")] @@ -614,9 +618,14 @@ impl WindowManager { match serde_json::from_str::(&request) { Ok(message) => { + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("ipc::request::handle", cmd = message.cmd).entered(); + let _ = window.on_message(message); } Err(e) => { + #[cfg(feature = "tracing")] + tracing::trace!("ipc::request::error {}", e); let error: crate::Error = e.into(); let _ = window.eval(&format!( r#"console.error({})"#, @@ -958,7 +967,7 @@ impl WindowManager { .plugins .lock() .expect("poisoned plugin store") - .initialize(app, &self.inner.config.plugins) + .initialize_all(app, &self.inner.config.plugins) } pub fn prepare_window( @@ -1148,6 +1157,8 @@ impl WindowManager { S: Serialize + Clone, F: Fn(&Window) -> bool, { + #[cfg(feature = "tracing")] + let _span = tracing::debug_span!("emit::run").entered(); let emit_args = WindowEmitArgs::from(event, source_window_label, payload)?; assert_event_name_is_valid(event); self diff --git a/core/tauri/src/plugin.rs b/core/tauri/src/plugin.rs index f04890c582a2..0a53b4d84c9f 100644 --- a/core/tauri/src/plugin.rs +++ b/core/tauri/src/plugin.rs @@ -575,8 +575,8 @@ impl PluginStore { /// Adds a plugin to the store. /// /// Returns `true` if a plugin with the same name is already in the store. - pub fn register + 'static>(&mut self, plugin: P) -> bool { - self.store.insert(plugin.name(), Box::new(plugin)).is_some() + pub fn register(&mut self, plugin: Box>) -> bool { + self.store.insert(plugin.name(), plugin).is_some() } /// Removes the plugin with the given name from the store. @@ -584,20 +584,26 @@ impl PluginStore { self.store.remove(plugin).is_some() } - /// Initializes all plugins in the store. + /// Initializes the given plugin. pub(crate) fn initialize( + &self, + plugin: &mut Box>, + app: &AppHandle, + config: &PluginConfig, + ) -> crate::Result<()> { + initialize(plugin, app, config) + } + + /// Initializes all plugins in the store. + pub(crate) fn initialize_all( &mut self, app: &AppHandle, config: &PluginConfig, ) -> crate::Result<()> { - self.store.values_mut().try_for_each(|plugin| { - plugin - .initialize( - app, - config.0.get(plugin.name()).cloned().unwrap_or_default(), - ) - .map_err(|e| crate::Error::PluginInitialization(plugin.name().to_string(), e.to_string())) - }) + self + .store + .values_mut() + .try_for_each(|plugin| initialize(plugin, app, config)) } /// Generates an initialization script from all plugins in the store. @@ -613,18 +619,21 @@ impl PluginStore { /// Runs the created hook for all plugins in the store. pub(crate) fn created(&mut self, window: Window) { - self - .store - .values_mut() - .for_each(|plugin| plugin.created(window.clone())) + self.store.values_mut().for_each(|plugin| { + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("plugin::hooks::created", name = plugin.name()).entered(); + plugin.created(window.clone()) + }) } /// Runs the on_page_load hook for all plugins in the store. pub(crate) fn on_page_load(&mut self, window: Window, payload: PageLoadPayload) { - self - .store - .values_mut() - .for_each(|plugin| plugin.on_page_load(window.clone(), payload.clone())) + self.store.values_mut().for_each(|plugin| { + #[cfg(feature = "tracing")] + let _span = + tracing::trace_span!("plugin::hooks::on_page_load", name = plugin.name()).entered(); + plugin.on_page_load(window.clone(), payload.clone()) + }) } /// Runs the on_event hook for all plugins in the store. @@ -646,9 +655,25 @@ impl PluginStore { .next() .map(|c| c.to_string()) .unwrap_or_else(String::new); + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("plugin::hooks::ipc", name = plugin.name()).entered(); plugin.extend_api(invoke); } else { invoke.resolver.reject(format!("plugin {target} not found")); } } } + +#[cfg_attr(feature = "tracing", tracing::instrument(name = "plugin::hooks::initialize", skip(plugin), fields(name = plugin.name())))] +fn initialize( + plugin: &mut Box>, + app: &AppHandle, + config: &PluginConfig, +) -> crate::Result<()> { + plugin + .initialize( + app, + config.0.get(plugin.name()).cloned().unwrap_or_default(), + ) + .map_err(|e| crate::Error::PluginInitialization(plugin.name().to_string(), e.to_string())) +} diff --git a/core/tauri/src/updater/core.rs b/core/tauri/src/updater/core.rs index 838ded932523..d75f1b506006 100644 --- a/core/tauri/src/updater/core.rs +++ b/core/tauri/src/updater/core.rs @@ -19,6 +19,8 @@ use semver::Version; use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize}; use tauri_utils::{platform::current_exe, Env}; use time::OffsetDateTime; +#[cfg(feature = "tracing")] +use tracing::Instrument; use url::Url; #[cfg(desktop)] @@ -312,6 +314,10 @@ impl UpdateBuilder { Ok(self) } + #[cfg_attr( + feature = "tracing", + tracing::instrument("updater::check", skip_all, fields(arch, target), ret, err) + )] pub async fn build(mut self) -> Result> { let mut remote_release: Option = None; @@ -335,6 +341,12 @@ impl UpdateBuilder { (target.to_string(), format!("{target}-{arch}")) }; + #[cfg(feature = "tracing")] + { + tracing::Span::current().record("arch", arch); + tracing::Span::current().record("target", &target); + } + // Get the extract_path from the provided executable_path let extract_path = extract_path_from_executable(&self.app.state::(), &executable_path); @@ -370,38 +382,75 @@ impl UpdateBuilder { .replace("{{target}}", &target) .replace("{{arch}}", arch); - let mut request = HttpRequestBuilder::new("GET", &fixed_link)?.headers(headers.clone()); - if let Some(timeout) = self.timeout { - request = request.timeout(timeout); - } - let resp = ClientBuilder::new().build()?.send(request).await; - - // If we got a success, we stop the loop - // and we set our remote_release variable - if let Ok(res) = resp { - let status = res.status(); - // got status code 2XX - if status.is_success() { - // if we got 204 - if status == StatusCode::NO_CONTENT { - // return with `UpToDate` error - // we should catch on the client - return Err(Error::UpToDate); - }; - let res = res.read().await?; - // Convert the remote result to our local struct - let built_release = serde_json::from_value(res.data).map_err(Into::into); - // make sure all went well and the remote data is compatible - // with what we need locally - match built_release { - Ok(release) => { - last_error = None; - remote_release = Some(release); - break; + let task = async { + #[cfg(feature = "tracing")] + tracing::debug!("checking if there is an update via {}", url); + + let mut request = HttpRequestBuilder::new("GET", &fixed_link)?.headers(headers.clone()); + if let Some(timeout) = self.timeout { + request = request.timeout(timeout); + } + let resp = ClientBuilder::new().build()?.send(request).await; + + // If we got a success, we stop the loop + // and we set our remote_release variable + if let Ok(res) = resp { + let status = res.status(); + // got status code 2XX + if status.is_success() { + // if we got 204 + if status == StatusCode::NO_CONTENT { + #[cfg(feature = "tracing")] + tracing::event!(tracing::Level::DEBUG, kind = "result", data = "no content"); + // return with `UpToDate` error + // we should catch on the client + return Err(Error::UpToDate); + }; + let res = res.read().await?; + + // Convert the remote result to our local struct + let built_release: Result = + serde_json::from_value(res.data).map_err(Into::into); + + // make sure all went well and the remote data is compatible + // with what we need locally + match built_release { + Ok(release) => { + #[cfg(feature = "tracing")] + tracing::event!( + tracing::Level::DEBUG, + kind = "result", + data = tracing::field::debug(&release) + ); + last_error = None; + return Ok(Some(release)); + } + Err(err) => { + #[cfg(feature = "tracing")] + tracing::event!( + tracing::Level::ERROR, + kind = "error", + error = err.to_string() + ); + last_error = Some(err) + } } - Err(err) => last_error = Some(err), - } - } // if status code is not 2XX we keep loopin' our urls + } // if status code is not 2XX we keep loopin' our urls + } + + Ok(None) + }; + + #[cfg(feature = "tracing")] + let found_release = { + let span = tracing::info_span!("updater::check::fetch", url = &fixed_link,); + task.instrument(span).await? + }; + #[cfg(not(feature = "tracing"))] + let found_release = task.await?; + if let Some(release) = found_release { + remote_release.replace(release); + break; } } @@ -447,7 +496,6 @@ pub(crate) fn builder(app: AppHandle) -> UpdateBuilder { UpdateBuilder::new(app) } -#[derive(Debug)] pub(crate) struct Update { /// Application handle. pub app: AppHandle, @@ -480,6 +528,29 @@ pub(crate) struct Update { headers: HeaderMap, } +impl fmt::Debug for Update { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = f.debug_struct("Update"); + + s.field("current_version", &self.current_version) + .field("version", &self.version) + .field("date", &self.date) + .field("should_update", &self.should_update) + .field("body", &self.body) + .field("target", &self.target) + .field("extract_path", &self.extract_path) + .field("download_url", &self.download_url) + .field("signature", &self.signature) + .field("timeout", &self.timeout) + .field("headers", &self.headers); + + #[cfg(target_os = "windows")] + s.field("with_elevated_task", &self.with_elevated_task); + + s.finish() + } +} + impl Clone for Update { fn clone(&self) -> Self { Self { @@ -527,6 +598,7 @@ impl Update { // Download and install our update // @todo(lemarier): Split into download and install (two step) but need to be thread safe + #[cfg_attr(feature = "tracing", tracing::instrument("updater::download_and_install", skip_all, fields(url = %self.download_url), ret, err))] pub async fn download_and_install), D: FnOnce()>( &self, pub_key: String, @@ -540,6 +612,10 @@ impl Update { // anything with it yet #[cfg(target_os = "linux")] if self.app.state::().appimage.is_none() { + #[cfg(feature = "tracing")] + tracing::error!( + "app is not a supported Linux package. Currently only AppImages are supported" + ); return Err(Error::UnsupportedLinuxPackage); } @@ -561,10 +637,14 @@ impl Update { req = req.timeout(timeout); } + #[cfg(feature = "tracing")] + tracing::info!("Downloading update"); let response = client.send(req).await?; // make sure it's success if !response.status().is_success() { + #[cfg(feature = "tracing")] + tracing::error!("Failed to download update"); return Err(Error::Network(format!( "Download request failed with status: {}", response.status() @@ -577,17 +657,31 @@ impl Update { .and_then(|value| value.to_str().ok()) .and_then(|value| value.parse().ok()); - let mut buffer = Vec::new(); - { + let buffer = { use futures_util::StreamExt; let mut stream = response.bytes_stream(); - while let Some(chunk) = stream.next().await { - let chunk = chunk?; - let bytes = chunk.as_ref().to_vec(); - on_chunk(bytes.len(), content_length); - buffer.extend(bytes); + + let task = async move { + let mut buffer = Vec::new(); + while let Some(chunk) = stream.next().await { + let chunk = chunk?; + let bytes = chunk.as_ref().to_vec(); + on_chunk(bytes.len(), content_length); + buffer.extend(bytes); + } + Result::Ok(buffer) + }; + + #[cfg(feature = "tracing")] + { + let span = tracing::info_span!("updater::download_and_install::stream"); + task.instrument(span).await } - } + #[cfg(not(feature = "tracing"))] + { + task.await + } + }?; on_download_finish(); @@ -601,6 +695,8 @@ impl Update { // TODO: implement updater in mobile #[cfg(desktop)] { + #[cfg(feature = "tracing")] + tracing::info_span!("updater::download_and_install::install"); // we copy the files depending of the operating system // we run the setup, appimage re-install or overwrite the // macos .app diff --git a/core/tauri/src/window.rs b/core/tauri/src/window.rs index 594ff2de63d3..d9190d0fb299 100644 --- a/core/tauri/src/window.rs +++ b/core/tauri/src/window.rs @@ -68,6 +68,8 @@ impl WindowEmitArgs { source_window_label: Option<&str>, payload: S, ) -> crate::Result { + #[cfg(feature = "tracing")] + let _span = tracing::debug_span!("window::emit::serialize").entered(); Ok(WindowEmitArgs { event: serde_json::to_string(event)?, source_window_label: serde_json::to_string(&source_window_label)?, @@ -321,6 +323,7 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { } /// Creates a new webview window. + #[cfg_attr(feature = "tracing", tracing::instrument(name = "window::create"))] pub fn build(mut self) -> crate::Result> { let mut pending = PendingWindow::new( self.window_builder.clone(), @@ -776,6 +779,10 @@ impl PartialEq for Window { } impl Manager for Window { + #[cfg_attr( + feature = "tracing", + tracing::instrument("window::emit::to", skip(self, payload)) + )] fn emit_to( &self, label: &str, @@ -787,12 +794,17 @@ impl Manager for Window { .emit_filter(event, Some(self.label()), payload, |w| label == w.label()) } + #[cfg_attr( + feature = "tracing", + tracing::instrument("window::emit::all", skip(self, payload)) + )] fn emit_all(&self, event: &str, payload: S) -> crate::Result<()> { self .manager() .emit_filter(event, Some(self.label()), payload, |_| true) } } + impl ManagerBase for Window { fn manager(&self) -> &WindowManager { &self.manager @@ -1790,6 +1802,15 @@ impl Window { self.emit(event, payload) } + #[cfg_attr(feature = "tracing", tracing::instrument( + "window::emit::eval", + skip(emit_args), + fields( + event = emit_args.event, + source_window = emit_args.source_window_label, + payload = emit_args.payload + )) + )] pub(crate) fn emit_internal(&self, emit_args: &WindowEmitArgs) -> crate::Result<()> { self.eval(&format!( "(function () {{ const fn = window['{}']; fn && fn({{event: {}, windowLabel: {}, payload: {}}}) }})()", @@ -1816,6 +1837,10 @@ impl Window { /// } /// } /// ``` + #[cfg_attr( + feature = "tracing", + tracing::instrument("window::emit", skip(self, payload)) + )] pub fn emit(&self, event: &str, payload: S) -> crate::Result<()> { self .manager @@ -1908,6 +1933,10 @@ impl Window { /// } /// } /// ``` + #[cfg_attr( + feature = "tracing", + tracing::instrument("window::trigger", skip(self)) + )] pub fn trigger(&self, event: &str, data: Option) { let label = self.window.label.clone(); self.manager.trigger(event, Some(label), data) diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 96070ddbeecb..813d8098d6a2 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -712,12 +712,12 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.26" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +checksum = "37e366bff8cd32dd8754b0991fb66b279dc48f598c3a18914852a6673deef583" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -1724,9 +1724,9 @@ dependencies = [ [[package]] name = "infer" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a898e4b7951673fce96614ce5751d13c40fc5674bc2d759288e46c3ab62598b3" +checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc" dependencies = [ "cfb", ] @@ -1846,9 +1846,9 @@ dependencies = [ [[package]] name = "json-patch" -version = "1.0.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f54898088ccb91df1b492cc80029a6fdf1c48ca0db7c6822a8babad69c94658" +checksum = "55ff1e1486799e3f64129f8ccad108b38290df9cd7015cd31bed17239f0789d6" dependencies = [ "serde", "serde_json", @@ -2514,9 +2514,17 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ - "phf_macros 0.10.0", "phf_shared 0.10.0", - "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros 0.11.2", + "phf_shared 0.11.2", ] [[package]] @@ -2559,6 +2567,16 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand 0.8.5", +] + [[package]] name = "phf_macros" version = "0.8.0" @@ -2575,16 +2593,15 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.10.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro-hack", + "phf_generator 0.11.2", + "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -2605,6 +2622,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -3693,12 +3719,12 @@ dependencies = [ "glob", "heck 0.4.1", "html5ever 0.26.0", - "infer 0.12.0", + "infer 0.13.0", "json-patch", "kuchikiki", "log", "memchr", - "phf 0.10.1", + "phf 0.11.2", "proc-macro2", "quote", "semver", @@ -3709,7 +3735,7 @@ dependencies = [ "thiserror", "url", "walkdir", - "windows 0.39.0", + "windows-version", ] [[package]] @@ -4557,12 +4583,36 @@ dependencies = [ "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + [[package]] name = "windows-tokens" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" +[[package]] +name = "windows-version" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -4575,6 +4625,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.37.0" @@ -4599,6 +4655,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.37.0" @@ -4623,6 +4685,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.37.0" @@ -4647,6 +4715,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.37.0" @@ -4671,6 +4745,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -4683,6 +4763,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.37.0" @@ -4707,6 +4793,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" version = "0.4.7" @@ -4737,9 +4829,9 @@ dependencies = [ [[package]] name = "wry" -version = "0.24.4" +version = "0.24.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ef04bdad49eba2e01f06e53688c8413bd6a87b0bc14b72284465cf96e3578e" +checksum = "64a70547e8f9d85da0f5af609143f7bde3ac7457a6e1073104d9b73d6c5ac744" dependencies = [ "base64 0.13.1", "block",