diff --git a/.gitignore b/.gitignore index 24f7b19..b2a8701 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ target/ .env history.txt +trace-*.json diff --git a/Cargo.lock b/Cargo.lock index a8bb1eb..918d34a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,6 +172,8 @@ dependencies = [ "serde", "surrealdb", "thiserror", + "tokio", + "tracing", "ulid", ] @@ -333,6 +335,7 @@ dependencies = [ "thiserror", "tower-sessions", "tower-sessions-surrealdb-store", + "tracing", ] [[package]] @@ -573,6 +576,7 @@ dependencies = [ "serde", "surrealdb", "thiserror", + "tracing", "ulid", ] @@ -2723,6 +2727,15 @@ dependencies = [ "quote", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchit" version = "0.7.3" @@ -2897,6 +2910,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.4" @@ -3085,6 +3108,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owo-colors" version = "3.5.0" @@ -3730,10 +3759,19 @@ checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata", + "regex-automata 0.4.5", "regex-syntax 0.8.2", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + [[package]] name = "regex-automata" version = "0.4.5" @@ -3745,6 +3783,12 @@ dependencies = [ "regex-syntax 0.8.2", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.7.5" @@ -4540,6 +4584,7 @@ dependencies = [ "serde", "server_fn", "thiserror", + "tracing", "validation", "validator", "web-sys", @@ -4574,6 +4619,8 @@ dependencies = [ "tokio", "tower", "tower-http", + "tracing-chrome", + "tracing-subscriber", ] [[package]] @@ -5377,6 +5424,17 @@ dependencies = [ "syn 2.0.49", ] +[[package]] +name = "tracing-chrome" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496b3cd5447f7ff527bbbf19b071ad542a000adf297d4127078b4dfdb931f41a" +dependencies = [ + "serde_json", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "tracing-core" version = "0.1.32" @@ -5397,15 +5455,33 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", "sharded-slab", + "smallvec", "thread_local", + "tracing", "tracing-core", + "tracing-log", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b7b2870..9043952 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ lto = true codegen-units = 1 [workspace.dependencies] -axum = { version = "0.7.4", features = ["macros"] } +axum = { version = "0.7.4", features = ["macros", "tracing"] } axum-login = "0.13" base64 = { version = "0.21" } bytes = "1.5.0" @@ -34,6 +34,7 @@ thiserror = "1" tokio = { version = "1", features = ["full"] } tower = { version = "0.4", features = ["full"] } tower-http = { version = "0.5", features = ["full"] } +tracing = { version = "0.1" } ulid = { version = "1.1", features = [ "serde" ] } wasm-bindgen = "=0.2.89" diff --git a/crates/artifact/Cargo.toml b/crates/artifact/Cargo.toml index 5816116..ec1444a 100644 --- a/crates/artifact/Cargo.toml +++ b/crates/artifact/Cargo.toml @@ -14,6 +14,8 @@ serde.workspace = true color-eyre.workspace = true surrealdb.workspace = true ulid.workspace = true +tokio = { workspace = true, features = [ "fs" ] } +tracing.workspace = true core_types = { path = "../core_types", features = [ "ssr" ] } clients = { path = "../clients" } diff --git a/crates/artifact/src/lib.rs b/crates/artifact/src/lib.rs index 64ef5ff..1d2b83d 100644 --- a/crates/artifact/src/lib.rs +++ b/crates/artifact/src/lib.rs @@ -21,14 +21,21 @@ //! We have separate types (and tables) to enforce the publicity state of our //! artifacts with the type system. +mod methods; + use std::future::Future; -use color_eyre::eyre::{OptionExt, Result, WrapErr}; -use core_types::{NewId, PrivateArtifact, PublicArtifact}; +use color_eyre::eyre::{Result, WrapErr}; +use core_types::{PrivateArtifact, PublicArtifact}; + +use self::methods::*; const ARTIFACT_PRIVATE_LTS_BUCKET: &str = "picturepro-artifact-private-lts"; const ARTIFACT_PUBLIC_LTS_BUCKET: &str = "picturepro-artifact-public-lts"; +type ObjectStoreGenerator = + Box Result> + Send + 'static>; + /// The core artifact trait. pub trait Artifact { /// The type of the ID of the artifact. @@ -49,7 +56,20 @@ pub trait Artifact { /// fails if the `contents` field is `None`. fn upload(&self) -> impl Future> + Send; /// Convenience method for uploading and pushing to SurrealDB. - fn upload_and_push(&self) -> impl Future> + Send; + fn upload_and_push(&self) -> impl Future> + Send + where + Self: Sync, + { + async move { + self.upload().await.wrap_err("Failed to upload artifact")?; + self + .push_to_surreal() + .await + .wrap_err("Failed to push to surreal")?; + + Ok(()) + } + } /// Push the artifact to SurrealDB. fn push_to_surreal(&self) -> impl Future> + Send; @@ -58,7 +78,7 @@ pub trait Artifact { id: Self::Id, ) -> impl Future>> + Send; /// Get the object store for the artifact. - fn object_store(&self) -> Result>; + fn object_store() -> Result>; } impl Artifact for PublicArtifact { @@ -68,7 +88,6 @@ impl Artifact for PublicArtifact { let id = core_types::PublicArtifactRecordId(ulid::Ulid::new()); Self::new_with_id(id, contents) } - fn new_with_id(id: Self::Id, contents: Option) -> Self { Self { id, @@ -81,99 +100,32 @@ impl Artifact for PublicArtifact { ), } } - async fn download(&mut self) -> Result<()> { - let object_store = self.object_store()?; - let path = object_store::path::Path::from(self.id.0.to_string()); - - let contents = object_store - .get(&path) - .await - .wrap_err("Failed to download artifact")?; - - self.contents = Some( - contents - .bytes() - .await - .wrap_err("Failed to read bytes of downloaded artifact")?, - ); + let object_store = Box::new(|| Self::object_store()); + self.contents = + Some(download_artifact(object_store, &self.id.0.to_string()).await?); Ok(()) } - async fn upload(&self) -> Result<()> { - let object_store = self.object_store()?; - let path = object_store::path::Path::from(self.id.0.to_string()); - - object_store - .put( - &path, - self.contents.clone().ok_or_eyre("No contents to upload")?, - ) - .await - .wrap_err("Failed to upload artifact")?; + let object_store = Box::new(|| Self::object_store()); + upload_artifact( + object_store, + &self.id.0.to_string(), + self.contents.clone().unwrap(), + ) + .await?; Ok(()) } - - async fn upload_and_push(&self) -> Result<()> { - self.upload().await.wrap_err("Failed to upload artifact")?; - self - .push_to_surreal() - .await - .wrap_err("Failed to push to surreal")?; - - Ok(()) - } - async fn push_to_surreal(&self) -> Result<()> { - let client = clients::surreal::SurrealRootClient::new() - .await - .wrap_err("Failed to create surreal client")?; - - client.use_ns("main").use_db("main").await?; - - let pushed_artifact: Vec = client - .create(Self::Id::TABLE) - .content(self.clone()) - .await - .wrap_err("Failed to create artifact in surreal")?; - - let _pushed_artifact = pushed_artifact - .first() - .ok_or_eyre("Failed to create artifact in surreal")?; - - Ok(()) + push_to_surreal::(self.clone()).await } - async fn pull_from_surreal(id: Self::Id) -> Result> { - let client = clients::surreal::SurrealRootClient::new() - .await - .wrap_err("Failed to create surreal client")?; - - client.use_ns("main").use_db("main").await?; - let artifact: Option = client - .select((Self::Id::TABLE, id.id_with_brackets())) - .await - .wrap_err("Failed to get artifact from surreal")?; - - let artifact = artifact.ok_or_eyre("Artifact does not exist in surreal")?; - - Ok(Box::new(artifact)) + pull_from_surreal::(id).await } - - fn object_store(&self) -> Result> { - let object_store = object_store::aws::AmazonS3Builder::from_env() - .with_region( - std::env::var("AWS_DEFAULT_REGION") - .wrap_err("Failed to get AWS region from environment") - .unwrap(), - ) - .with_bucket_name(ARTIFACT_PUBLIC_LTS_BUCKET) - .build() - .wrap_err("Failed to create object store")?; - - Ok(Box::new(object_store)) + fn object_store() -> Result> { + object_store_from_env(ARTIFACT_PUBLIC_LTS_BUCKET) } } @@ -184,102 +136,34 @@ impl Artifact for PrivateArtifact { let id = core_types::PrivateArtifactRecordId(ulid::Ulid::new()); Self::new_with_id(id, contents) } - fn new_with_id(id: Self::Id, contents: Option) -> Self { Self { id, contents } } - async fn download(&mut self) -> Result<()> { - let object_store = self.object_store()?; - let path = object_store::path::Path::from(self.id.0.to_string()); - - let contents = object_store - .get(&path) - .await - .wrap_err("Failed to download artifact")?; - - self.contents = Some( - contents - .bytes() - .await - .wrap_err("Failed to read bytes of downloaded artifact")?, - ); + let object_store = Box::new(|| Self::object_store()); + self.contents = + Some(download_artifact(object_store, &self.id.0.to_string()).await?); Ok(()) } - async fn upload(&self) -> Result<()> { - let object_store = self.object_store()?; - let path = object_store::path::Path::from(self.id.0.to_string()); - - object_store - .put( - &path, - self.contents.clone().ok_or_eyre("No contents to upload")?, - ) - .await - .wrap_err("Failed to upload artifact")?; - - Ok(()) - } - - async fn upload_and_push(&self) -> Result<()> { - self.upload().await.wrap_err("Failed to upload artifact")?; - self - .push_to_surreal() - .await - .wrap_err("Failed to push to surreal")?; + let object_store = Box::new(|| Self::object_store()); + upload_artifact( + object_store, + &self.id.0.to_string(), + self.contents.clone().unwrap(), + ) + .await?; Ok(()) } - async fn push_to_surreal(&self) -> Result<()> { - let client = clients::surreal::SurrealRootClient::new() - .await - .wrap_err("Failed to create surreal client")?; - - client.use_ns("main").use_db("main").await?; - - let pushed_artifact: Vec = client - .create(Self::Id::TABLE) - .content(self.clone()) - .await - .wrap_err("Failed to create artifact in surreal")?; - - let _pushed_artifact = pushed_artifact - .first() - .ok_or_eyre("Failed to create artifact in surreal")?; - - Ok(()) + push_to_surreal::(self.clone()).await } - async fn pull_from_surreal(id: Self::Id) -> Result> { - let client = clients::surreal::SurrealRootClient::new() - .await - .wrap_err("Failed to create surreal client")?; - - client.use_ns("main").use_db("main").await?; - let artifact: Option = client - .select((Self::Id::TABLE, id.id_with_brackets())) - .await - .wrap_err("Failed to get artifact from surreal")?; - - let artifact = artifact.ok_or_eyre("Artifact does not exist in surreal")?; - - Ok(Box::new(artifact)) + pull_from_surreal::(id).await } - - fn object_store(&self) -> Result> { - let object_store = object_store::aws::AmazonS3Builder::from_env() - .with_region( - std::env::var("AWS_DEFAULT_REGION") - .wrap_err("Failed to get AWS region from environment") - .unwrap(), - ) - .with_bucket_name(ARTIFACT_PRIVATE_LTS_BUCKET) - .build() - .wrap_err("Failed to create object store")?; - - Ok(Box::new(object_store)) + fn object_store() -> Result> { + object_store_from_env(ARTIFACT_PRIVATE_LTS_BUCKET) } } diff --git a/crates/artifact/src/methods.rs b/crates/artifact/src/methods.rs new file mode 100644 index 0000000..58d7a1c --- /dev/null +++ b/crates/artifact/src/methods.rs @@ -0,0 +1,139 @@ +use color_eyre::eyre::{OptionExt, Result, WrapErr}; +use core_types::NewId; +use tracing::instrument; + +use crate::ObjectStoreGenerator; + +pub fn object_store_from_env( + bucket_name: &str, +) -> Result> { + let object_store = object_store::aws::AmazonS3Builder::from_env() + .with_region( + std::env::var("AWS_DEFAULT_REGION") + .wrap_err("Failed to get AWS region from environment")?, + ) + .with_bucket_name(bucket_name) + .build() + .wrap_err("Failed to create object store")?; + + Ok(Box::new(object_store)) +} + +fn cache_path(id: &str) -> std::path::PathBuf { std::env::temp_dir().join(id) } + +#[instrument(skip(object_store))] +pub async fn download_artifact( + object_store: ObjectStoreGenerator, + id: &str, +) -> Result { + let cache_path = cache_path(id); + if cache_path.exists() { + let contents = tokio::fs::read(cache_path) + .await + .wrap_err("Failed to read cached artifact")?; + return Ok(contents.into()); + } + + let object_store = object_store()?; + let contents = inner_download_artifact(&*object_store, id).await?; + tokio::fs::write(&cache_path, &contents) + .await + .wrap_err("Failed to write cached artifact")?; + + Ok(contents) +} + +#[instrument(skip(object_store))] +async fn inner_download_artifact( + object_store: &dyn object_store::ObjectStore, + id: &str, +) -> Result { + let path = object_store::path::Path::from(id); + + let contents = object_store + .get(&path) + .await + .wrap_err("Failed to download artifact")?; + + contents + .bytes() + .await + .wrap_err("Failed to read bytes of downloaded artifact") +} + +#[instrument(skip(object_store))] +pub async fn upload_artifact( + object_store: ObjectStoreGenerator, + id: &str, + contents: bytes::Bytes, +) -> Result<()> { + let cache_path = cache_path(id); + tokio::fs::write(&cache_path, &contents) + .await + .wrap_err("Failed to write cached artifact")?; + + let object_store = object_store()?; + inner_upload_artifact(&*object_store, id, contents).await +} + +#[instrument(skip(object_store))] +async fn inner_upload_artifact( + object_store: &dyn object_store::ObjectStore, + id: &str, + contents: bytes::Bytes, +) -> Result<()> { + let path = object_store::path::Path::from(id); + + object_store + .put(&path, contents) + .await + .wrap_err("Failed to upload artifact")?; + + Ok(()) +} + +#[instrument(skip(artifact))] +pub async fn push_to_surreal(artifact: T) -> Result<()> +where + Id: NewId, + T: serde::Serialize + for<'a> serde::Deserialize<'a> + Clone, +{ + let client = clients::surreal::SurrealRootClient::new() + .await + .wrap_err("Failed to create surreal client")?; + + client.use_ns("main").use_db("main").await?; + + let pushed_artifact: Vec = client + .create(Id::TABLE) + .content(artifact) + .await + .wrap_err("Failed to create artifact in surreal")?; + + let _pushed_artifact = pushed_artifact + .first() + .ok_or_eyre("Failed to create artifact in surreal")?; + + Ok(()) +} + +#[instrument(skip(id))] +pub async fn pull_from_surreal(id: Id) -> Result> +where + Id: NewId, + T: serde::Serialize + for<'a> serde::Deserialize<'a> + Clone, +{ + let client = clients::surreal::SurrealRootClient::new() + .await + .wrap_err("Failed to create surreal client")?; + + client.use_ns("main").use_db("main").await?; + let artifact: Option = client + .select((Id::TABLE, id.id_with_brackets())) + .await + .wrap_err("Failed to get artifact from surreal")?; + + let artifact = artifact.ok_or_eyre("Artifact does not exist in surreal")?; + + Ok(Box::new(artifact)) +} diff --git a/crates/auth/Cargo.toml b/crates/auth/Cargo.toml index e2b0a19..2682fbe 100644 --- a/crates/auth/Cargo.toml +++ b/crates/auth/Cargo.toml @@ -15,6 +15,7 @@ axum-login.workspace = true serde.workspace = true color-eyre.workspace = true thiserror.workspace = true +tracing.workspace = true surrealdb.workspace = true clients = { path = "../clients" } diff --git a/crates/auth/src/lib.rs b/crates/auth/src/lib.rs index 3119865..3c6e36d 100644 --- a/crates/auth/src/lib.rs +++ b/crates/auth/src/lib.rs @@ -10,6 +10,7 @@ use color_eyre::eyre::{eyre, Context, OptionExt, Result}; use core_types::NewId; use serde::{Deserialize, Serialize}; use surrealdb::engine::remote::ws::Client; +use tracing::instrument; /// The credentials type for the authentication layer. /// @@ -44,6 +45,7 @@ impl Backend { /// /// This method has checks to ensure that a user with the given email does /// not already exist. + #[instrument(skip(password))] pub async fn signup( &self, name: String, @@ -92,6 +94,7 @@ impl AuthnBackend for Backend { type Credentials = Credentials; type Error = surrealdb::Error; + #[instrument(skip(self, credentials))] async fn authenticate( &self, credentials: Self::Credentials, @@ -111,6 +114,7 @@ impl AuthnBackend for Backend { Ok(user) } + #[instrument(skip(self))] async fn get_user( &self, user_id: &UserId, diff --git a/crates/bl/Cargo.toml b/crates/bl/Cargo.toml index ca5d6b1..953061d 100644 --- a/crates/bl/Cargo.toml +++ b/crates/bl/Cargo.toml @@ -14,6 +14,7 @@ bytes.workspace = true color-eyre.workspace = true image.workspace = true thiserror.workspace = true +tracing.workspace = true surrealdb.workspace = true serde.workspace = true ulid.workspace = true diff --git a/crates/bl/src/lib.rs b/crates/bl/src/lib.rs index 976f86b..4a2e7a7 100644 --- a/crates/bl/src/lib.rs +++ b/crates/bl/src/lib.rs @@ -7,6 +7,7 @@ use core_types::{ PrivateArtifact, PublicArtifact, }; use serde::{Deserialize, Serialize}; +use tracing::instrument; #[derive(Clone, Debug, Deserialize, Serialize, thiserror::Error)] pub enum PhotoUploadError { @@ -26,6 +27,7 @@ fn thumbnail_size(aspect_ratio: f32) -> (u32, u32) { } } +#[instrument(skip(original_bytes))] pub async fn upload_single_photo( user_id: core_types::UserRecordId, original_bytes: Bytes, @@ -142,6 +144,7 @@ pub async fn upload_single_photo( Ok(group.clone()) } +#[instrument] pub async fn get_user_photo_groups( user_id: core_types::UserRecordId, ) -> Result> { diff --git a/crates/site-app/Cargo.toml b/crates/site-app/Cargo.toml index b15b56a..3579d62 100644 --- a/crates/site-app/Cargo.toml +++ b/crates/site-app/Cargo.toml @@ -19,6 +19,7 @@ serde.workspace = true bytes = { workspace = true, optional = true } base64 = { workspace = true, optional = true } image = { workspace = true, optional = true } +tracing = { workspace = true, optional = true } validator = "0.16.1" web-sys = { version = "0.3", features = [ "Window", "EventTarget" ] } @@ -35,7 +36,7 @@ bl = { path = "../bl", optional = true } default = [] hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"] ssr = [ - "leptos/ssr", "leptos_meta/ssr", "leptos_router/ssr", "dep:leptos_axum", - "artifact", "auth", "base64", "bl", "bytes", "clients", "image" + "leptos/ssr", "leptos/tracing", "leptos_meta/ssr", "leptos_router/ssr", "dep:leptos_axum", + "artifact", "auth", "base64", "bl", "bytes", "clients", "image", "tracing" ] diff --git a/crates/site-app/src/components/gallery.rs b/crates/site-app/src/components/gallery.rs index 7e88234..5a026d2 100644 --- a/crates/site-app/src/components/gallery.rs +++ b/crates/site-app/src/components/gallery.rs @@ -66,6 +66,7 @@ fn PhotoGroup(group: core_types::PhotoGroup) -> impl IntoView { } } +#[cfg_attr(feature = "ssr", tracing::instrument)] #[server] pub async fn fetch_user_photo_groups( user_id: core_types::UserRecordId, diff --git a/crates/site-app/src/components/photo.rs b/crates/site-app/src/components/photo.rs index 3cd4ca2..f6ac0fc 100644 --- a/crates/site-app/src/components/photo.rs +++ b/crates/site-app/src/components/photo.rs @@ -38,15 +38,18 @@ pub fn Photo(photo_id: core_types::PhotoRecordId) -> impl IntoView { } } +#[cfg_attr(feature = "ssr", tracing::instrument)] #[server] pub async fn fetch_photo_thumbnail( photo_id: core_types::PhotoRecordId, ) -> Result { use artifact::Artifact; use base64::prelude::*; + use tracing::{info_span, Instrument}; // prep the surreal client let surreal_client = clients::surreal::SurrealRootClient::new() + .instrument(info_span!("create_surreal_client")) .await .map_err(|e| { ServerFnError::new(format!("Failed to create surreal client: {e:?}")) @@ -96,20 +99,27 @@ pub async fn fetch_photo_thumbnail( }; // load using the image crate - let thumbnail_image = image::load_from_memory(&thumbnail_artifact_content) - .map_err(|e| { - ServerFnError::new(format!("Failed to load thumbnail as image: {e:?}")) - })?; + // let thumbnail_image = + // info_span!("load_image_from_artifact_data").in_scope(|| { + // image::load_from_memory(&thumbnail_artifact_content).map_err(|e| { + // ServerFnError::new(format!("Failed to load thumbnail as image: + // {e:?}")) }) + // })?; - // encode to avif bytes - let mut buffer = Vec::new(); - let encoder = image::codecs::avif::AvifEncoder::new(&mut buffer); - thumbnail_image.write_with_encoder(encoder).map_err(|e| { - ServerFnError::new(format!("Failed to encode thumbnail: {e:?}")) - })?; + // // encode to avif bytes + // let mut buffer = Vec::new(); + // let encoder = image::codecs::avif::AvifEncoder::new(&mut buffer); + // info_span!("encode_thumbnail_to_avif").in_scope(|| { + // thumbnail_image.write_with_encoder(encoder).map_err(|e| { + // ServerFnError::new(format!("Failed to encode thumbnail: {e:?}")) + // }) + // })?; + + // // encode avif bytes to base64 + // let data = info_span!("encode_thumbnail_to_base64") + // .in_scope(|| BASE64_STANDARD.encode(&buffer)); - // encode avif bytes to base64 - let data = BASE64_STANDARD.encode(&buffer); + let data = BASE64_STANDARD.encode(&thumbnail_artifact_content); Ok(PhotoThumbnailDisplayParams { data, diff --git a/crates/site-app/src/components/photo_upload.rs b/crates/site-app/src/components/photo_upload.rs index 736b1e3..ae9a8a9 100644 --- a/crates/site-app/src/components/photo_upload.rs +++ b/crates/site-app/src/components/photo_upload.rs @@ -39,6 +39,7 @@ pub fn PhotoUpload() -> impl IntoView { } } +#[cfg_attr(feature = "ssr", tracing::instrument)] #[server(input = MultipartFormData)] pub async fn photo_upload( data: MultipartData, diff --git a/crates/site-app/src/lib.rs b/crates/site-app/src/lib.rs index 3630140..cc01199 100644 --- a/crates/site-app/src/lib.rs +++ b/crates/site-app/src/lib.rs @@ -98,6 +98,7 @@ pub fn LogoutButton(class: Option) -> impl IntoView { } } +#[cfg_attr(feature = "ssr", tracing::instrument)] #[server(Logout)] pub async fn logout() -> Result<(), ServerFnError> { let mut auth_session = use_context::() diff --git a/crates/site-app/src/pages/auth/login_page.rs b/crates/site-app/src/pages/auth/login_page.rs index eff093b..a9196e0 100644 --- a/crates/site-app/src/pages/auth/login_page.rs +++ b/crates/site-app/src/pages/auth/login_page.rs @@ -80,6 +80,7 @@ pub fn LoginPageInner() -> impl IntoView { } } +#[cfg_attr(feature = "ssr", tracing::instrument)] #[server(Login)] pub async fn login(params: LoginParams) -> Result { params.validate().map_err(|e| { diff --git a/crates/site-app/src/pages/auth/signup_page.rs b/crates/site-app/src/pages/auth/signup_page.rs index 461c0cf..103dfd8 100644 --- a/crates/site-app/src/pages/auth/signup_page.rs +++ b/crates/site-app/src/pages/auth/signup_page.rs @@ -84,6 +84,7 @@ pub fn SignupPageInner() -> impl IntoView { } } +#[cfg_attr(feature = "ssr", tracing::instrument)] #[server(Signup)] pub async fn login(params: SignupParams) -> Result<(), ServerFnError> { params.validate().map_err(|e| { diff --git a/crates/site-server/Cargo.toml b/crates/site-server/Cargo.toml index fc85e3e..b818eed 100644 --- a/crates/site-server/Cargo.toml +++ b/crates/site-server/Cargo.toml @@ -10,7 +10,7 @@ site-app = { path = "../site-app", default-features = false, features = ["ssr"] core_types = { path = "../core_types" } auth = { path = "../auth" } -leptos = { workspace = true, features = [ "ssr" ]} +leptos = { workspace = true, features = [ "ssr", "tracing" ]} leptos_axum.workspace = true leptos_router.workspace = true @@ -21,3 +21,10 @@ tokio.workspace = true tower.workspace = true tower-http.workspace = true log.workspace = true + +tracing-chrome = { version = "0.7", optional = true } +tracing-subscriber = { version = "0.3", features = [ "env-filter" ] } + +[features] +default = [] +chrome-tracing = [ "tracing-chrome" ] diff --git a/crates/site-server/src/main.rs b/crates/site-server/src/main.rs index e5a6331..9b01250 100644 --- a/crates/site-server/src/main.rs +++ b/crates/site-server/src/main.rs @@ -60,12 +60,34 @@ async fn leptos_routes_handler( handler(req).await.into_response() } +fn init_logging() { + color_eyre::install().expect("Failed to install color_eyre"); + + let filter = tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or( + tracing_subscriber::EnvFilter::new("info,site_server=debug,site_app=debug"), + ); + + #[cfg(not(feature = "chrome-tracing"))] + { + tracing_subscriber::fmt().with_env_filter(filter).init(); + } + #[cfg(feature = "chrome-tracing")] + { + use tracing_subscriber::prelude::*; + + let (chrome_layer, _guard) = + tracing_chrome::ChromeLayerBuilder::new().build(); + tracing_subscriber::registry() + .with(tracing_subscriber::fmt::layer()) + .with(filter) + .with(chrome_layer) + .init(); + } +} + #[tokio::main] async fn main() -> Result<()> { - color_eyre::install()?; - - simple_logger::init_with_level(log::Level::Debug) - .expect("couldn't initialize logging"); + init_logging(); // Setting get_configuration(None) means we'll be using cargo-leptos's env // values For deployment these variables are: