Refactor logging to use tracing #7

merged
opened by tsiry-sandratraina.com targeting main from setup-tracing
Changed files
+315 -586
crates
+77 -4
Cargo.lock
···
[[package]]
name = "log"
-
version = "0.4.27"
+
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "lru-slab"
···
"winapi",
+
[[package]]
+
name = "nu-ansi-term"
+
version = "0.50.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
+
dependencies = [
+
"windows-sys 0.52.0",
+
]
+
[[package]]
name = "nuid"
version = "0.5.0"
···
[[package]]
name = "owo-colors"
-
version = "4.2.1"
+
version = "4.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "26995317201fa17f3656c36716aed4a7c81743a9634ac4c99c0eeda495db0cec"
+
checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e"
dependencies = [
"supports-color 2.1.0",
"supports-color 3.0.2",
···
"sqlx",
"tokio",
"tokio-stream",
+
"tracing",
[[package]]
···
"tempfile",
"tokio",
"tokio-stream",
+
"tracing",
[[package]]
···
"tempfile",
"tokio",
"tokio-stream",
+
"tracing",
[[package]]
···
"tokio",
"tokio-stream",
"tokio-tungstenite",
+
"tracing",
"tungstenite",
"url",
···
"sqlx",
"tokio",
"tokio-stream",
+
"tracing",
[[package]]
···
"sqlx",
"tokio",
"tokio-stream",
+
"tracing",
"uuid",
···
"sqlx",
"tokio",
"tokio-stream",
+
"tracing",
[[package]]
···
"sqlx",
"tokio",
"tokio-stream",
+
"tracing",
[[package]]
···
"serde_json",
"tokio",
"tokio-stream",
+
"tracing",
"uuid",
···
"sqlx",
"tokio",
"tokio-stream",
+
"tracing",
[[package]]
···
"anyhow",
"clap",
"dotenv",
+
"owo-colors",
"rocksky-analytics",
"rocksky-dropbox",
"rocksky-googledrive",
···
"rocksky-tracklist",
"rocksky-webscrobbler",
"tokio",
+
"tracing",
+
"tracing-log",
+
"tracing-subscriber",
[[package]]
···
"tokio",
+
[[package]]
+
name = "sharded-slab"
+
version = "0.1.7"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+
dependencies = [
+
"lazy_static",
+
]
+
[[package]]
name = "shlex"
version = "1.3.0"
···
"syn 2.0.101",
+
[[package]]
+
name = "thread_local"
+
version = "1.1.9"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
+
dependencies = [
+
"cfg-if",
+
]
+
[[package]]
name = "time"
version = "0.3.44"
···
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
dependencies = [
"once_cell",
+
"valuable",
+
]
+
+
[[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.20"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
+
dependencies = [
+
"nu-ansi-term",
+
"sharded-slab",
+
"smallvec",
+
"thread_local",
+
"tracing-core",
+
"tracing-log",
[[package]]
···
"wasm-bindgen",
+
[[package]]
+
name = "valuable"
+
version = "0.1.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
[[package]]
name = "vcpkg"
version = "0.2.15"
+1
crates/analytics/Cargo.toml
···
clap = "4.5.31"
actix-web = "4.9.0"
tokio-stream = { version = "0.1.17", features = ["full"] }
+
tracing = "0.1.41"
+2 -2
crates/analytics/src/cmd/serve.rs
···
req: HttpRequest,
) -> Result<impl Responder, actix_web::Error> {
let method = req.match_info().get("method").unwrap_or("unknown");
-
println!("Method: {}", method.bright_green());
+
tracing::info!(method = %method.bright_green(), "API call");
let conn = data.get_ref().clone();
handle(method, &mut payload, &req, conn)
···
let addr = format!("{}:{}", host, port);
let url = format!("http://{}", addr);
-
println!("Listening on {}", url.bright_green());
+
tracing::info!(url = %url.bright_green(), "Listening on");
let conn = conn.clone();
HttpServer::new(move || {
+37 -84
crates/analytics/src/core.rs
···
.await?;
for (i, track) in tracks.clone().into_iter().enumerate() {
-
println!(
-
"track {} - {} - {}",
-
i,
-
track.title.bright_green(),
-
track.artist
-
);
+
tracing::info!(track = i, title = %track.title.bright_green(), artist = %track.artist);
match conn.execute(
"INSERT INTO tracks (
id,
···
],
) {
Ok(_) => (),
-
Err(e) => println!("error: {}", e),
+
Err(e) => tracing::error!(error = %e, "Error inserting track"),
}
}
-
println!("tracks: {:?}", tracks.len());
+
tracing::info!(tracks = tracks.len(), "Loaded tracks");
Ok(())
}
···
.await?;
for (i, artist) in artists.clone().into_iter().enumerate() {
-
println!("artist {} - {}", i, artist.name.bright_green());
+
tracing::info!(artist = i, name = %artist.name.bright_green());
match conn.execute(
"INSERT INTO artists (
id,
···
],
) {
Ok(_) => (),
-
Err(e) => println!("error: {}", e),
+
Err(e) => tracing::error!(error = %e, "Error inserting artist"),
}
}
-
println!("artists: {:?}", artists.len());
+
tracing::info!(artists = artists.len(), "Loaded artists");
Ok(())
}
···
.await?;
for (i, album) in albums.clone().into_iter().enumerate() {
-
println!("album {} - {}", i, album.title.bright_green());
+
tracing::info!(album = i, title = %album.title.bright_green(), artist = %album.artist);
match conn.execute(
"INSERT INTO albums (
id,
···
],
) {
Ok(_) => (),
-
Err(e) => println!("error: {}", e),
+
Err(e) => tracing::error!(error = %e, "Error inserting album"),
}
}
-
println!("albums: {:?}", albums.len());
+
tracing::info!(albums = albums.len(), "Loaded albums");
Ok(())
}
···
.await?;
for (i, user) in users.clone().into_iter().enumerate() {
-
println!("user {} - {}", i, user.display_name.bright_green());
+
tracing::info!(user = i, name = %user.display_name.bright_green());
match conn.execute(
"INSERT INTO users (
id,
···
],
) {
Ok(_) => (),
-
Err(e) => println!("error: {}", e),
+
Err(e) => tracing::error!(error = %e, "Error inserting user"),
}
}
-
println!("users: {:?}", users.len());
+
tracing::info!(users = users.len(), "Loaded users");
Ok(())
}
···
.await?;
for (i, scrobble) in scrobbles.clone().into_iter().enumerate() {
-
println!(
-
"scrobble {} - {}",
-
i,
-
match scrobble.uri.clone() {
-
Some(uri) => uri.to_string(),
-
None => "None".to_string(),
-
}
-
.bright_green()
-
);
+
tracing::info!(scrobble = i, uri = %scrobble.uri.clone().unwrap_or_else(|| "None".to_string()).bright_green());
match conn.execute(
"INSERT INTO scrobbles (
id,
···
],
) {
Ok(_) => (),
-
Err(e) => println!("error: {}", e),
+
Err(e) => tracing::error!(error = %e, "Error inserting scrobble"),
}
}
-
println!("scrobbles: {:?}", scrobbles.len());
+
tracing::info!(scrobbles = scrobbles.len(), "Loaded scrobbles");
Ok(())
}
···
.await?;
for (i, album_track) in album_tracks.clone().into_iter().enumerate() {
-
println!(
-
"album_track {} - {} - {}",
-
i,
-
album_track.album_id.bright_green(),
-
album_track.track_id
-
);
+
tracing::info!(album_track = i, album_id = %album_track.album_id.bright_green(), track_id = %album_track.track_id);
match conn.execute(
"INSERT INTO album_tracks (
id,
···
],
) {
Ok(_) => (),
-
Err(e) => println!("error: {}", e),
+
Err(e) => tracing::error!(error = %e, "Error inserting album_track"),
}
}
-
println!("album_tracks: {:?}", album_tracks.len());
+
+
tracing::info!(album_tracks = album_tracks.len(), "Loaded album_tracks");
Ok(())
}
···
.await?;
for (i, loved_track) in loved_tracks.clone().into_iter().enumerate() {
-
println!(
-
"loved_track {} - {} - {}",
-
i,
-
loved_track.user_id.bright_green(),
-
loved_track.track_id
-
);
+
tracing::info!(loved_track = i, user_id = %loved_track.user_id.bright_green(), track_id = %loved_track.track_id);
match conn.execute(
"INSERT INTO loved_tracks (
id,
···
],
) {
Ok(_) => (),
-
Err(e) => println!("error: {}", e),
+
Err(e) => tracing::error!(error = %e, "Error inserting loved_track"),
}
}
-
println!("loved_tracks: {:?}", loved_tracks.len());
+
tracing::info!(loved_tracks = loved_tracks.len(), "Loaded loved_tracks");
Ok(())
}
···
.await?;
for (i, artist_track) in artist_tracks.clone().into_iter().enumerate() {
-
println!(
-
"artist_track {} - {} - {}",
-
i,
-
artist_track.artist_id.bright_green(),
-
artist_track.track_id
-
);
+
tracing::info!(artist_track = i, artist_id = %artist_track.artist_id.bright_green(), track_id = %artist_track.track_id);
match conn.execute(
"INSERT INTO artist_tracks (id, artist_id, track_id, created_at) VALUES (?, ?, ?, ?)",
params![
···
],
) {
Ok(_) => (),
-
Err(e) => println!("error: {}", e),
+
Err(e) => tracing::error!(error = %e, "Error inserting artist_track"),
}
}
-
println!("artist_tracks: {:?}", artist_tracks.len());
+
tracing::info!(artist_tracks = artist_tracks.len(), "Loaded artist_tracks");
Ok(())
}
···
.await?;
for (i, artist_album) in artist_albums.clone().into_iter().enumerate() {
-
println!(
-
"artist_albums {} - {} - {}",
-
i,
-
artist_album.artist_id.bright_green(),
-
artist_album.album_id
-
);
+
tracing::info!(artist_album = i, artist_id = %artist_album.artist_id.bright_green(), album_id = %artist_album.album_id);
match conn.execute(
"INSERT INTO artist_albums (id, artist_id, album_id, created_at) VALUES (?, ?, ?, ?)",
params![
···
],
) {
Ok(_) => (),
-
Err(e) => println!("error: {}", e),
+
Err(e) => tracing::error!(error = %e, "Error inserting artist_album"),
}
}
-
println!("artist_albums: {:?}", artist_albums.len());
+
tracing::info!(artist_albums = artist_albums.len(), "Loaded artist_albums");
Ok(())
}
···
.await?;
for (i, user_album) in user_albums.clone().into_iter().enumerate() {
-
println!(
-
"user_album {} - {} - {}",
-
i,
-
user_album.user_id.bright_green(),
-
user_album.album_id
-
);
+
tracing::info!(user_album = i, user_id = %user_album.user_id.bright_green(), album_id = %user_album.album_id);
match conn.execute(
"INSERT INTO user_albums (id, user_id, album_id, created_at) VALUES (?, ?, ?, ?)",
params![
···
],
) {
Ok(_) => (),
-
Err(e) => println!("error: {}", e),
+
Err(e) => tracing::error!(error = %e, "Error inserting user_album"),
}
}
-
println!("user_albums: {:?}", user_albums.len());
+
tracing::info!(user_albums = user_albums.len(), "Loaded user_albums");
Ok(())
}
···
.await?;
for (i, user_artist) in user_artists.clone().into_iter().enumerate() {
-
println!(
-
"user_artist {} - {} - {}",
-
i,
-
user_artist.user_id.bright_green(),
-
user_artist.artist_id
-
);
+
tracing::info!(user_artist = i, user_id = %user_artist.user_id.bright_green(), artist_id = %user_artist.artist_id);
match conn.execute(
"INSERT INTO user_artists (id, user_id, artist_id, created_at) VALUES (?, ?, ?, ?)",
params![
···
],
) {
Ok(_) => (),
-
Err(e) => println!("error: {}", e),
+
Err(e) => tracing::error!(error = %e, "Error inserting user_artist"),
}
}
-
println!("user_artists: {:?}", user_artists.len());
+
tracing::info!(user_artists = user_artists.len(), "Loaded user_artists");
Ok(())
}
···
.await?;
for (i, user_track) in user_tracks.clone().into_iter().enumerate() {
-
println!(
-
"user_track {} - {} - {}",
-
i,
-
user_track.user_id.bright_green(),
-
user_track.track_id
-
);
+
tracing::info!(user_track = i, user_id = %user_track.user_id.bright_green(), track_id = %user_track.track_id);
match conn.execute(
"INSERT INTO user_tracks (id, user_id, track_id, created_at) VALUES (?, ?, ?, ?)",
params![
···
],
) {
Ok(_) => (),
-
Err(e) => println!("error: {}", e),
+
Err(e) => tracing::error!(error = %e, "Error inserting user_track"),
}
}
-
println!("user_tracks: {:?}", user_tracks.len());
+
tracing::info!(user_tracks = user_tracks.len(), "Loaded user_tracks");
Ok(())
}
+4
crates/analytics/src/handlers/albums.rs
···
let offset = pagination.skip.unwrap_or(0);
let limit = pagination.take.unwrap_or(20);
let did = params.user_did;
+
tracing::info!(limit, offset, user_did = ?did, "Get albums");
let conn = conn.lock().unwrap();
let mut stmt = match did {
···
let offset = pagination.skip.unwrap_or(0);
let limit = pagination.take.unwrap_or(20);
let did = params.user_did;
+
tracing::info!(limit, offset, user_did = ?did, "Get top albums");
let conn = conn.lock().unwrap();
let mut stmt = match did {
···
let body = read_payload!(payload);
let params = serde_json::from_slice::<GetAlbumTracksParams>(&body)?;
let conn = conn.lock().unwrap();
+
tracing::info!(album_id = %params.album_id, "Get album tracks");
+
let mut stmt = conn.prepare(r#"
SELECT
t.id,
+2
crates/analytics/src/handlers/artists.rs
···
let body = read_payload!(payload);
let params = serde_json::from_slice::<GetArtistAlbumsParams>(&body)?;
let conn = conn.lock().unwrap();
+
tracing::info!(artist_id = %params.artist_id, "Get artist albums");
let mut stmt = conn.prepare(r#"
SELECT
···
let pagination = params.pagination.unwrap_or_default();
let offset = pagination.skip.unwrap_or(0);
let limit = pagination.take.unwrap_or(10);
+
tracing::info!(artist_id = %params.artist_id, limit, offset, "Get artist listeners");
let conn = conn.lock().unwrap();
let mut stmt =
+2
crates/analytics/src/handlers/scrobbles.rs
···
let offset = pagination.skip.unwrap_or(0);
let limit = pagination.take.unwrap_or(20);
let did = params.user_did;
+
tracing::info!(limit, offset, user_did = ?did, "Get scrobbles");
let conn = conn.lock().unwrap();
let mut stmt = match did {
···
let pagination = params.pagination.unwrap_or_default();
let offset = pagination.skip.unwrap_or(0);
let limit = pagination.take.unwrap_or(10);
+
tracing::info!(limit, offset, user_did = ?params.user_did, "Get distinct scrobbles");
let conn = conn.lock().unwrap();
let mut stmt = conn.prepare(
+8
crates/analytics/src/handlers/stats.rs
···
let body = read_payload!(payload);
let params = serde_json::from_slice::<GetStatsParams>(&body)?;
+
tracing::info!(user_did = ?params.user_did, "Get stats");
let conn = conn.lock().unwrap();
let mut stmt = conn.prepare("SELECT COUNT(*) FROM scrobbles s LEFT JOIN users u ON s.user_id = u.id WHERE u.did = ? OR u.handle = ?")?;
···
.end
.unwrap_or(GetScrobblesPerDayParams::default().end.unwrap());
let did = params.user_did;
+
tracing::info!(start = %start, end = %end, user_did = ?did, "Get scrobbles per day");
let conn = conn.lock().unwrap();
match did {
···
.end
.unwrap_or(GetScrobblesPerDayParams::default().end.unwrap());
let did = params.user_did;
+
tracing::info!(start = %start, end = %end, user_did = ?did, "Get scrobbles per month");
let conn = conn.lock().unwrap();
match did {
···
.end
.unwrap_or(GetScrobblesPerDayParams::default().end.unwrap());
let did = params.user_did;
+
tracing::info!(start = %start, end = %end, user_did = ?did, "Get scrobbles per year");
let conn = conn.lock().unwrap();
match did {
···
.end
.unwrap_or(GetAlbumScrobblesParams::default().end.unwrap());
let conn = conn.lock().unwrap();
+
tracing::info!(album_id = %params.album_id, start = %start, end = %end, "Get album scrobbles");
+
let mut stmt = conn.prepare(
r#"
SELECT
···
.end
.unwrap_or(GetArtistScrobblesParams::default().end.unwrap());
let conn = conn.lock().unwrap();
+
tracing::info!(artist_id = %params.artist_id, start = %start, end = %end, "Get artist scrobbles");
let mut stmt = conn.prepare(
r#"
···
.end
.unwrap_or(GetTrackScrobblesParams::default().end.unwrap());
let conn = conn.lock().unwrap();
+
tracing::info!(track_id = %params.track_id, start = %start, end = %end, "Get track scrobbles");
let mut stmt = conn.prepare(
r#"
+3
crates/analytics/src/handlers/tracks.rs
···
let offset = pagination.skip.unwrap_or(0);
let limit = pagination.take.unwrap_or(20);
let did = params.user_did;
+
tracing::info!(limit, offset, user_did = ?did, "Get tracks");
let conn = conn.lock().unwrap();
match did {
···
let offset = pagination.skip.unwrap_or(0);
let limit = pagination.take.unwrap_or(20);
let did = params.user_did;
+
tracing::info!(limit, offset, user_did = ?did, "Get loved tracks");
let conn = conn.lock().unwrap();
let mut stmt = conn.prepare(
···
let offset = pagination.skip.unwrap_or(0);
let limit = pagination.take.unwrap_or(20);
let did = params.user_did;
+
tracing::info!(limit, offset, user_did = ?did, "Get top tracks");
let conn = conn.lock().unwrap();
match did {
+1
crates/dropbox/Cargo.toml
···
tempfile = "3.19.1"
tokio = { version = "1.43.0", features = ["full"] }
tokio-stream = { version = "0.1.17", features = ["full"] }
+
tracing = "0.1.41"
+1
crates/googledrive/Cargo.toml
···
tempfile = "3.19.1"
tokio = { version = "1.43.0", features = ["full"] }
tokio-stream = { version = "0.1.17", features = ["full"] }
+
tracing = "0.1.41"
+1
crates/jetstream/Cargo.toml
···
sha256 = "1.6.0"
time = { version = "0.3.44", features = ["formatting", "macros"] }
redis = { version = "0.29.0", features = ["aio", "tokio-comp"] }
+
tracing = "0.1.41"
+1
crates/playlists/Cargo.toml
···
hex = "0.4.3"
jsonwebtoken = "9.3.1"
sha2 = "0.10.8"
+
tracing = "0.1.41"
+4
crates/rockskyd/Cargo.toml
···
rocksky-spotify = { path = "../spotify" }
rocksky-tracklist = { path = "../tracklist" }
rocksky-webscrobbler = { path = "../webscrobbler" }
+
tracing = "0.1.41"
+
tracing-subscriber = "0.3.20"
+
tracing-log = "0.2.0"
+
owo-colors = "4.2.2"
+12
crates/rockskyd/src/main.rs
···
use clap::Command;
use dotenv::dotenv;
+
use tracing_subscriber::fmt::format::Format;
pub mod cmd;
···
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
+
let format = Format::default()
+
.with_level(true)
+
.with_target(true)
+
.with_ansi(true)
+
.compact();
+
+
tracing_subscriber::fmt()
+
.event_format(format)
+
.with_max_level(tracing::Level::INFO)
+
.init();
+
dotenv().ok();
let args = cli().get_matches();
+1
crates/spotify/Cargo.toml
···
] }
tokio = { version = "1.43.0", features = ["full"] }
tokio-stream = { version = "0.1.17", features = ["full"] }
+
tracing = "0.1.41"
+1
crates/storage/Cargo.toml
···
] }
actix-web = "4.9.0"
tokio-stream = { version = "0.1.17", features = ["full"] }
+
tracing = "0.1.41"
+1
crates/tracklist/Cargo.toml
···
clap = "4.5.31"
rand = "0.9.2"
uuid = { version = "1.18.0", features = ["v4"] }
+
tracing = "0.1.41"
+2 -2
crates/dropbox/src/cmd/serve.rs
···
req: HttpRequest,
) -> Result<impl Responder, actix_web::Error> {
let method = req.match_info().get("method").unwrap_or("unknown");
-
println!("Method: {}", method.bright_green());
+
tracing::info!(method = %method.bright_green(), "API call");
let conn = data.get_ref().clone();
handle(method, &mut payload, &req, conn)
···
let addr = format!("{}:{}", host, port);
let url = format!("http://{}", addr);
-
println!("Listening on {}", url.bright_green());
+
tracing::info!(url = %url.bright_green(), "Listening on");
let pool = PgPoolOptions::new()
.max_connections(5)
+8
crates/dropbox/src/handlers/files.rs
···
use actix_web::{web, HttpRequest, HttpResponse};
use anyhow::Error;
+
use owo_colors::OwoColorize;
use sqlx::{Pool, Postgres};
use tokio_stream::StreamExt;
···
let body = read_payload!(payload);
let params = serde_json::from_slice::<GetFilesParams>(&body)?;
let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?;
+
tracing::info!(did = %params.did.bright_green(), "dropbox.getFiles");
if refresh_token.is_none() {
return Ok(HttpResponse::Unauthorized().finish());
···
let body = read_payload!(payload);
let params = serde_json::from_slice::<GetFilesParams>(&body)?;
let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?;
+
tracing::info!(did = %params.did.bright_green(), "dropbox.createMusicFolder");
if refresh_token.is_none() {
return Ok(HttpResponse::Unauthorized().finish());
···
let body = read_payload!(payload);
let params = serde_json::from_slice::<GetFilesAtParams>(&body)?;
let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?;
+
tracing::info!(did = %params.did.bright_green(), path = %params.path.bright_green(), "dropbox.getFilesAt");
if refresh_token.is_none() {
return Ok(HttpResponse::Unauthorized().finish());
···
let body = read_payload!(payload);
let params = serde_json::from_slice::<DownloadFileParams>(&body)?;
let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?;
+
tracing::info!(did = %params.did.bright_green(), path = %params.path.bright_green(), "dropbox.downloadFile");
if refresh_token.is_none() {
return Ok(HttpResponse::Unauthorized().finish());
···
let body = read_payload!(payload);
let params = serde_json::from_slice::<DownloadFileParams>(&body)?;
let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?;
+
tracing::info!(did = %params.did.bright_green(), path = %params.path.bright_green(), "dropbox.getTemporaryLink");
if refresh_token.is_none() {
return Ok(HttpResponse::Unauthorized().finish());
···
let body = read_payload!(payload);
let params = serde_json::from_slice::<DownloadFileParams>(&body)?;
let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?;
+
tracing::info!(did = %params.did.bright_green(), path = %params.path.bright_green(), "dropbox.getMetadata");
if refresh_token.is_none() {
return Ok(HttpResponse::Unauthorized().finish());
···
) -> Result<HttpResponse, Error> {
let body = read_payload!(payload);
let params = serde_json::from_slice::<ScanFolderParams>(&body)?;
+
tracing::info!(did = %params.did.bright_green(), path = %params.path.bright_green(), "dropbox.scanFolder");
let pool = pool.clone();
thread::spawn(move || {
+60 -74
crates/dropbox/src/scan.rs
···
.await?;
if res.status().as_u16() == 400 || res.status().as_u16() == 409 {
-
println!("Path not found: {}", path.bright_red());
+
tracing::error!(path = %path.bright_red(), "Path not found");
return Ok(());
}
let entry = res.json::<Entry>().await?;
if entry.tag.clone().unwrap().as_str() == "folder" {
-
println!("Scanning folder: {}", path.bright_green());
+
tracing::info!(path = %path.bright_green(), "Scanning folder");
let parent_path = Path::new(&path)
.parent()
···
let client = Client::new();
-
println!("Downloading file: {}", path.bright_green());
+
tracing::info!(path = %path.bright_green(), "Downloading file");
let res = client
.post(&format!("{}/files/download", CONTENT_URL))
···
let mut tmpfile = File::create(&tmppath)?;
tmpfile.write_all(&bytes)?;
-
println!(
-
"Reading file: {}",
-
&tmppath.clone().display().to_string().bright_green()
-
);
+
tracing::info!(path = %tmppath.clone().display().to_string().bright_green(), "Reading file");
let tagged_file = match Probe::open(&tmppath)?.read() {
Ok(tagged_file) => tagged_file,
Err(e) => {
-
println!("Error opening file: {}", e);
+
tracing::error!(path = %tmppath.clone().display().to_string().bright_red(), "Error reading file: {}", e);
return Ok(());
}
};
···
let tag = match primary_tag {
Some(tag) => tag,
None => {
-
println!("No tag found in file");
+
tracing::error!(path = %tmppath.clone().display().to_string().bright_red(), "No tag found in file");
return Ok(());
}
};
let pictures = tag.pictures();
-
println!(
-
"Title: {}",
-
tag.get_string(&lofty::tag::ItemKey::TrackTitle)
-
.unwrap_or_default()
-
.bright_green()
-
);
-
println!(
-
"Artist: {}",
-
tag.get_string(&lofty::tag::ItemKey::TrackArtist)
-
.unwrap_or_default()
-
.bright_green()
+
tracing::info!(
+
title = %tag
+
.get_string(&lofty::tag::ItemKey::TrackTitle)
+
.unwrap_or_default(),
);
-
println!(
-
"Album Artist: {}",
-
tag.get_string(&lofty::tag::ItemKey::AlbumArtist)
-
.unwrap_or_default()
-
.bright_green()
+
tracing::info!(
+
artist = %tag
+
.get_string(&lofty::tag::ItemKey::TrackArtist)
+
.unwrap_or_default(),
);
-
println!(
-
"Album: {}",
-
tag.get_string(&lofty::tag::ItemKey::AlbumTitle)
-
.unwrap_or_default()
-
.bright_green()
+
tracing::info!(
+
album = %tag
+
.get_string(&lofty::tag::ItemKey::AlbumTitle)
+
.unwrap_or_default(),
);
-
println!(
-
"Lyrics: {}",
-
tag.get_string(&lofty::tag::ItemKey::Lyrics)
-
.unwrap_or_default()
-
.bright_green()
+
tracing::info!(
+
album_artist = %tag
+
.get_string(&lofty::tag::ItemKey::AlbumArtist)
+
.unwrap_or_default(),
);
-
println!("Year: {}", tag.year().unwrap_or_default().bright_green());
-
println!(
-
"Track Number: {}",
-
tag.track().unwrap_or_default().bright_green()
+
tracing::info!(
+
lyrics = %tag
+
.get_string(&lofty::tag::ItemKey::Lyrics)
+
.unwrap_or_default(),
);
-
println!(
-
"Track Total: {}",
-
tag.track_total().unwrap_or_default().bright_green()
+
tracing::info!(year = %tag.year().unwrap_or_default());
+
tracing::info!(track_number = %tag.track().unwrap_or_default());
+
tracing::info!(track_total = %tag.track_total().unwrap_or_default());
+
tracing::info!(
+
release_date = %tag
+
.get_string(&lofty::tag::ItemKey::OriginalReleaseDate)
+
.unwrap_or_default(),
);
-
println!(
-
"Release Date: {:?}",
-
tag.get_string(&lofty::tag::ItemKey::OriginalReleaseDate)
-
.unwrap_or_default()
-
.bright_green()
+
tracing::info!(
+
recording_date = %tag
+
.get_string(&lofty::tag::ItemKey::RecordingDate)
+
.unwrap_or_default(),
);
-
println!(
-
"Recording Date: {:?}",
-
tag.get_string(&lofty::tag::ItemKey::RecordingDate)
-
.unwrap_or_default()
-
.bright_green()
+
tracing::info!(
+
copyright_message = %tag
+
.get_string(&lofty::tag::ItemKey::CopyrightMessage)
+
.unwrap_or_default(),
);
-
println!(
-
"Copyright Message: {}",
-
tag.get_string(&lofty::tag::ItemKey::CopyrightMessage)
-
.unwrap_or_default()
-
.bright_green()
-
);
-
println!("Pictures: {:?}", pictures);
+
tracing::info!(pictures = ?pictures);
let title = tag
.get_string(&lofty::tag::ItemKey::TrackTitle)
···
match track {
Some(track) => {
-
println!("Track exists: {}", title.bright_green());
+
tracing::info!(title = %title.bright_green(), "Track exists");
let parent_path = Path::new(&path)
.parent()
.map(|p| p.to_string_lossy().to_string());
let status =
create_dropbox_path(&pool, &entry, &track, &dropbox_id, parent_path).await;
-
println!("status: {:?}", status);
+
tracing::info!(status = ?status);
// TODO: publish file metadata to nats
}
None => {
-
println!("Creating track: {}", title.bright_green());
+
tracing::info!(title = %title.bright_green(), "Creating track");
let album_art =
upload_album_cover(albumart_id.into(), pictures, &access_token).await?;
let client = Client::new();
···
}))
.send()
.await?;
-
println!("Track Saved: {} {}", title, response.status());
+
tracing::info!(title = title, status = %response.status(), "Track saved");
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
let track = get_track_by_hash(&pool, &hash).await?;
···
return Ok(());
}
-
println!("Failed to create track: {}", title.bright_green());
+
tracing::error!(title = %title.bright_red(), "Failed to create track");
}
}
···
.send()
.await?;
-
println!("Cover uploaded: {}", response.status());
+
tracing::info!(status = %response.status(), "Cover uploaded");
Ok(Some(name))
}
···
let meta_opts = MetadataOptions::default();
let format_opts = FormatOptions::default();
-
let probed =
-
match symphonia::default::get_probe().format(&hint, media_source, &format_opts, &meta_opts)
-
{
-
Ok(probed) => probed,
-
Err(_) => {
-
println!("Error probing file");
-
return Ok(duration);
-
}
-
};
+
let probed = match symphonia::default::get_probe().format(
+
&hint,
+
media_source,
+
&format_opts,
+
&meta_opts,
+
) {
+
Ok(probed) => probed,
+
Err(e) => {
+
tracing::error!(path = %path.display().to_string().bright_red(), "Error probing file: {}", e);
+
return Ok(duration);
+
}
+
};
if let Some(track) = probed.format.tracks().first() {
if let Some(duration) = track.codec_params.n_frames {
+2 -2
crates/googledrive/src/cmd/serve.rs
···
req: HttpRequest,
) -> Result<impl Responder, actix_web::Error> {
let method = req.match_info().get("method").unwrap_or("unknown");
-
println!("Method: {}", method.bright_green());
+
tracing::info!(method = %method.bright_green(), "API call");
let conn = data.get_ref().clone();
handle(method, &mut payload, &req, conn)
···
let addr = format!("{}:{}", host, port);
let url = format!("http://{}", addr);
-
println!("Listening on {}", url.bright_green());
+
tracing::info!(url = %url.bright_green(), "Listening on");
let pool = PgPoolOptions::new()
.max_connections(5)
+6 -1
crates/googledrive/src/repo/google_drive_path.rs
···
+
use owo_colors::OwoColorize;
use sqlx::{Pool, Postgres};
use crate::{
···
.execute(pool)
.await?;
-
println!("{:?}", result);
+
tracing::info!(
+
file_id = %file.id.bright_green(),
+
rows_affected = %result.rows_affected(),
+
"Google Drive path created"
+
);
sqlx::query(
r#"
+27 -79
crates/googledrive/src/scan.rs
···
let file = res.json::<File>().await?;
if file.mime_type == "application/vnd.google-apps.folder" {
-
println!("Scanning folder: {}", file.name.bright_green());
+
tracing::info!(folder = %file.name.bright_green(), "Scanning folder");
create_google_drive_directory(
&pool,
···
return Ok(());
}
-
println!("Downloading file: {}", file.name.bright_green());
+
tracing::info!(file = %file.name.bright_green(), "Downloading file");
let client = Client::new();
···
let mut tmpfile = std::fs::File::create(&tmppath)?;
tmpfile.write_all(&bytes)?;
-
println!(
-
"Reading file: {}",
-
&tmppath.clone().display().to_string().bright_green()
-
);
+
tracing::info!(path = %tmppath.display(), "Reading file");
let tagged_file = match Probe::open(&tmppath)?.read() {
Ok(tagged_file) => tagged_file,
Err(e) => {
-
println!("Error opening file: {}", e);
+
tracing::warn!(file = %file.name.bright_green(), error = %e, "Failed to open file with lofty");
return Ok(());
}
};
···
let tag = match primary_tag {
Some(tag) => tag,
None => {
-
println!("No tag found in file");
+
tracing::warn!(file = %file.name.bright_green(), "No tag found in file");
return Ok(());
}
};
let pictures = tag.pictures();
-
println!(
-
"Title: {}",
-
tag.get_string(&lofty::tag::ItemKey::TrackTitle)
-
.unwrap_or_default()
-
.bright_green()
-
);
-
println!(
-
"Artist: {}",
-
tag.get_string(&lofty::tag::ItemKey::TrackArtist)
-
.unwrap_or_default()
-
.bright_green()
-
);
-
println!(
-
"Album Artist: {}",
-
tag.get_string(&lofty::tag::ItemKey::AlbumArtist)
-
.unwrap_or_default()
-
.bright_green()
-
);
-
println!(
-
"Album: {}",
-
tag.get_string(&lofty::tag::ItemKey::AlbumTitle)
-
.unwrap_or_default()
-
.bright_green()
-
);
-
println!(
-
"Lyrics: {}",
-
tag.get_string(&lofty::tag::ItemKey::Lyrics)
-
.unwrap_or_default()
-
.bright_green()
-
);
-
println!("Year: {}", tag.year().unwrap_or_default().bright_green());
-
println!(
-
"Track Number: {}",
-
tag.track().unwrap_or_default().bright_green()
-
);
-
println!(
-
"Track Total: {}",
-
tag.track_total().unwrap_or_default().bright_green()
-
);
-
println!(
-
"Release Date: {:?}",
-
tag.get_string(&lofty::tag::ItemKey::OriginalReleaseDate)
-
.unwrap_or_default()
-
.bright_green()
-
);
-
println!(
-
"Recording Date: {:?}",
-
tag.get_string(&lofty::tag::ItemKey::RecordingDate)
-
.unwrap_or_default()
-
.bright_green()
-
);
-
println!(
-
"Copyright Message: {}",
-
tag.get_string(&lofty::tag::ItemKey::CopyrightMessage)
-
.unwrap_or_default()
-
.bright_green()
-
);
-
println!("Pictures: {:?}", pictures);
+
tracing::info!(title = %tag.get_string(&lofty::tag::ItemKey::TrackTitle).unwrap_or_default(), "Title");
+
tracing::info!(artist = %tag.get_string(&lofty::tag::ItemKey::TrackArtist).unwrap_or_default(), "Artist");
+
tracing::info!(album_artist = %tag.get_string(&lofty::tag::ItemKey::AlbumArtist).unwrap_or_default(), "Album artist");
+
tracing::info!(album = %tag.get_string(&lofty::tag::ItemKey::AlbumTitle).unwrap_or_default(), "Album");
+
tracing::info!(lyrics = %tag.get_string(&lofty::tag::ItemKey::Lyrics).unwrap_or_default(), "Lyrics");
+
tracing::info!(year = %tag.year().unwrap_or_default(), "Year");
+
tracing::info!(track_number = %tag.track().unwrap_or_default(), "Track number");
+
tracing::info!(track_total = %tag.track_total().unwrap_or_default(), "Track total");
+
tracing::info!(release_date = %tag.get_string(&lofty::tag::ItemKey::OriginalReleaseDate).unwrap_or_default(), "Release date");
+
tracing::info!(recording_date = %tag.get_string(&lofty::tag::ItemKey::RecordingDate).unwrap_or_default(), "Recording date");
+
tracing::info!(copyright = %tag.get_string(&lofty::tag::ItemKey::CopyrightMessage).unwrap_or_default(), "Copyright message");
+
tracing::info!(pictures = %pictures.len(), "Pictures found");
let title = tag
.get_string(&lofty::tag::ItemKey::TrackTitle)
···
match track {
Some(track) => {
-
println!("Track exists: {}", title.bright_green());
+
tracing::info!(title = %title.bright_green(), "Track exists");
let parent_drive_id = parent_drive_file_id.as_deref();
-
let status = create_google_drive_path(
+
create_google_drive_path(
&pool,
&file,
&track,
···
)
.await?;
-
println!("status: {:?}", status);
// TODO: publish file metadata to nats
}
None => {
-
println!("Creating track: {}", title.bright_green());
+
tracing::info!(title = %title.bright_green(), "Creating track");
let albumart =
upload_album_cover(albumart_id.into(), pictures, &access_token).await?;
···
}))
.send()
.await?;
-
println!("Track Saved: {} {}", title, response.status());
+
tracing::info!(status = %response.status(), "Track saved");
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
let track = get_track_by_hash(&pool, &hash).await?;
if let Some(track) = track {
let parent_drive_id = parent_drive_file_id.as_deref();
-
let status = create_google_drive_path(
+
create_google_drive_path(
&pool,
&file,
&track,
&google_drive_id,
parent_drive_id.unwrap_or(""),
)
-
.await;
-
-
println!("status: {:?}", status);
+
.await?;
// TODO: publish file metadata to nats
···
return Ok(());
}
-
println!("Failed to create track: {}", title.bright_green());
+
tracing::warn!(title = %title.bright_green(), "Failed to create track");
}
}
···
.send()
.await?;
-
println!("Cover uploaded: {}", response.status());
+
tracing::info!(status = %response.status(), "Cover uploaded");
Ok(Some(name))
}
···
match symphonia::default::get_probe().format(&hint, media_source, &format_opts, &meta_opts)
{
Ok(probed) => probed,
-
Err(_) => {
-
println!("Error probing file");
+
Err(e) => {
+
tracing::warn!(path = %path.display(), error = %e, "Failed to probe media");
return Ok(duration);
}
};
+6 -9
crates/jetstream/src/subscriber.rs
···
let pool = Arc::new(Mutex::new(pool));
let (mut ws_stream, _) = connect_async(&self.service_url).await?;
-
println!(
-
"Connected to jetstream at {}",
-
self.service_url.bright_green()
-
);
+
tracing::info!(url = %self.service_url.bright_green(), "Connected to jetstream at");
while let Some(msg) = ws_stream.next().await {
match msg {
Ok(msg) => {
if let Err(e) = handle_message(state.clone(), pool.clone(), msg).await {
-
eprintln!("Error handling message: {}", e);
+
tracing::error!(error = %e, "Error handling message");
}
}
Err(e) => {
-
eprintln!("WebSocket error: {}", e);
+
tracing::error!(error = %e, "WebSocket error");
break;
}
}
···
return Ok::<(), Error>(());
}
-
println!("Received message: {:#?}", message);
+
tracing::info!(message = %text.bright_green(), "Received message");
if let Some(commit) = message.commit {
match save_scrobble(state, pool, &message.did, commit).await {
Ok(_) => {
-
println!("Scrobble saved successfully");
+
tracing::info!(user_id = %message.did.bright_green(), "Scrobble saved successfully");
}
Err(e) => {
-
eprintln!("Error saving scrobble: {}", e);
+
tracing::error!(error = %e, "Error saving scrobble");
}
}
}
+2 -2
crates/jetstream/src/webhook/discord/mod.rs
···
embeds: Vec<DiscordEmbed>,
) -> reqwest::Result<()> {
if discord_webhook_url.is_empty() {
-
println!("DISCORD_WEBHOOK_URL is not set, skipping webhook post");
+
tracing::warn!("DISCORD_WEBHOOK_URL is not set, skipping webhook post");
return Ok(());
}
···
let res = http.post(discord_webhook_url).json(&body).send().await?;
if !res.status().is_success() {
let text = res.text().await.unwrap_or_default();
-
eprintln!("Failed to post to Discord webhook: {}", text);
+
tracing::error!(error = %text, "Failed to post to Discord webhook");
}
Ok(())
}
+2 -2
crates/jetstream/src/webhook_worker.rs
···
}
Ok(None) => break,
Err(e) => {
-
eprintln!("Failed to pop from Redis: {}", e);
+
tracing::error!(error = %e, "Failed to pop from Redis");
break;
}
}
···
tokens -= 1;
if let Err(e) = discord::post_embeds(&http, &discord_webhook_url, embeds).await {
-
eprintln!("Failed to post to Discord webhook: {}", e);
+
tracing::error!(error = %e, "Failed to post to Discord webhook");
}
}
}
+1 -1
crates/scrobbler/src/auth.rs
···
let expected_password = md5::compute(expected_password);
let expected_password = format!("{:x}", expected_password);
if expected_password != password_md5 {
-
println!("{} != {}", expected_password, password_md5);
+
tracing::error!(expected = %expected_password, provided = %password_md5, "Invalid password");
return Err(Error::msg("Invalid password"));
}
Ok(())
+1 -1
crates/scrobbler/src/handlers/v1/nowplaying.rs
···
let a = form.get("a").unwrap().to_string();
let t = form.get("t").unwrap().to_string();
-
println!("Now playing: {} - {} {}", a, t, s.cyan());
+
tracing::info!(artist = %a, track = %t, user = %s.cyan(), "Now playing");
let user_id = verify_session_id(cache, &s);
if let Err(e) = user_id {
+1 -4
crates/scrobbler/src/lib.rs
···
.parse::<u16>()
.unwrap_or(7882);
-
println!(
-
"Starting Scrobble server @ {}",
-
format!("{}:{}", host, port).green()
-
);
+
tracing::info!(url = %format!("http://{}:{}", host, port).bright_green(), "Starting Scrobble server @");
let limiter = web::Data::new(
Limiter::builder("redis://127.0.0.1")
+2 -8
crates/scrobbler/src/listenbrainz/core/submit.rs
···
token: &str,
) -> Result<HttpResponse, Error> {
if payload.listen_type != "playing_now" {
-
println!("skipping listen type: {}", payload.listen_type.cyan());
+
tracing::info!(listen_type = %payload.listen_type.cyan(), "Skipping listen type");
return Ok(HttpResponse::Ok().json(json!({
"status": "ok",
"payload": {
···
cache.del(&format!("listenbrainz:cache:{}:{}:{}", artist, track, did))?;
-
println!(
-
"Retryable error on attempt {}/{}: {}",
-
attempt,
-
RETRIES,
-
e.to_string().yellow()
-
);
-
println!("{:#?}", payload);
+
tracing::error!(error = %e, attempt = attempt, "Retryable error submitting listens for {} - {} (attempt {}/{})", artist, track, attempt, RETRIES);
if attempt == RETRIES {
return Ok(HttpResponse::BadRequest().json(serde_json::json!({
+1 -1
crates/scrobbler/src/listenbrainz/core/validate_token.rs
···
},
}))),
Err(e) => {
-
println!("Error validating token: {}", e);
+
tracing::error!(error = %e, "Failed to validate token");
Ok(HttpResponse::BadRequest().json(serde_json::json!({
"error": 4,
"message": format!("Failed to validate token: {}", e)
+12 -13
crates/scrobbler/src/listenbrainz/handlers.rs
···
let body = String::from_utf8_lossy(&payload);
let req = serde_json::from_str::<SubmitListensRequest>(&body)
.map_err(|e| {
-
println!("{}", body);
-
println!("Error parsing request body: {}", e);
+
tracing::error!(body = %body, error = %e, "Error parsing request body");
e
})
.map_err(actix_web::error::ErrorBadRequest)?;
···
}));
}
Err(e) => {
-
println!("Error validating token: {}", e);
+
tracing::error!(error = %e, "Error validating token");
return HttpResponse::InternalServerError().finish();
}
}
···
query: web::Query<String>,
data: web::Data<Arc<Pool<Postgres>>>,
) -> impl Responder {
-
let pool = data.get_ref();
+
let _pool = data.get_ref();
let query = query.into_inner();
match search_users(&query).await {
Ok(users) => HttpResponse::Ok().json(users),
Err(e) => {
-
println!("Error searching users: {}", e);
+
tracing::error!(error = %e, "Error searching users");
HttpResponse::InternalServerError().finish()
}
}
···
match get_listens(&user_name).await {
Ok(listens) => HttpResponse::Ok().json(listens),
Err(e) => {
-
println!("Error getting listens for user {}: {}", user_name, e);
+
tracing::error!(error = %e, "Error getting listens for user {}", user_name);
HttpResponse::InternalServerError().finish()
}
}
···
match get_listen_count(&user_name).await {
Ok(count) => HttpResponse::Ok().json(count),
Err(e) => {
-
println!("Error getting listen count for user {}: {}", user_name, e);
+
tracing::error!(error = %e, "Error getting listen count for user {}", user_name);
HttpResponse::InternalServerError().finish()
}
}
···
match get_playing_now(&user_name).await {
Ok(playing_now) => HttpResponse::Ok().json(playing_now),
Err(e) => {
-
println!("Error getting playing now for user {}: {}", user_name, e);
+
tracing::error!(error = %e, "Error getting playing now for user {}", user_name);
HttpResponse::InternalServerError().finish()
}
}
···
match get_top_artists(&user_name).await {
Ok(artists) => HttpResponse::Ok().json(artists),
Err(e) => {
-
println!("Error getting top artists: {}", e);
+
tracing::error!(error = %e, "Error getting top artists");
HttpResponse::InternalServerError().finish()
}
}
···
match get_top_releases(&user_name).await {
Ok(releases) => HttpResponse::Ok().json(releases),
Err(e) => {
-
println!("Error getting top releases: {}", e);
+
tracing::error!(error = %e, "Error getting top releases");
HttpResponse::InternalServerError().finish()
}
}
···
match get_top_recordings(&user_name).await {
Ok(recordings) => HttpResponse::Ok().json(recordings),
Err(e) => {
-
println!("Error getting sitewide recordings: {}", e);
+
tracing::error!(error = %e, "Error getting top recordings");
HttpResponse::InternalServerError().finish()
}
}
···
match get_top_release_groups(&user_name).await {
Ok(release_groups) => HttpResponse::Ok().json(release_groups),
Err(e) => {
-
println!("Error getting top release groups: {}", e);
+
tracing::error!(error = %e, "Error getting top release groups");
HttpResponse::InternalServerError().finish()
}
}
···
match get_top_recordings(&user_name).await {
Ok(recordings) => HttpResponse::Ok().json(recordings),
Err(e) => {
-
println!("Error getting top recordings: {}", e);
+
tracing::error!(error = %e, "Error getting top recordings");
HttpResponse::InternalServerError().finish()
}
}
+3 -4
crates/scrobbler/src/rocksky.rs
···
let token = generate_token(did)?;
let client = Client::new();
-
println!("Scrobbling track: \n {:#?}", track);
+
tracing::info!(did = %did, track = ?track, "Scrobbling track");
let response = client
.post(&format!("{}/now-playing", ROCKSKY_API))
···
.await?;
let status = response.status();
-
println!("Response status: {}", status);
+
tracing::info!(did = %did, artist = %track.artist, track = %track.title, status = %status, "Scrobble response");
if !status.is_success() {
let response_text = response.text().await?;
-
println!("did: {}", did);
-
println!("Failed to scrobble track: {}", response_text);
+
tracing::error!(did = %did, response = %response_text, "Failed to scrobble track");
return Err(Error::msg(format!(
"Failed to scrobble track: {}",
response_text
+2 -10
crates/scrobbler/src/spotify/client.rs
···
let data = response.text().await?;
if data == "Too many requests" {
-
println!(
-
"> retry-after {}",
-
headers.get("retry-after").unwrap().to_str().unwrap()
-
);
-
println!("> {} [get_album]", data);
+
tracing::info!(retry_after = %headers.get("retry-after").unwrap().to_str().unwrap(), data = %data, "Rate limited on get_album");
return Ok(None);
}
···
let data = response.text().await?;
if data == "Too many requests" {
-
println!(
-
"> retry-after {}",
-
headers.get("retry-after").unwrap().to_str().unwrap()
-
);
-
println!("> {} [get_artist]", data);
+
tracing::info!(retry_after = %headers.get("retry-after").unwrap().to_str().unwrap(), data = %data, "Rate limited on get_artist");
return Ok(None);
}
+7 -14
crates/webscrobbler/src/handlers.rs
···
req: HttpRequest,
) -> Result<impl Responder, actix_web::Error> {
let id = req.match_info().get("id").unwrap();
-
println!("Received scrobble for ID: {}", id.cyan());
+
tracing::info!(id = %id.bright_green(), "Received scrobble");
let pool = data.get_ref().clone();
···
let body = read_payload!(payload);
let params = serde_json::from_slice::<ScrobbleRequest>(&body).map_err(|err| {
let body = String::from_utf8_lossy(&body);
-
println!("Failed to parse JSON: {}", body);
-
println!("Failed to parse JSON: {}", err);
+
tracing::error!(body = %body, error = %err, "Failed to parse JSON");
actix_web::error::ErrorBadRequest(format!("Failed to parse JSON: {}", err))
})?;
-
println!("Parsed scrobble request: {:#?}", params);
+
tracing::info!(params = ?params, "Parsed scrobble request");
if params.event_name != "scrobble" {
-
println!("Skipping non-scrobble event: {}", params.event_name.green());
+
tracing::info!(event_name = %params.event_name.cyan(), "Skipping non-scrobble event");
return Ok(HttpResponse::Ok().body("Skipping non-scrobble event"));
}
···
})?;
if spotify_token.is_some() {
-
println!("User has a Spotify token, skipping scrobble");
+
tracing::info!("User has a Spotify token, skipping scrobble");
return Ok(HttpResponse::Ok().body("User has a Spotify token, skipping scrobble"));
}
}
···
));
if cached.is_err() {
-
println!(
-
"Failed to check cache for Emby scrobble: {}",
-
cached.unwrap_err()
-
);
+
tracing::error!(artist = %artist, track = %track, error = %cached.unwrap_err(), "Failed to check cache for Emby scrobble");
return Ok(HttpResponse::Ok().body("Failed to check cache for Emby scrobble"));
}
if cached.unwrap().is_some() {
-
println!(
-
"Skipping duplicate scrobble for Emby: {} - {}",
-
artist, track
-
);
+
tracing::warn!(artist = %artist, track = %track, "Skipping duplicate scrobble for Emby");
return Ok(HttpResponse::Ok().body("Skipping duplicate scrobble for Emby"));
}
}
+1 -4
crates/webscrobbler/src/lib.rs
···
.parse::<u16>()
.unwrap_or(7883);
-
println!(
-
"Starting WebScrobbler Webhook @ {}",
-
format!("{}:{}", host, port).green()
-
);
+
tracing::info!(url = %format!("http://{}:{}", host, port).bright_green(), "Starting WebScrobbler server @");
let limiter = web::Data::new(
Limiter::builder("redis://127.0.0.1")
+4 -8
crates/webscrobbler/src/rocksky.rs
···
use anyhow::Error;
-
use owo_colors::OwoColorize;
use reqwest::Client;
use crate::{auth::generate_token, cache::Cache, types::Track};
···
let token = generate_token(did)?;
let client = Client::new();
-
println!("Scrobbling track: \n {:#?}", track);
+
tracing::info!(did = %did, track = ?track, "Scrobbling track");
let response = client
.post(&format!("{}/now-playing", ROCKSKY_API))
···
.await?;
if !response.status().is_success() {
-
println!(
-
"Failed to scrobble track: {}",
-
response.status().to_string()
-
);
+
tracing::error!(did = %did, artist = %track.artist, track = %track.title, status = %response.status(), "Failed to scrobble track");
let text = response.text().await?;
-
println!("Response: {}", text);
+
tracing::error!(did = %did, response = %text, "Response");
return Err(Error::msg(format!("Failed to scrobble track: {}", text)));
}
-
println!("Scrobbled track: {}", track.title.green());
+
tracing::info!(did = %did, artist = %track.artist, track = %track.title, "Scrobbled track");
Ok(())
}
+4 -9
crates/webscrobbler/src/scrobbler.rs
···
let cached = cache.get(&key)?;
if cached.is_some() {
-
println!("{}", format!("Cached: {}", key).yellow());
+
tracing::info!(artist = %scrobble.data.song.parsed.artist, track = %scrobble.data.song.parsed.track, "Using cached track");
let track = serde_json::from_str::<Track>(&cached.unwrap())?;
rocksky::scrobble(cache, &did, track, scrobble.time).await?;
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
···
let result = spotify_client.search(&query).await?;
if let Some(track) = result.tracks.items.first() {
-
println!("{}", "Spotify (track)".yellow());
+
tracing::info!("Spotify (track)");
let mut track = track.clone();
if let Some(album) = spotify_client.get_album(&track.album.id).await? {
···
if let Some(recording) = result.recordings.first() {
let result = mb_client.get_recording(&recording.id).await?;
-
println!("{}", "Musicbrainz (recording)".yellow());
+
tracing::info!("Musicbrainz (recording)");
rocksky::scrobble(cache, &did, result.into(), scrobble.time).await?;
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
return Ok(());
}
-
println!(
-
"{} {} - {}, skipping",
-
"Track not found: ".yellow(),
-
scrobble.data.song.parsed.artist,
-
scrobble.data.song.parsed.track
-
);
+
tracing::warn!(artist = %scrobble.data.song.parsed.artist, track = %scrobble.data.song.parsed.track, "Track not found, skipping");
Ok(())
}
+2 -10
crates/webscrobbler/src/spotify/client.rs
···
let data = response.text().await?;
if data == "Too many requests" {
-
println!(
-
"> retry-after {}",
-
headers.get("retry-after").unwrap().to_str().unwrap()
-
);
-
println!("> {} [get_album]", data);
+
tracing::info!(retry_after = %headers.get("retry-after").unwrap().to_str().unwrap(), data = %data, "Rate limited on get_album");
return Ok(None);
}
···
let data = response.text().await?;
if data == "Too many requests" {
-
println!(
-
"> retry-after {}",
-
headers.get("retry-after").unwrap().to_str().unwrap()
-
);
-
println!("> {} [get_artist]", data);
+
tracing::info!(retry_after = %headers.get("retry-after").unwrap().to_str().unwrap(), data = %data, "Rate limited on get_artist");
return Ok(None);
}
-50
crates/analytics/src/main.rs
···
-
use core::create_tables;
-
use std::{
-
env,
-
sync::{Arc, Mutex},
-
};
-
-
use clap::Command;
-
use cmd::{serve::serve, sync::sync};
-
use dotenv::dotenv;
-
use duckdb::Connection;
-
use sqlx::postgres::PgPoolOptions;
-
-
pub mod cmd;
-
pub mod core;
-
pub mod handlers;
-
pub mod subscriber;
-
pub mod types;
-
pub mod xata;
-
-
fn cli() -> Command {
-
Command::new("analytics")
-
.version(env!("CARGO_PKG_VERSION"))
-
.about("Rocksky Analytics CLI built with Rust and DuckDB")
-
.subcommand(Command::new("sync").about("Sync data from Xata to DuckDB"))
-
.subcommand(Command::new("serve").about("Serve the Rocksky Analytics API"))
-
}
-
-
#[tokio::main]
-
async fn main() -> Result<(), Box<dyn std::error::Error>> {
-
dotenv().ok();
-
-
let pool = PgPoolOptions::new()
-
.max_connections(5)
-
.connect(&env::var("XATA_POSTGRES_URL")?)
-
.await?;
-
let conn = Connection::open("./rocksky-analytics.ddb")?;
-
-
create_tables(&conn).await?;
-
-
let args = cli().get_matches();
-
let conn = Arc::new(Mutex::new(conn));
-
-
match args.subcommand() {
-
Some(("sync", _)) => sync(conn, &pool).await?,
-
Some(("serve", _)) => serve(conn).await?,
-
_ => serve(conn).await?,
-
}
-
-
Ok(())
-
}
-37
crates/dropbox/src/main.rs
···
-
use clap::Command;
-
use cmd::{scan::scan, serve::serve};
-
use dotenv::dotenv;
-
-
pub mod client;
-
pub mod cmd;
-
pub mod consts;
-
pub mod crypto;
-
pub mod handlers;
-
pub mod repo;
-
pub mod scan;
-
pub mod token;
-
pub mod types;
-
pub mod xata;
-
-
fn cli() -> Command {
-
Command::new("dropbox")
-
.version(env!("CARGO_PKG_VERSION"))
-
.about("Rocksky Dropbox Service")
-
.subcommand(Command::new("scan").about("Scan Dropbox Music Folder"))
-
.subcommand(Command::new("serve").about("Serve Rocksky Dropbox API"))
-
}
-
-
#[tokio::main]
-
async fn main() -> Result<(), Box<dyn std::error::Error>> {
-
dotenv().ok();
-
-
let args = cli().get_matches();
-
-
match args.subcommand() {
-
Some(("scan", _)) => scan().await?,
-
Some(("serve", _)) => serve().await?,
-
_ => serve().await?,
-
}
-
-
Ok(())
-
}
-37
crates/googledrive/src/main.rs
···
-
use clap::Command;
-
use cmd::{scan::scan, serve::serve};
-
use dotenv::dotenv;
-
-
pub mod client;
-
pub mod cmd;
-
pub mod consts;
-
pub mod crypto;
-
pub mod handlers;
-
pub mod repo;
-
pub mod scan;
-
pub mod token;
-
pub mod types;
-
pub mod xata;
-
-
fn cli() -> Command {
-
Command::new("googledrive")
-
.version(env!("CARGO_PKG_VERSION"))
-
.about("Rocksky Google Drive Service")
-
.subcommand(Command::new("scan").about("Scan Google Drive Music Folder"))
-
.subcommand(Command::new("serve").about("Serve Rocksky Google Drive API"))
-
}
-
-
#[tokio::main]
-
async fn main() -> Result<(), Box<dyn std::error::Error>> {
-
dotenv().ok();
-
-
let args = cli().get_matches();
-
-
match args.subcommand() {
-
Some(("scan", _)) => scan().await?,
-
Some(("serve", _)) => serve().await?,
-
_ => serve().await?,
-
}
-
-
Ok(())
-
}
-37
crates/jetstream/src/main.rs
···
-
use std::{env, sync::Arc};
-
-
use dotenv::dotenv;
-
use subscriber::ScrobbleSubscriber;
-
use tokio::sync::Mutex;
-
-
use crate::webhook_worker::AppState;
-
-
pub mod profile;
-
pub mod repo;
-
pub mod subscriber;
-
pub mod types;
-
pub mod webhook;
-
pub mod webhook_worker;
-
pub mod xata;
-
-
#[tokio::main]
-
async fn main() -> Result<(), anyhow::Error> {
-
dotenv()?;
-
let jetstream_server = env::var("JETSTREAM_SERVER")
-
.unwrap_or_else(|_| "wss://jetstream2.us-west.bsky.network".to_string());
-
let url = format!(
-
"{}/subscribe?wantedCollections=app.rocksky.*",
-
jetstream_server
-
);
-
let subscriber = ScrobbleSubscriber::new(&url);
-
-
let redis_url = env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
-
let redis = redis::Client::open(redis_url)?;
-
let queue_key =
-
env::var("WEBHOOK_QUEUE_KEY").unwrap_or_else(|_| "rocksky:webhook_queue".to_string());
-
-
let state = Arc::new(Mutex::new(AppState { redis, queue_key }));
-
-
subscriber.run(state).await?;
-
Ok(())
-
}
-65
crates/playlists/src/main.rs
···
-
use core::{create_tables, find_spotify_users, load_users, save_playlists};
-
use std::{
-
env,
-
sync::{Arc, Mutex},
-
};
-
-
use anyhow::Error;
-
use async_nats::connect;
-
use dotenv::dotenv;
-
use duckdb::Connection;
-
use owo_colors::OwoColorize;
-
use rocksky_playlists::subscriber::subscribe;
-
use spotify::get_user_playlists;
-
use sqlx::postgres::PgPoolOptions;
-
-
pub mod core;
-
pub mod crypto;
-
pub mod spotify;
-
pub mod types;
-
pub mod xata;
-
-
#[tokio::main]
-
async fn main() -> Result<(), Error> {
-
dotenv().ok();
-
-
let conn = Connection::open("./rocksky-playlists.ddb")?;
-
let conn = Arc::new(Mutex::new(conn));
-
create_tables(conn.clone())?;
-
-
subscribe(conn.clone()).await?;
-
-
let pool = PgPoolOptions::new()
-
.max_connections(5)
-
.connect(&env::var("XATA_POSTGRES_URL")?)
-
.await?;
-
let users = find_spotify_users(&pool, 0, 100).await?;
-
-
load_users(conn.clone(), &pool).await?;
-
-
sqlx::query(r#"
-
CREATE UNIQUE INDEX IF NOT EXISTS user_playlists_unique_index ON user_playlists (user_id, playlist_id)
-
"#)
-
.execute(&pool)
-
.await?;
-
let conn = conn.clone();
-
-
let addr = env::var("NATS_URL").unwrap_or_else(|_| "nats://localhost:4222".to_string());
-
let nc = connect(&addr).await?;
-
let nc = Arc::new(Mutex::new(nc));
-
println!("Connected to NATS server at {}", addr.bright_green());
-
-
for user in users {
-
let token = user.1.clone();
-
let did = user.2.clone();
-
let user_id = user.3.clone();
-
let playlists = get_user_playlists(token).await?;
-
save_playlists(&pool, conn.clone(), nc.clone(), playlists, &user_id, &did).await?;
-
}
-
-
println!("Done!");
-
-
loop {
-
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
-
}
-
}
-12
crates/webscrobbler/src/main.rs
···
-
use anyhow::Error;
-
use dotenv::dotenv;
-
use rocksky_webscrobbler::start_server;
-
-
#[tokio::main]
-
async fn main() -> Result<(), Error> {
-
dotenv().ok();
-
-
start_server().await?;
-
-
Ok(())
-
}