From 9a59da600eba7f52f5e0483846ae4d8af208add8 Mon Sep 17 00:00:00 2001 From: meeg_leeto Date: Thu, 12 May 2022 19:16:28 +0100 Subject: [PATCH 01/11] wip: added log rules to configuration file --- src/main.rs | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/main.rs b/src/main.rs index 5daaf70..aa6dbc2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -138,6 +138,15 @@ mod conf { pub addr: ServeAddr, } + #[derive(Deserialize, Serialize, Debug, Clone)] + /// Configuration of logging by lonk. + pub struct LogRules { + /// Where to write error logs to. The file will be appended to. + error_log_file: PathBuf, + /// Where to write access ogs to. The file will be appended to. + access_log_file: PathBuf, + } + #[derive(Deserialize, Serialize, Debug, Clone)] /// Configuration struct. This struct is a typed representation of the /// configuration file, with each of the domain-specific configurations @@ -153,6 +162,8 @@ mod conf { pub version: Option, /// Configuration regarding the Redis database. pub db: DbConfig, + /// Configuration regarding logging. + pub log_rules: LogRules, /// Configuration regarding the types of (URL shorten) slugs produced. pub slug_rules: SlugRules, /// Configuration regarding where and how the HTTP server is served. @@ -171,6 +182,10 @@ mod conf { ServeFileNotExists(PathBuf), ServeDirNotDir(PathBuf), ServeDirNotExists(PathBuf), + BadAccessLogPath, + BadErrorLogPath, + AccessLogDirectoryNotExists(PathBuf), + ErrorLogDirectoryNotExists(PathBuf), } impl Config { @@ -210,6 +225,34 @@ mod conf { } } + // Check access and error log parent directories + // - Access log file + let canonical = self + .log_rules + .access_log_file + .canonicalize() + .map_err(|_| ConfigParseError::BadAccessLogPath)?; + if let Some(parent) = canonical.parent() { + if !parent.exists() { + return Err(ConfigParseError::AccessLogDirectoryNotExists( + parent.to_path_buf(), + )); + } + } + // - Error log file + let canonical = self + .log_rules + .error_log_file + .canonicalize() + .map_err(|_| ConfigParseError::BadErrorLogPath)?; + if let Some(parent) = canonical.parent() { + if !parent.exists() { + return Err(ConfigParseError::ErrorLogDirectoryNotExists( + parent.to_path_buf(), + )); + } + } + Ok(self) } } @@ -271,6 +314,18 @@ mod conf { file.to_string_lossy() ) } + ConfigParseError::BadAccessLogPath => { + panic!("Access log path could not be parsed as a canonicalizable path.") + } + ConfigParseError::BadErrorLogPath => { + panic!("Error log path could not be parsed as a canonicalizable path.") + } + ConfigParseError::AccessLogDirectoryNotExists(dir) => { + panic!("Access log file should have parent directory {}, but this directory does not exist.", dir.to_string_lossy()) + } + ConfigParseError::ErrorLogDirectoryNotExists(dir) => { + panic!("Error log file should have parent directory {}, but this directory does not exist.", dir.to_string_lossy()) + } } } } @@ -329,6 +384,15 @@ mod conf { } } + impl Default for LogRules { + fn default() -> Self { + Self { + error_log_file: "/etc/lonk/log/error.log".into(), + access_log_file: "/etc/lonk/log/access.log".into(), + } + } + } + impl Default for Config { fn default() -> Self { Self { @@ -336,6 +400,7 @@ mod conf { db: Default::default(), slug_rules: Default::default(), serve_rules: Default::default(), + log_rules: Default::default(), } } } From 8bac40ebdc4e147b1f7ca123c08028e89e647ce0 Mon Sep 17 00:00:00 2001 From: meeg_leeto Date: Thu, 12 May 2022 19:19:27 +0100 Subject: [PATCH 02/11] chore: bumped MAJOR because of configuration file brk. chng. --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87805ea..3710623 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -838,7 +838,7 @@ dependencies = [ [[package]] name = "lonk" -version = "0.1.0" +version = "1.0.0" dependencies = [ "argh", "async-object-pool", diff --git a/Cargo.toml b/Cargo.toml index c217ed9..5bb1dae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lonk" -version = "0.1.0" +version = "1.0.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 4dfa764603d7b0c2d6e3ff6dd6dcb20cb2f7e160 Mon Sep 17 00:00:00 2001 From: meeg_leeto Date: Thu, 12 May 2022 19:34:10 +0100 Subject: [PATCH 03/11] chore: updated config.json to match changes --- data/config.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/data/config.json b/data/config.json index 166ac11..ae640d4 100644 --- a/data/config.json +++ b/data/config.json @@ -1,4 +1,5 @@ { + "version": 1, "db": { "address": "redis://redis:6379", "expire_seconds": 259200 @@ -7,6 +8,10 @@ "length": 5, "chars": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-" }, + "log_rules": { + "error_log_file": "/data/log/error.log", + "access_log_file": "/data/log/access.log" + }, "serve_rules": { "dir": { "Dir": "/data/served" From 50b0ab56cbe105b5848b5279f358651a6977754c Mon Sep 17 00:00:00 2001 From: meeg_leeto Date: Thu, 12 May 2022 19:34:24 +0100 Subject: [PATCH 04/11] misc: more common line/col formatting --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index aa6dbc2..b93c7b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -266,14 +266,14 @@ mod conf { } serde_json::error::Category::Syntax => panic!( "Configuration file is syntactically incorrect. - See {}:line {}, column {}.", + See {}:{}:{}.", config_file_name, err.line(), err.column() ), serde_json::error::Category::Data => panic!( "Error deserializing configuration file; expected different data type. - See {}:line {}, column {}.", + See {}:{}:{}.", config_file_name, err.line(), err.column() From 165b0e34d0dac51f3bb84efdaaa578f77fac0b4c Mon Sep 17 00:00:00 2001 From: meeg_leeto Date: Thu, 12 May 2022 19:38:09 +0100 Subject: [PATCH 05/11] misc: replaced user facing panics with prints+exit --- src/main.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/main.rs b/src/main.rs index b93c7b8..15ff0cb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -262,16 +262,16 @@ mod conf { match self { ConfigParseError::SerdeError(err) => match err.classify() { serde_json::error::Category::Io => { - panic!("IO error when reading configuration file.") + eprintln!("IO error when reading configuration file.") } - serde_json::error::Category::Syntax => panic!( + serde_json::error::Category::Syntax => eprintln!( "Configuration file is syntactically incorrect. See {}:{}:{}.", config_file_name, err.line(), err.column() ), - serde_json::error::Category::Data => panic!( + serde_json::error::Category::Data => eprintln!( "Error deserializing configuration file; expected different data type. See {}:{}:{}.", config_file_name, @@ -279,11 +279,11 @@ mod conf { err.column() ), serde_json::error::Category::Eof => { - panic!("Unexpected end of file when reading configuration file.") + eprintln!("Unexpected end of file when reading configuration file.") } }, ConfigParseError::OldVersion(old_version) => { - panic!( + eprintln!( "Configuration file has outdated version. Expected version field to be {} but got {}.", old_version, @@ -291,42 +291,43 @@ mod conf { ); } ConfigParseError::ServeDirNotExists(dir) => { - panic!( + eprintln!( "Configuration file indicates directory {} should be served, but it does not exist.", dir.to_string_lossy() ) } ConfigParseError::ServeDirNotDir(dir) => { - panic!( + eprintln!( "Configuration file indicates directory {} should be served, but it is not a directory.", dir.to_string_lossy() ) } ConfigParseError::ServeFileNotExists(file) => { - panic!( + eprintln!( "Configuration file indicates file {} should be served, but it does not exist.", file.to_string_lossy() ) } ConfigParseError::ServeFileNotFile(file) => { - panic!( + eprintln!( "Configuration file indicates file {} should be served, but it is not a file.", file.to_string_lossy() ) } ConfigParseError::BadAccessLogPath => { - panic!("Access log path could not be parsed as a canonicalizable path.") + eprintln!("Access log path could not be parsed as a canonicalizable path.") } ConfigParseError::BadErrorLogPath => { - panic!("Error log path could not be parsed as a canonicalizable path.") + eprintln!("Error log path could not be parsed as a canonicalizable path.") } ConfigParseError::AccessLogDirectoryNotExists(dir) => { - panic!("Access log file should have parent directory {}, but this directory does not exist.", dir.to_string_lossy()) + eprintln!("Access log file should have parent directory {}, but this directory does not exist.", dir.to_string_lossy()) } ConfigParseError::ErrorLogDirectoryNotExists(dir) => { - panic!("Error log file should have parent directory {}, but this directory does not exist.", dir.to_string_lossy()) + eprintln!("Error log file should have parent directory {}, but this directory does not exist.", dir.to_string_lossy()) } } + std::process::exit(1); } } @@ -1001,16 +1002,17 @@ async fn serve() { let config_file = std::fs::File::open(config_file_name.clone()).unwrap_or_else(|err| { match err.kind() { std::io::ErrorKind::NotFound => { - panic!("Configuration file {} does not exist.", config_file_name) + eprintln!("Configuration file {} does not exist.", config_file_name) } std::io::ErrorKind::PermissionDenied => { - panic!("Read permission to {} was denied.", config_file_name) + eprintln!("Read permission to {} was denied.", config_file_name) } - _ => panic!( + _ => eprintln!( "Error when trying to read configuration file {}: {}", config_file_name, err ), }; + std::process::exit(1); }); let parse_result = tokio::task::spawn_blocking(move || { conf::Config::from_sync_buffer(std::io::BufReader::new(config_file)) From d2109fce65141b3c1ea83cfbf17e2e41bf0c54a6 Mon Sep 17 00:00:00 2001 From: meeg_leeto Date: Fri, 13 May 2022 16:19:57 +0100 Subject: [PATCH 06/11] fix: misc. fix to printing, from bad concatenation --- src/main.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 15ff0cb..db33ca3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -265,15 +265,17 @@ mod conf { eprintln!("IO error when reading configuration file.") } serde_json::error::Category::Syntax => eprintln!( - "Configuration file is syntactically incorrect. - See {}:{}:{}.", + concat!( + "Configuration file is syntactically incorrect.\n", + "See {}:{}:{}." + ), config_file_name, err.line(), err.column() ), serde_json::error::Category::Data => eprintln!( - "Error deserializing configuration file; expected different data type. - See {}:{}:{}.", + concat!("Error deserializing configuration file; expected different data type.\n", + "See {}:{}:{}."), config_file_name, err.line(), err.column() @@ -284,8 +286,8 @@ mod conf { }, ConfigParseError::OldVersion(old_version) => { eprintln!( - "Configuration file has outdated version. - Expected version field to be {} but got {}.", + concat!("Configuration file has outdated version.\n", + "Expected version field to be {} but got {}."), old_version, config_version() ); @@ -672,10 +674,12 @@ mod service { // Collision! response_channel.send(AddResult::Fail).ok(); eprintln!( - "Collision for slug {}! - Slug space may have been exhausted. - If you see this message repeatedly, - consider increasing the slug size.", + concat!( + "Collision for slug {}!\n", + "Slug space may have been exhausted.\n", + "If you see this message repeatedly,", + "consider increasing the slug size." + ), slug_key ); return; From 2aff7bb416ef9d8fcc6fcc3f2c758a8439a153c8 Mon Sep 17 00:00:00 2001 From: meeg_leeto Date: Fri, 13 May 2022 16:20:34 +0100 Subject: [PATCH 07/11] feat: added Logger and wired it up to warp --- src/main.rs | 140 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 132 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index db33ca3..a1a0611 100644 --- a/src/main.rs +++ b/src/main.rs @@ -142,9 +142,9 @@ mod conf { /// Configuration of logging by lonk. pub struct LogRules { /// Where to write error logs to. The file will be appended to. - error_log_file: PathBuf, + pub error_log_file: PathBuf, /// Where to write access ogs to. The file will be appended to. - access_log_file: PathBuf, + pub access_log_file: PathBuf, } #[derive(Deserialize, Serialize, Debug, Clone)] @@ -898,6 +898,104 @@ mod service { } } } + + /// Affine to logging + pub mod log { + use std::path::PathBuf; + + use tokio::{ + fs::OpenOptions, + io::{AsyncWriteExt, BufWriter}, + sync, + }; + + /// A struct responsible for logging events, per messages received from + /// other processes. + pub struct Logger { + access_tx: sync::mpsc::UnboundedSender, + error_tx: sync::mpsc::UnboundedSender, + } + + impl Logger { + pub fn from_log_rules(config: &crate::conf::LogRules) -> Self { + // Create the communication channels + let (access_tx, access_rx) = sync::mpsc::unbounded_channel::(); + let (error_tx, error_rx) = sync::mpsc::unbounded_channel::(); + + // Start the logging tasks + tokio::spawn(Self::logging_task( + access_rx, + config.access_log_file.clone(), + )); + tokio::spawn(Self::logging_task(error_rx, config.error_log_file.clone())); + + // Done + Logger { + access_tx, + error_tx, + } + } + + /// Log a message into the access log file. + /// + /// Returns a Result with empty type; if posting the log message + /// failed for any reason, it's unlikely to recover, and the user + /// should decide either to stop logging, ignore these errors, or + /// halt the program. + pub fn access(&self, msg: String) -> Result<(), ()> { + self.access_tx.send(msg).map_err(|e| ()) + } + + /// Log a message into the error log file. + /// + /// Returns a Result with empty type; if posting the log message + /// failed for any reason, it's unlikely to recover, and the user + /// should decide either to stop logging, ignore these errors, or + /// halt the program. + pub fn error(&self, msg: String) -> Result<(), ()> { + self.error_tx.send(msg).map_err(|e| ()) + } + + /// The task responsible for receiving the log messages and actually + /// writing them into the corresponding files. One task is created + /// for each target file. + async fn logging_task(mut rx: sync::mpsc::UnboundedReceiver, into: PathBuf) { + // Open the log file in append mode + let file = OpenOptions::new().append(true).open(into.clone()).await; + if let Err(e) = file { + eprintln!( + concat!( + "Could not open {} for logging, with error:\n", + "{}\n", + "Future logging may result in errors." + ), + into.clone().to_string_lossy(), + e + ); + return; + } + let mut file = BufWriter::new(file.unwrap()); + + // Listen to the logging message channel + while let Some(log) = rx.recv().await { + let write_result = file.write(log.as_bytes()).await; + if let Err(e) = write_result { + eprintln!( + concat!( + "Error writing to {}!\n", + "{}\n", + "Continuing, but future logging may error again." + ), + into.clone().to_string_lossy(), + e + ) + } + } + + // All logging tx channels were dropped, close this task + } + } + } } use service::*; @@ -1030,6 +1128,9 @@ async fn serve() { } }; + // Create logger + let logger = log::Logger::from_log_rules(&config.log_rules); + // Create slug factory let slug_factory = slug::SlugFactory::from_rules(config.slug_rules); @@ -1039,11 +1140,30 @@ async fn serve() { db::SlugDatabase::from_client(client, config.db.expire_seconds) }; - // We leak the slug factory and the database, because we know that these - // will live forever, and want them to have 'static lifetime so that warp is - // happy. + // We leak the slug factory, the database, and the logger, because we know + // that these will live forever, and want them to have 'static lifetime so + // that warp is happy. let slug_factory: &'static slug::SlugFactory = Box::leak(Box::new(slug_factory)); let db: &'static db::SlugDatabase = Box::leak(Box::new(db)); + let logger: &'static log::Logger = Box::leak(Box::new(logger)); + + // Warp logging compatibility layer + let log = warp::log::custom(move |info| { + let log_msg = format!( + "{} requested {}/{}, replied with status {}", + info.remote_addr() + .map(|x| x.to_string()) + .unwrap_or_else(|| "".to_string()), + info.path(), + info.method(), + info.status().as_u16(), + ); + if info.status().is_client_error() || info.status().is_server_error() { + logger.error(log_msg).ok(); + } else { + logger.access(log_msg).ok(); + } + }); // POST /shorten/ with link in argument let shorten = warp::post() @@ -1064,7 +1184,8 @@ async fn serve() { .unwrap(), Err((status, message)) => Response::builder().status(status).body(message).unwrap(), } - }); + }) + .with(log); // GET /l/:Slug let link = warp::path("l") @@ -1078,12 +1199,15 @@ async fn serve() { .unwrap(), Err((status, message)) => Response::builder().status(status).body(message).unwrap(), } - }); + }) + .with(log); // GET / // This should be the last thing matched, so that anything that doesn't // match another filter will try to match a file. - let homepage = warp::get().and(config.serve_rules.dir.to_filter()); + let homepage = warp::get() + .and(config.serve_rules.dir.to_filter()) + .with(log); let get_routes = warp::get().and(link.or(homepage)); let post_routes = warp::post().and(shorten); From b86ead74a4660201e38b546e428491165586ecc8 Mon Sep 17 00:00:00 2001 From: meeg_leeto Date: Fri, 13 May 2022 16:22:02 +0100 Subject: [PATCH 08/11] chore: add the necessary log/ directory to data/ --- .gitignore | 1 + data/log/.gitinclude | 0 2 files changed, 1 insertion(+) create mode 100644 data/log/.gitinclude diff --git a/.gitignore b/.gitignore index ea8c4bf..60937cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +data/log/*.log \ No newline at end of file diff --git a/data/log/.gitinclude b/data/log/.gitinclude new file mode 100644 index 0000000..e69de29 From d47a38e143759cfe25aae7d17ad4cd0232f3fb8b Mon Sep 17 00:00:00 2001 From: meeg_leeto Date: Fri, 13 May 2022 16:51:32 +0100 Subject: [PATCH 09/11] fix: necessary fixes to logging after testing --- src/main.rs | 73 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/src/main.rs b/src/main.rs index a1a0611..66fc197 100644 --- a/src/main.rs +++ b/src/main.rs @@ -182,8 +182,6 @@ mod conf { ServeFileNotExists(PathBuf), ServeDirNotDir(PathBuf), ServeDirNotExists(PathBuf), - BadAccessLogPath, - BadErrorLogPath, AccessLogDirectoryNotExists(PathBuf), ErrorLogDirectoryNotExists(PathBuf), } @@ -227,12 +225,8 @@ mod conf { // Check access and error log parent directories // - Access log file - let canonical = self - .log_rules - .access_log_file - .canonicalize() - .map_err(|_| ConfigParseError::BadAccessLogPath)?; - if let Some(parent) = canonical.parent() { + let weak_canonical = normalize_path(&self.log_rules.access_log_file); + if let Some(parent) = weak_canonical.parent() { if !parent.exists() { return Err(ConfigParseError::AccessLogDirectoryNotExists( parent.to_path_buf(), @@ -240,12 +234,8 @@ mod conf { } } // - Error log file - let canonical = self - .log_rules - .error_log_file - .canonicalize() - .map_err(|_| ConfigParseError::BadErrorLogPath)?; - if let Some(parent) = canonical.parent() { + let weak_canonical = normalize_path(&self.log_rules.error_log_file); + if let Some(parent) = weak_canonical.parent() { if !parent.exists() { return Err(ConfigParseError::ErrorLogDirectoryNotExists( parent.to_path_buf(), @@ -257,6 +247,37 @@ mod conf { } } + /// Yanked from the source of cargo. Weaker than canonicalize, because it + /// doesn't require the target file to exist. + fn normalize_path(path: &std::path::Path) -> PathBuf { + use std::path::*; + + let mut components = path.components().peekable(); + let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { + components.next(); + PathBuf::from(c.as_os_str()) + } else { + PathBuf::new() + }; + + for component in components { + match component { + Component::Prefix(..) => unreachable!(), + Component::RootDir => { + ret.push(component.as_os_str()); + } + Component::CurDir => {} + Component::ParentDir => { + ret.pop(); + } + Component::Normal(c) => { + ret.push(c); + } + } + } + ret + } + impl ConfigParseError { pub fn panic_with_message(self, config_file_name: &str) -> ! { match self { @@ -316,12 +337,6 @@ mod conf { file.to_string_lossy() ) } - ConfigParseError::BadAccessLogPath => { - eprintln!("Access log path could not be parsed as a canonicalizable path.") - } - ConfigParseError::BadErrorLogPath => { - eprintln!("Error log path could not be parsed as a canonicalizable path.") - } ConfigParseError::AccessLogDirectoryNotExists(dir) => { eprintln!("Access log file should have parent directory {}, but this directory does not exist.", dir.to_string_lossy()) } @@ -943,7 +958,7 @@ mod service { /// should decide either to stop logging, ignore these errors, or /// halt the program. pub fn access(&self, msg: String) -> Result<(), ()> { - self.access_tx.send(msg).map_err(|e| ()) + self.access_tx.send(msg).map_err(|_| ()) } /// Log a message into the error log file. @@ -953,7 +968,7 @@ mod service { /// should decide either to stop logging, ignore these errors, or /// halt the program. pub fn error(&self, msg: String) -> Result<(), ()> { - self.error_tx.send(msg).map_err(|e| ()) + self.error_tx.send(msg).map_err(|_| ()) } /// The task responsible for receiving the log messages and actually @@ -961,7 +976,11 @@ mod service { /// for each target file. async fn logging_task(mut rx: sync::mpsc::UnboundedReceiver, into: PathBuf) { // Open the log file in append mode - let file = OpenOptions::new().append(true).open(into.clone()).await; + let file = OpenOptions::new() + .append(true) + .create(true) + .open(into.clone()) + .await; if let Err(e) = file { eprintln!( concat!( @@ -974,11 +993,11 @@ mod service { ); return; } - let mut file = BufWriter::new(file.unwrap()); + let mut file = file.unwrap(); // Listen to the logging message channel while let Some(log) = rx.recv().await { - let write_result = file.write(log.as_bytes()).await; + let write_result = file.write_buf(&mut log.as_bytes()).await; if let Err(e) = write_result { eprintln!( concat!( @@ -1150,12 +1169,12 @@ async fn serve() { // Warp logging compatibility layer let log = warp::log::custom(move |info| { let log_msg = format!( - "{} requested {}/{}, replied with status {}", + "{} {} {}, replied with status {}\n", info.remote_addr() .map(|x| x.to_string()) .unwrap_or_else(|| "".to_string()), - info.path(), info.method(), + info.path(), info.status().as_u16(), ); if info.status().is_client_error() || info.status().is_server_error() { From 364e5e80386ed3f7382ba636757488e91b185430 Mon Sep 17 00:00:00 2001 From: meeg_leeto Date: Fri, 13 May 2022 17:05:51 +0100 Subject: [PATCH 10/11] misc: refined logging --- src/main.rs | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 66fc197..de4616f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -997,7 +997,19 @@ mod service { // Listen to the logging message channel while let Some(log) = rx.recv().await { - let write_result = file.write_buf(&mut log.as_bytes()).await; + let write_result = file + .write_buf( + &mut format!( + "{} ", + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("Bad system time") + .as_secs() + ) + .as_bytes(), + ) + .await + .and(file.write_buf(&mut log.as_bytes()).await); if let Err(e) = write_result { eprintln!( concat!( @@ -1024,6 +1036,7 @@ async fn shorten( slug_factory: &slug::SlugFactory, db: &db::SlugDatabase, b64str: &str, + logger: &log::Logger, ) -> Result { // Parse the URL given by the user. It should arrive as a Base64 string, // and anything other than this should cleanly result in an HTTP rejection. @@ -1055,10 +1068,15 @@ async fn shorten( // ...and attempt to insert it into the database. // Failure to do so is reported to the user. - let insert_result = db.insert_slug(new_slug, url).await; + let insert_result = db.insert_slug(new_slug, url.clone()).await; match insert_result { Ok(result) => match result { - service::db::AddResult::Success(slug) => Ok(slug), + service::db::AddResult::Success(slug) => { + logger + .access(format!("{} -> {}\n", slug.inner_str(), url)) + .ok(); + Ok(slug) + } service::db::AddResult::Fail => Err(( warp::http::StatusCode::INTERNAL_SERVER_ERROR, debuginfo!("Got insertion response, but it was error.").into(), @@ -1169,10 +1187,12 @@ async fn serve() { // Warp logging compatibility layer let log = warp::log::custom(move |info| { let log_msg = format!( - "{} {} {}, replied with status {}\n", + "{} ({}) {} {}, replied with status {}\n", info.remote_addr() .map(|x| x.to_string()) - .unwrap_or_else(|| "".to_string()), + .unwrap_or_else(|| "".to_string()), + info.user_agent() + .unwrap_or_else(|| ""), info.method(), info.path(), info.status().as_u16(), @@ -1197,7 +1217,7 @@ async fn serve() { .body(String::new()) .unwrap(); } - match shorten(&slug_factory, &db, b64str.unwrap()).await { + match shorten(&slug_factory, &db, b64str.unwrap(), logger).await { Ok(slug) => Response::builder() .body(slug.inner_str().to_string()) .unwrap(), From c60dcce63c42a2c78ce485a6bad1966a88ddecd5 Mon Sep 17 00:00:00 2001 From: meeg_leeto Date: Fri, 13 May 2022 17:20:33 +0100 Subject: [PATCH 11/11] refact: misc warning squashing --- src/main.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index de4616f..671f5a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ use argh::FromArgs; -use core::panic; use validators::traits::ValidateString; use warp::{http::Response, hyper::StatusCode, Filter}; @@ -918,11 +917,7 @@ mod service { pub mod log { use std::path::PathBuf; - use tokio::{ - fs::OpenOptions, - io::{AsyncWriteExt, BufWriter}, - sync, - }; + use tokio::{fs::OpenOptions, io::AsyncWriteExt, sync}; /// A struct responsible for logging events, per messages received from /// other processes.