Compare commits
2 Commits
165b0e34d0
...
2aff7bb416
Author | SHA1 | Date |
---|---|---|
meeg_leeto | 2aff7bb416 | |
meeg_leeto | d2109fce65 |
164
src/main.rs
164
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)]
|
||||
|
@ -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;
|
||||
|
@ -894,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<String>,
|
||||
error_tx: sync::mpsc::UnboundedSender<String>,
|
||||
}
|
||||
|
||||
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::<String>();
|
||||
let (error_tx, error_rx) = sync::mpsc::unbounded_channel::<String>();
|
||||
|
||||
// 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<String>, 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::*;
|
||||
|
@ -1026,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);
|
||||
|
||||
|
@ -1035,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()
|
||||
|
@ -1060,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")
|
||||
|
@ -1074,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);
|
||||
|
|
Loading…
Reference in New Issue