Recursive Sorting fix and many logging/tracing enhancements #33

Merged
cameron merged 11 commits from feature/fix-recursive-sort into master 2025-06-12 20:03:21 +00:00
3 changed files with 35 additions and 7 deletions
Showing only changes of commit d37deb36fe - Show all commits

View File

@@ -15,7 +15,8 @@ use actix_web::{
HttpResponse, HttpResponse,
}; };
use log::{debug, error, info, trace}; use log::{debug, error, info, trace};
use opentelemetry::KeyValue;
use opentelemetry::trace::{Span, Status, Tracer};
use crate::data::{Claims, FilesRequest, FilterMode, PhotosResponse, SortType}; use crate::data::{Claims, FilesRequest, FilterMode, PhotosResponse, SortType};
use crate::{create_thumbnails, AppState}; use crate::{create_thumbnails, AppState};
@@ -27,6 +28,7 @@ use path_absolutize::*;
use rand::prelude::SliceRandom; use rand::prelude::SliceRandom;
use rand::thread_rng; use rand::thread_rng;
use serde::Deserialize; use serde::Deserialize;
use crate::otel::global_tracer;
pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>( pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
_: Claims, _: Claims,
@@ -36,6 +38,10 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
tag_dao: web::Data<Mutex<TagD>>, tag_dao: web::Data<Mutex<TagD>>,
) -> HttpResponse { ) -> HttpResponse {
let search_path = &req.path; let search_path = &req.path;
let tracer = global_tracer();
let mut span = tracer.start("list_photos");
span.set_attribute(KeyValue::new("path", search_path.to_string()));
let search_recursively = req.recursive.unwrap_or(false); let search_recursively = req.recursive.unwrap_or(false);
if let Some(tag_ids) = &req.tag_ids { if let Some(tag_ids) = &req.tag_ids {
@@ -99,6 +105,8 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
tagged_files.len(), tagged_files.len(),
tagged_files tagged_files
); );
span.set_attribute(KeyValue::new("file_count", tagged_files.len().to_string()));
span.set_status(Status::Ok);
HttpResponse::Ok().json(PhotosResponse { HttpResponse::Ok().json(PhotosResponse {
photos: tagged_files, photos: tagged_files,
@@ -190,12 +198,16 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
.map(|f| f.to_str().unwrap().to_string()) .map(|f| f.to_str().unwrap().to_string())
.collect::<Vec<String>>(); .collect::<Vec<String>>();
span.set_attribute(KeyValue::new("file_count", files.len().to_string()));
span.set_status(Status::Ok);
HttpResponse::Ok().json(PhotosResponse { HttpResponse::Ok().json(PhotosResponse {
photos: response_files, photos: response_files,
dirs, dirs,
}) })
} else { } else {
error!("Bad photos request: {}", req.path); error!("Bad photos request: {}", req.path);
span.set_status(Status::error("Invalid path"));
HttpResponse::BadRequest().finish() HttpResponse::BadRequest().finish()
} }
} }
@@ -224,12 +236,18 @@ fn sort(mut files: Vec<FileWithTagCount>, sort_type: SortType) -> Vec<String> {
} }
pub fn list_files(dir: &Path) -> io::Result<Vec<PathBuf>> { pub fn list_files(dir: &Path) -> io::Result<Vec<PathBuf>> {
let tracer = global_tracer();
let mut span = tracer.start("list_files");
let dir_name_string = dir.to_str().unwrap_or_default().to_string();
span.set_attribute(KeyValue::new("dir", dir_name_string));
let files = read_dir(dir)? let files = read_dir(dir)?
.filter_map(|res| res.ok()) .filter_map(|res| res.ok())
.filter(|entry| is_image_or_video(&entry.path()) || entry.file_type().unwrap().is_dir()) .filter(|entry| is_image_or_video(&entry.path()) || entry.file_type().unwrap().is_dir())
.map(|entry| entry.path()) .map(|entry| entry.path())
.collect::<Vec<PathBuf>>(); .collect::<Vec<PathBuf>>();
span.set_attribute(KeyValue::new("file_count", files.len().to_string()));
Ok(files) Ok(files)
} }
@@ -372,6 +390,7 @@ impl FileSystemAccess for RealFileSystem {
} }
fn move_file<P: AsRef<Path>>(&self, from: P, destination: P) -> anyhow::Result<()> { fn move_file<P: AsRef<Path>>(&self, from: P, destination: P) -> anyhow::Result<()> {
info!("Moving file: '{:?}' -> '{:?}'", from.as_ref(), destination.as_ref());
let name = from let name = from
.as_ref() .as_ref()
.file_name() .file_name()
@@ -393,6 +412,8 @@ impl Handler<RefreshThumbnailsMessage> for StreamActor {
type Result = (); type Result = ();
fn handle(&mut self, _msg: RefreshThumbnailsMessage, _ctx: &mut Self::Context) -> Self::Result { fn handle(&mut self, _msg: RefreshThumbnailsMessage, _ctx: &mut Self::Context) -> Self::Result {
let tracer = global_tracer();
let _ = tracer.start("RefreshThumbnailsMessage");
info!("Refreshing thumbnails after upload"); info!("Refreshing thumbnails after upload");
create_thumbnails() create_thumbnails()
} }

View File

@@ -83,6 +83,9 @@ async fn get_image(
req: web::Query<ThumbnailRequest>, req: web::Query<ThumbnailRequest>,
app_state: Data<AppState>, app_state: Data<AppState>,
) -> impl Responder { ) -> impl Responder {
let tracer = global_tracer();
let mut span = tracer.start("get_image");
if let Some(path) = is_valid_full_path(&app_state.base_path, &req.path, false) { if let Some(path) = is_valid_full_path(&app_state.base_path, &req.path, false) {
let image_size = req.size.unwrap_or(PhotoSize::Full); let image_size = req.size.unwrap_or(PhotoSize::Full);
if image_size == PhotoSize::Thumb { if image_size == PhotoSize::Thumb {
@@ -95,16 +98,21 @@ async fn get_image(
trace!("Thumbnail path: {:?}", thumb_path); trace!("Thumbnail path: {:?}", thumb_path);
if let Ok(file) = NamedFile::open(&thumb_path) { if let Ok(file) = NamedFile::open(&thumb_path) {
span.set_status(Status::Ok);
file.into_response(&request) file.into_response(&request)
} else { } else {
span.set_status(Status::error("Not found"));
HttpResponse::NotFound().finish() HttpResponse::NotFound().finish()
} }
} else if let Ok(file) = NamedFile::open(path) { } else if let Ok(file) = NamedFile::open(path) {
span.set_status(Status::Ok);
file.into_response(&request) file.into_response(&request)
} else { } else {
span.set_status(Status::error("Not found"));
HttpResponse::NotFound().finish() HttpResponse::NotFound().finish()
} }
} else { } else {
span.set_status(Status::error("Bad photos request"));
error!("Bad photos request: {}", req.path); error!("Bad photos request: {}", req.path);
HttpResponse::BadRequest().finish() HttpResponse::BadRequest().finish()
} }
@@ -309,7 +317,7 @@ async fn get_video_part(
path: web::Path<ThumbnailRequest>, path: web::Path<ThumbnailRequest>,
app_state: Data<AppState>, app_state: Data<AppState>,
) -> impl Responder { ) -> impl Responder {
let tracer = global::tracer("image-server"); let tracer = global_tracer();
let mut span = tracer.start("get_video_part"); let mut span = tracer.start("get_video_part");
let part = &path.path; let part = &path.path;
@@ -381,15 +389,15 @@ async fn put_add_favorite(
.await .await
{ {
Ok(Err(e)) if e.kind == DbErrorKind::AlreadyExists => { Ok(Err(e)) if e.kind == DbErrorKind::AlreadyExists => {
debug!("Favorite: {} exists for user: {}", &body.path, user_id); warn!("Favorite: {} exists for user: {}", &body.path, user_id);
HttpResponse::Ok() HttpResponse::Ok()
} }
Ok(Err(e)) => { Ok(Err(e)) => {
info!("{:?} {}. for user: {}", e, body.path, user_id); error!("{:?} {}. for user: {}", e, body.path, user_id);
HttpResponse::BadRequest() HttpResponse::BadRequest()
} }
Ok(Ok(_)) => { Ok(Ok(_)) => {
debug!("Adding favorite \"{}\" for userid: {}", body.path, user_id); info!("Adding favorite \"{}\" for userid: {}", body.path, user_id);
HttpResponse::Created() HttpResponse::Created()
} }
Err(e) => { Err(e) => {

View File

@@ -13,8 +13,7 @@ pub fn init_tracing() {
let resources = Resource::builder() let resources = Resource::builder()
.with_attributes([ .with_attributes([
KeyValue::new("service.name", "image-server"), KeyValue::new("service.name", "image-server"),
//TODO: Get this from somewhere KeyValue::new("service.version", env!("CARGO_PKG_VERSION")),
KeyValue::new("service.version", "1.0"),
]) ])
.build(); .build();