From d6451ee782496b21cbad7358a6ca1dcc502ff415 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 6 May 2025 20:15:03 -0400 Subject: [PATCH 1/4] Add Simple OpenTelemetry setup --- Cargo.lock | 673 +++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 7 + src/main.rs | 94 ++++++-- 3 files changed, 749 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dca8e11..9f8bb85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,8 +87,8 @@ dependencies = [ "encoding_rs", "flate2", "futures-core", - "h2", - "http", + "h2 0.3.26", + "http 0.2.12", "httparse", "httpdate", "itoa", @@ -162,7 +162,7 @@ checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" dependencies = [ "bytestring", "cfg-if", - "http", + "http 0.2.12", "regex", "regex-lite", "serde", @@ -449,6 +449,45 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.4.0" @@ -478,6 +517,53 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http 1.3.1", + "http-body", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower 0.5.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.3.1", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -1138,6 +1224,12 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + [[package]] name = "h2" version = "0.3.26" @@ -1149,14 +1241,39 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", - "indexmap", + "http 0.2.12", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "h2" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.3.1", + "indexmap 2.6.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.15.1" @@ -1186,6 +1303,40 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.3.1", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.3.1", + "http-body", + "pin-project-lite", +] + [[package]] name = "http-range" version = "0.1.5" @@ -1210,6 +1361,59 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.8", + "http 1.3.1", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.3.1", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.61" @@ -1417,12 +1621,19 @@ dependencies = [ "lazy_static", "log", "notify", + "opentelemetry", + "opentelemetry-appender-log", + "opentelemetry-otlp", + "opentelemetry-resource-detectors", + "opentelemetry-stdout", + "opentelemetry_sdk", "path-absolutize", "prometheus", "rand", "rayon", "serde", "serde_json", + "tempfile", "tokio", "walkdir", ] @@ -1439,6 +1650,16 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae21c3177a27788957044151cc2800043d127acaa460a47ebb9b84dfa2c6aa0" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.6.0" @@ -1446,7 +1667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.1", ] [[package]] @@ -1489,6 +1710,12 @@ dependencies = [ "syn", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -1666,6 +1893,12 @@ dependencies = [ "imgref", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "maybe-rayon" version = "0.1.1" @@ -1872,6 +2105,130 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "opentelemetry" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "236e667b670a5cdf90c258f5a55794ec5ac5027e960c224bff8367a59e1e6426" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "opentelemetry-appender-log" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bbd76b0dafbb54e8631ca147e7e090d09616ae7da45781d5403a83ac9af4290" +dependencies = [ + "log", + "opentelemetry", +] + +[[package]] +name = "opentelemetry-http" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8863faf2910030d139fb48715ad5ff2f35029fc5f244f6d5f689ddcf4d26253" +dependencies = [ + "async-trait", + "bytes", + "http 1.3.1", + "opentelemetry", + "reqwest", + "tracing", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bef114c6d41bea83d6dc60eb41720eedd0261a67af57b66dd2b84ac46c01d91" +dependencies = [ + "async-trait", + "futures-core", + "http 1.3.1", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost", + "reqwest", + "thiserror 2.0.12", + "tokio", + "tonic", + "tracing", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f8870d3024727e99212eb3bb1762ec16e255e3e6f58eeb3dc8db1aa226746d" +dependencies = [ + "opentelemetry", + "opentelemetry_sdk", + "prost", + "tonic", +] + +[[package]] +name = "opentelemetry-resource-detectors" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0cd3cf373f6f7f3a8f25a189acf1300c8b87e85f7959b45ba83c01e305f5cc3" +dependencies = [ + "opentelemetry", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fb3a2f78c2d55362cd6c313b8abedfbc0142ab3c2676822068fd2ab7d51f9b7" + +[[package]] +name = "opentelemetry-stdout" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb0e5a5132e4b80bf037a78e3e12c8402535199f5de490d0c38f7eac71bc831" +dependencies = [ + "async-trait", + "chrono", + "futures-util", + "opentelemetry", + "opentelemetry_sdk", + "serde", + "thiserror 2.0.12", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84dfad6042089c7fc1f6118b7040dc2eb4ab520abbf410b79dc481032af39570" +dependencies = [ + "async-trait", + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "opentelemetry", + "percent-encoding", + "rand", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "tracing", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -1941,6 +2298,26 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.15" @@ -2027,7 +2404,30 @@ dependencies = [ "memchr", "parking_lot", "protobuf", - "thiserror", + "thiserror 1.0.69", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2111,7 +2511,7 @@ dependencies = [ "rand_chacha", "simd_helpers", "system-deps", - "thiserror", + "thiserror 1.0.69", "v_frame", "wasm-bindgen", ] @@ -2195,6 +2595,43 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "reqwest" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http 1.3.1", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower 0.5.2", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + [[package]] name = "rgb" version = "0.8.50" @@ -2244,6 +2681,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + [[package]] name = "ryu" version = "1.0.18" @@ -2382,7 +2825,7 @@ checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ "num-bigint", "num-traits", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -2452,6 +2895,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.1" @@ -2501,7 +2953,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -2515,6 +2976,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.3.36" @@ -2570,9 +3042,32 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "socket2", + "tokio-macros", "windows-sys 0.52.0", ] +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.12" @@ -2613,13 +3108,90 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap", + "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", "winnow", ] +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.22.1", + "bytes", + "h2 0.4.8", + "http 1.3.1", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "socket2", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.40" @@ -2628,9 +3200,21 @@ checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing-core" version = "0.1.32" @@ -2640,6 +3224,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.17.0" @@ -2738,6 +3328,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2770,6 +3369,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.95" @@ -2799,6 +3410,16 @@ version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +[[package]] +name = "web-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi-util" version = "0.1.9" @@ -2817,6 +3438,36 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 9b5e957..f4a0ecd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,3 +37,10 @@ prometheus = "0.13" lazy_static = "1.5" anyhow = "1.0" rand = "0.8.5" +tempfile = "3.14.0" +opentelemetry = { version = "0.28.0", features = ["default", "metrics", "tracing"] } +opentelemetry_sdk = { version = "0.28.0", features = ["default", "rt-tokio-current-thread", "tracing", "metrics"] } +opentelemetry-otlp = { version = "0.28.0", features = ["default", "metrics", "tracing", "grpc-tonic"] } +opentelemetry-stdout = "0.28.0" +opentelemetry-appender-log = "0.28.0" +opentelemetry-resource-detectors = "0.7.0" diff --git a/src/main.rs b/src/main.rs index b46338d..68df53e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,18 +32,20 @@ use diesel::sqlite::Sqlite; use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; use rayon::prelude::*; -use log::{debug, error, info, trace, warn}; - use crate::auth::login; use crate::data::*; use crate::database::*; use crate::files::{ is_image_or_video, is_valid_full_path, move_file, RealFileSystem, RefreshThumbnailsMessage, }; +use crate::otel::global_tracer; use crate::service::ServiceBuilder; use crate::state::AppState; use crate::tags::*; use crate::video::*; +use log::{debug, error, info, trace, warn}; +use opentelemetry::trace::{Span, Status, TraceContextExt, Tracer}; +use opentelemetry::{global, KeyValue}; mod auth; mod data; @@ -54,6 +56,7 @@ mod state; mod tags; mod video; +mod otel; mod service; #[cfg(test)] mod testhelpers; @@ -113,6 +116,8 @@ async fn get_file_metadata( path: web::Query, app_state: Data, ) -> impl Responder { + let tracer = global_tracer(); + let mut span = tracer.start("get_file_metadata"); match is_valid_full_path(&app_state.base_path, &path.path, false) .ok_or_else(|| ErrorKind::InvalidData.into()) .and_then(File::open) @@ -120,10 +125,19 @@ async fn get_file_metadata( { Ok(metadata) => { let response: MetadataResponse = metadata.into(); + span.add_event( + "Metadata fetched", + vec![KeyValue::new("file", path.path.clone())], + ); + span.set_status(Status::Ok); + HttpResponse::Ok().json(response) } Err(e) => { - error!("Error getting metadata for file '{}': {:?}", path.path, e); + let message = format!("Error getting metadata for file '{}': {:?}", path.path, e); + error!("{}", message); + span.set_status(Status::error(message)); + HttpResponse::InternalServerError().finish() } } @@ -135,6 +149,9 @@ async fn upload_image( mut payload: mp::Multipart, app_state: Data, ) -> impl Responder { + let tracer = global_tracer(); + let mut span = tracer.start("upload_image"); + let mut file_content: BytesMut = BytesMut::new(); let mut file_name: Option = None; let mut file_path: Option = None; @@ -167,6 +184,10 @@ async fn upload_image( &full_path.to_str().unwrap().to_string(), true, ) { + let context = opentelemetry::Context::new().with_remote_span_context(span.span_context().clone()); + tracer.span_builder("file write") + .start_with_context(&tracer, &context); + if !full_path.is_file() && is_image_or_video(&full_path) { let mut file = File::create(&full_path).unwrap(); file.write_all(&file_content).unwrap(); @@ -193,13 +214,16 @@ async fn upload_image( } } else { error!("Invalid path for upload: {:?}", full_path); + span.set_status(Status::error("Invalid path for upload")); return HttpResponse::BadRequest().body("Path was not valid"); } } else { + span.set_status(Status::error("No file body read")); return HttpResponse::BadRequest().body("No file body read"); } app_state.stream_manager.do_send(RefreshThumbnailsMessage); + span.set_status(Status::Ok); HttpResponse::Ok().finish() } @@ -210,6 +234,9 @@ async fn generate_video( app_state: Data, body: web::Json, ) -> impl Responder { + let tracer = global_tracer(); + let mut span = tracer.start("generate_video"); + let filename = PathBuf::from(&body.path); if let Some(name) = filename.file_name() { @@ -217,17 +244,29 @@ async fn generate_video( let playlist = format!("{}/{}.m3u8", app_state.video_path, filename); if let Some(path) = is_valid_full_path(&app_state.base_path, &body.path, false) { if let Ok(child) = create_playlist(path.to_str().unwrap(), &playlist).await { - app_state - .stream_manager - .do_send(ProcessMessage(playlist.clone(), child)); + span.add_event( + "playlist_created".to_string(), + vec![KeyValue::new("playlist-name", filename.to_string())], + ); + + span.set_status(Status::Ok); + app_state.stream_manager.do_send(ProcessMessage( + playlist.clone(), + child, + // opentelemetry::Context::new().with_span(span), + )); } } else { + span.set_status(Status::error(format!("invalid path {:?}", &body.path))); return HttpResponse::BadRequest().finish(); } HttpResponse::Ok().json(playlist) } else { - error!("Unable to get file name: {:?}", filename); + let message = format!("Unable to get file name: {:?}", filename); + error!("{}", message); + span.set_status(Status::error(message)); + HttpResponse::BadRequest().finish() } } @@ -239,6 +278,9 @@ async fn stream_video( path: web::Query, app_state: Data, ) -> impl Responder { + let tracer = global::tracer("image-server"); + let mut span = tracer.start("stream_video"); + let playlist = &path.path; debug!("Playlist: {}", playlist); @@ -246,10 +288,14 @@ async fn stream_video( if !playlist.starts_with(&app_state.video_path) && is_valid_full_path(&app_state.base_path, playlist, false).is_some() { + span.set_status(Status::error(format!("playlist not valid {}", playlist))); + HttpResponse::BadRequest().finish() } else if let Ok(file) = NamedFile::open(playlist) { + span.set_status(Status::Ok); file.into_response(&request) } else { + span.set_status(Status::error(format!("playlist not found {}", playlist))); HttpResponse::NotFound().finish() } } @@ -261,6 +307,9 @@ async fn get_video_part( path: web::Path, app_state: Data, ) -> impl Responder { + let tracer = global::tracer("image-server"); + let mut span = tracer.start("get_video_part"); + let part = &path.path; debug!("Video part: {}", part); @@ -269,9 +318,11 @@ async fn get_video_part( file_part.push(part); // TODO: Do we need to guard against directory attacks here? if let Ok(file) = NamedFile::open(&file_part) { + span.set_status(Status::Ok); file.into_response(&request) } else { error!("Video part not found: {:?}", file_part); + span.set_status(Status::error(format!("Video part not found '{}'", file_part.to_str().unwrap()))); HttpResponse::NotFound().finish() } } @@ -376,6 +427,9 @@ async fn delete_favorite( } fn create_thumbnails() { + let tracer = global_tracer(); + let span = tracer.start("creating thumbnails"); + let thumbs = &dotenv::var("THUMBNAILS").expect("THUMBNAILS not defined"); let thumbnail_directory: &Path = Path::new(thumbs); @@ -398,8 +452,18 @@ fn create_thumbnails() { ) .expect("Error creating directory"); + let mut video_span = tracer.start_with_context( + "generate_video_thumbnail", + &opentelemetry::Context::new().with_remote_span_context(span.span_context().clone()), + ); + video_span.set_attributes(vec![ + KeyValue::new("type", "video"), + KeyValue::new("file-name", thumb_path.display().to_string()), + ]); + debug!("Generating video thumbnail: {:?}", thumb_path); generate_video_thumbnail(entry.path(), &thumb_path); + video_span.end(); false } else { is_image(entry) @@ -474,15 +538,19 @@ fn main() -> std::io::Result<()> { if let Err(err) = dotenv::dotenv() { println!("Error parsing .env {:?}", err); } - env_logger::init(); + // env_logger::init(); run_migrations(&mut connect()).expect("Failed to run migrations"); - create_thumbnails(); watch_files(); let system = actix::System::new(); system.block_on(async { + + otel::init_logs(); + otel::init_tracing(); + create_thumbnails(); + let app_data = Data::new(AppState::default()); let labels = HashMap::new(); @@ -501,11 +569,9 @@ fn main() -> std::io::Result<()> { .unwrap(); let app_state = app_data.clone(); - app_state - .playlist_manager - .do_send(ScanDirectoryMessage { - directory: app_state.base_path.clone(), - }); + app_state.playlist_manager.do_send(ScanDirectoryMessage { + directory: app_state.base_path.clone(), + }); HttpServer::new(move || { let user_dao = SqliteUserDao::new(); From 518fba0ef540e7eef820927dc734667d798f428e Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 6 May 2025 20:26:02 -0400 Subject: [PATCH 2/4] Add missing otel.rs --- src/otel.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/otel.rs diff --git a/src/otel.rs b/src/otel.rs new file mode 100644 index 0000000..cc6af16 --- /dev/null +++ b/src/otel.rs @@ -0,0 +1,61 @@ +use opentelemetry::global::BoxedTracer; +use opentelemetry::{global, KeyValue}; +use opentelemetry_appender_log::OpenTelemetryLogBridge; +use opentelemetry_otlp::WithExportConfig; +use opentelemetry_sdk::logs::{BatchLogProcessor, SdkLoggerProvider}; +use opentelemetry_sdk::Resource; + +pub fn global_tracer() -> BoxedTracer { + global::tracer("image-server") +} + +pub fn init_tracing() { + let resources = Resource::builder() + .with_attributes([ + KeyValue::new("service.name", "image-server"), + //TODO: Get this from somewhere + KeyValue::new("service.version", "1.0"), + ]) + .build(); + + let span_exporter = opentelemetry_otlp::SpanExporter::builder() + .with_tonic() + .with_endpoint(std::env::var("OTLP_OTLS_ENDPOINT").unwrap()) + .build() + .unwrap(); + + let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder() + .with_batch_exporter(span_exporter) + .with_resource(resources) + .build(); + + global::set_tracer_provider(tracer_provider); +} + +pub fn init_logs() { + let otlp_exporter = opentelemetry_otlp::LogExporter::builder() + .with_tonic() + .with_endpoint(std::env::var("OTLP_OTLS_ENDPOINT").unwrap()) + .build() + .unwrap(); + + let exporter = opentelemetry_stdout::LogExporter::default(); + + let resources = Resource::builder() + .with_attributes([ + KeyValue::new("service.name", "image-server"), + KeyValue::new("service.version", "1.0"), + ]) + .build(); + + let log_provider = SdkLoggerProvider::builder() + .with_log_processor(BatchLogProcessor::builder(exporter).build()) + .with_log_processor(BatchLogProcessor::builder(otlp_exporter).build()) + .with_resource(resources) + .build(); + + let otel_log_appender = OpenTelemetryLogBridge::new(&log_provider); + log::set_boxed_logger(Box::new(otel_log_appender)).expect("Unable to set boxed logger"); + //TODO: Still set this with the env? Ideally we still have a clean/simple local logger for local dev + log::set_max_level(log::LevelFilter::Info); +} From 5b17fba51faf380a9c65f0aeb058e178cdf2eec6 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 17 May 2025 13:41:14 -0400 Subject: [PATCH 3/4] Add sorting by a File's Tag Count --- src/data/mod.rs | 2 ++ src/files.rs | 66 +++++++++++++++++++++++++++++++++------------- src/tags.rs | 54 ++++++++++++++++++++++++++++--------- src/testhelpers.rs | 5 +--- 4 files changed, 92 insertions(+), 35 deletions(-) diff --git a/src/data/mod.rs b/src/data/mod.rs index acb322e..b6e7973 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -106,6 +106,8 @@ pub enum SortType { Shuffle, NameAsc, NameDesc, + TagCountAsc, + TagCountDesc, } #[derive(Deserialize)] diff --git a/src/files.rs b/src/files.rs index 22d2e21..bc9cff6 100644 --- a/src/files.rs +++ b/src/files.rs @@ -19,9 +19,9 @@ use log::{debug, error, info, trace}; use crate::data::{Claims, FilesRequest, FilterMode, PhotosResponse, SortType}; use crate::{create_thumbnails, AppState}; -use crate::data::SortType::{NameAsc}; +use crate::data::SortType::NameAsc; use crate::error::IntoHttpError; -use crate::tags::TagDao; +use crate::tags::{FileWithTagCount, TagDao}; use crate::video::StreamActor; use path_absolutize::*; use rand::prelude::SliceRandom; @@ -68,6 +68,13 @@ pub async fn list_photos( "Failed to get files with tag_ids: {:?} with filter_mode: {:?}", tag_ids, filter_mode )) + .inspect(|files| { + debug!( + "Found {:?} tagged files, filtering down by search path {:?}", + files.len(), + search_path + ) + }) .map(|tagged_files| { tagged_files .into_iter() @@ -77,12 +84,12 @@ pub async fn list_photos( return true; } - f.starts_with(&format!( + f.file_name.starts_with(&format!( "{}/", search_path.strip_suffix('/').unwrap_or_else(|| search_path) )) }) - .collect::>() + .collect::>() }) .map(|files| sort(files, req.sort.unwrap_or(NameAsc))) .inspect(|files| debug!("Found {:?} files", files.len())) @@ -106,7 +113,7 @@ pub async fn list_photos( if let Ok(files) = file_system.get_files_for_path(search_path) { debug!("Valid search path: {:?}", search_path); - let mut photos = files + let photos = files .iter() .filter(|&f| { f.metadata().map_or_else( @@ -122,8 +129,14 @@ pub async fn list_photos( relative.to_path_buf() }) .map(|f| f.to_str().unwrap().to_string()) - .filter(|file_path| { - if let (Some(tag_ids), Ok(mut tag_dao)) = (&req.tag_ids, tag_dao.lock()) { + .map(|file_name| { + let mut tag_dao = tag_dao.lock().expect("Unable to get TagDao"); + let file_tags = tag_dao.get_tags_for_path(&file_name).unwrap_or_default(); + + (file_name, file_tags) + }) + .filter(|(_, file_tags)| { + if let Some(tag_ids) = &req.tag_ids { let tag_ids = tag_ids .split(',') .filter_map(|t| t.parse().ok()) @@ -138,7 +151,6 @@ pub async fn list_photos( .collect::>(); let filter_mode = &req.tag_filter_mode.unwrap_or(FilterMode::Any); - let file_tags = tag_dao.get_tags_for_path(file_path).unwrap_or_default(); let excluded = file_tags.iter().any(|t| excluded_tag_ids.contains(&t.id)); return !excluded @@ -152,11 +164,20 @@ pub async fn list_photos( true }) - .collect::>(); + .map(|(file_name, tags)| FileWithTagCount { + file_name, + tag_count: tags.len() as i64, + }) + .collect::>(); + let mut response_files = photos + .clone() + .into_iter() + .map(|f| f.file_name) + .collect::>(); if let Some(sort_type) = req.sort { debug!("Sorting files: {:?}", sort_type); - photos = sort(photos, sort_type) + response_files = sort(photos, sort_type) } let dirs = files @@ -169,25 +190,37 @@ pub async fn list_photos( .map(|f| f.to_str().unwrap().to_string()) .collect::>(); - HttpResponse::Ok().json(PhotosResponse { photos, dirs }) + HttpResponse::Ok().json(PhotosResponse { + photos: response_files, + dirs, + }) } else { error!("Bad photos request: {}", req.path); HttpResponse::BadRequest().finish() } } -fn sort(mut files: Vec, sort_type: SortType) -> Vec { +fn sort(mut files: Vec, sort_type: SortType) -> Vec { match sort_type { SortType::Shuffle => files.shuffle(&mut thread_rng()), SortType::NameAsc => { - files.sort(); + files.sort_by(|l, r| l.file_name.cmp(&r.file_name)); } SortType::NameDesc => { - files.sort_by(|l, r| r.cmp(l)); + files.sort_by(|l, r| r.file_name.cmp(&l.file_name)); + } + SortType::TagCountAsc => { + files.sort_by(|l, r| l.tag_count.cmp(&r.tag_count)); + } + SortType::TagCountDesc => { + files.sort_by(|l, r| r.tag_count.cmp(&l.tag_count)); } } files + .iter() + .map(|f| f.file_name.clone()) + .collect::>() } pub fn list_files(dir: &Path) -> io::Result> { @@ -396,10 +429,7 @@ mod tests { if self.err { Err(anyhow!("Error for test")) } else if let Some(files) = self.files.get(path) { - Ok(files - .iter() - .map(PathBuf::from) - .collect::>()) + Ok(files.iter().map(PathBuf::from).collect::>()) } else { Ok(Vec::new()) } diff --git a/src/tags.rs b/src/tags.rs index 212ec7a..0339dfd 100644 --- a/src/tags.rs +++ b/src/tags.rs @@ -202,12 +202,12 @@ pub trait TagDao { &mut self, tag_ids: Vec, exclude_tag_ids: Vec, - ) -> anyhow::Result>; + ) -> anyhow::Result>; fn get_files_with_any_tag_ids( &mut self, tag_ids: Vec, exclude_tag_ids: Vec, - ) -> anyhow::Result>; + ) -> anyhow::Result>; } pub struct SqliteTagDao { @@ -277,7 +277,7 @@ impl TagDao for SqliteTagDao { .and_then(|_| { info!("Inserted tag: {:?}", name); define_sql_function! { - fn last_insert_rowid() -> diesel::sql_types::Integer; + fn last_insert_rowid() -> Integer; } diesel::select(last_insert_rowid()) .get_result::(&mut self.connection) @@ -353,7 +353,7 @@ impl TagDao for SqliteTagDao { &mut self, tag_ids: Vec, exclude_tag_ids: Vec, - ) -> anyhow::Result> { + ) -> anyhow::Result> { use diesel::dsl::*; let exclude_subquery = tagged_photo::table @@ -365,10 +365,21 @@ impl TagDao for SqliteTagDao { .filter(tagged_photo::tag_id.eq_any(tag_ids.clone())) .filter(tagged_photo::photo_name.ne_all(exclude_subquery)) .group_by(tagged_photo::photo_name) - .select((tagged_photo::photo_name, count(tagged_photo::tag_id))) + .select(( + tagged_photo::photo_name, + count_distinct(tagged_photo::tag_id), + )) .having(count_distinct(tagged_photo::tag_id).ge(tag_ids.len() as i64)) - .select(tagged_photo::photo_name) - .get_results::(&mut self.connection) + .get_results::<(String, i64)>(&mut self.connection) + .map(|results| { + results + .into_iter() + .map(|(file_name, tag_count)| FileWithTagCount { + file_name, + tag_count, + }) + .collect() + }) .with_context(|| format!("Unable to get Tagged photos with ids: {:?}", tag_ids)) } @@ -376,7 +387,7 @@ impl TagDao for SqliteTagDao { &mut self, tag_ids: Vec, exclude_tag_ids: Vec, - ) -> anyhow::Result> { + ) -> anyhow::Result> { use diesel::dsl::*; let exclude_subquery = tagged_photo::table @@ -388,9 +399,20 @@ impl TagDao for SqliteTagDao { .filter(tagged_photo::tag_id.eq_any(tag_ids.clone())) .filter(tagged_photo::photo_name.ne_all(exclude_subquery)) .group_by(tagged_photo::photo_name) - .select((tagged_photo::photo_name, count(tagged_photo::tag_id))) - .select(tagged_photo::photo_name) - .get_results::(&mut self.connection) + .select(( + tagged_photo::photo_name, + count_distinct(tagged_photo::tag_id), + )) + .get_results::<(String, i64)>(&mut self.connection) + .map(|results| { + results + .into_iter() + .map(|(file_name, tag_count)| FileWithTagCount { + file_name, + tag_count, + }) + .collect() + }) .with_context(|| format!("Unable to get Tagged photos with ids: {:?}", tag_ids)) } } @@ -517,7 +539,7 @@ mod tests { &mut self, tag_ids: Vec, _exclude_tag_ids: Vec, - ) -> anyhow::Result> { + ) -> anyhow::Result> { todo!() } @@ -525,7 +547,7 @@ mod tests { &mut self, _tag_ids: Vec, _exclude_tag_ids: Vec, - ) -> anyhow::Result> { + ) -> anyhow::Result> { todo!() } } @@ -607,3 +629,9 @@ mod tests { ); } } + +#[derive(Debug, Clone)] +pub struct FileWithTagCount { + pub file_name: String, + pub tag_count: i64, +} diff --git a/src/testhelpers.rs b/src/testhelpers.rs index e288716..66c1ac2 100644 --- a/src/testhelpers.rs +++ b/src/testhelpers.rs @@ -48,10 +48,7 @@ impl UserDao for TestUserDao { } fn user_exists(&mut self, user: &str) -> bool { - self.user_map - .borrow() - .iter() - .any(|u| u.username == user) + self.user_map.borrow().iter().any(|u| u.username == user) } } From 484eec8b39f71193c62d8b5673e635835de32df0 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 17 May 2025 13:41:30 -0400 Subject: [PATCH 4/4] Only use Otel on release builds --- src/main.rs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index 68df53e..5aeaa1f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -184,8 +184,10 @@ async fn upload_image( &full_path.to_str().unwrap().to_string(), true, ) { - let context = opentelemetry::Context::new().with_remote_span_context(span.span_context().clone()); - tracer.span_builder("file write") + let context = + opentelemetry::Context::new().with_remote_span_context(span.span_context().clone()); + tracer + .span_builder("file write") .start_with_context(&tracer, &context); if !full_path.is_file() && is_image_or_video(&full_path) { @@ -322,7 +324,10 @@ async fn get_video_part( file.into_response(&request) } else { error!("Video part not found: {:?}", file_part); - span.set_status(Status::error(format!("Video part not found '{}'", file_part.to_str().unwrap()))); + span.set_status(Status::error(format!( + "Video part not found '{}'", + file_part.to_str().unwrap() + ))); HttpResponse::NotFound().finish() } } @@ -454,7 +459,8 @@ fn create_thumbnails() { let mut video_span = tracer.start_with_context( "generate_video_thumbnail", - &opentelemetry::Context::new().with_remote_span_context(span.span_context().clone()), + &opentelemetry::Context::new() + .with_remote_span_context(span.span_context().clone()), ); video_span.set_attributes(vec![ KeyValue::new("type", "video"), @@ -538,7 +544,6 @@ fn main() -> std::io::Result<()> { if let Err(err) = dotenv::dotenv() { println!("Error parsing .env {:?}", err); } - // env_logger::init(); run_migrations(&mut connect()).expect("Failed to run migrations"); @@ -546,9 +551,17 @@ fn main() -> std::io::Result<()> { let system = actix::System::new(); system.block_on(async { + // Just use basic logger when running a non-release build + #[cfg(debug_assertions)] + { + env_logger::init(); + } + #[cfg(not(debug_assertions))] + { + otel::init_logs(); + otel::init_tracing(); + } - otel::init_logs(); - otel::init_tracing(); create_thumbnails(); let app_data = Data::new(AppState::default());