reorganize debug events

This commit is contained in:
yggverse 2025-06-28 21:11:18 +03:00
parent 57dfe79568
commit 212ffeab79
10 changed files with 75 additions and 117 deletions

View file

@ -41,12 +41,8 @@ nexy -p /path/to/public_dir
[default: 127.0.0.1:1900 [::1]:1900] [default: 127.0.0.1:1900 [::1]:1900]
-d, --debug <DEBUG> -d, --debug
Debug level Print debug information
* `e` - error * `i` - info
[default: ei]
-p, --public <PUBLIC> -p, --public <PUBLIC>
Absolute path to the public files directory Absolute path to the public files directory

View file

@ -20,12 +20,9 @@ pub struct Config {
])] ])]
pub bind: Vec<String>, pub bind: Vec<String>,
/// Debug level /// Print debug information
/// #[arg(short, long, default_value_t = false)]
/// * `e` - error pub debug: bool,
/// * `i` - info
#[arg(short, long, default_value_t = String::from("ei"))]
pub debug: String,
/// Absolute path to the public files directory /// Absolute path to the public files directory
#[arg(short, long)] #[arg(short, long)]

View file

@ -5,10 +5,10 @@ mod session;
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
use clap::Parser; use clap::Parser;
let a = config::Config::parse(); let c = config::Config::parse();
let s = std::sync::Arc::new(session::Session::init(&a)?); let s = std::sync::Arc::new(session::Session::init(&c)?);
for b in a.bind { for b in c.bind {
s.debug.info(&format!("start server on `{b}`...")); println!("start server on `{b}`...");
match std::net::TcpListener::bind(&b) { match std::net::TcpListener::bind(&b) {
Ok(r) => { Ok(r) => {
std::thread::spawn({ std::thread::spawn({
@ -16,9 +16,7 @@ fn main() -> anyhow::Result<()> {
move || server::start(r, &s) move || server::start(r, &s)
}); });
} }
Err(e) => s Err(e) => eprintln!("failed to start server on `{b}`: `{e}`"),
.debug
.error(&format!("failed to start server on `{b}`: `{e}`")),
} }
} }
std::thread::park(); std::thread::park();

View file

@ -1,9 +1,9 @@
/// Internal types /// Internal server response types
pub enum Response<'a> { pub enum Response<'a> {
/// Includes reference to the original request /// Includes reference to the original request
AccessDenied(&'a str), AccessDenied(&'a str),
/// Includes server-side error description /// Includes query + server-side error description
InternalServerError(String), InternalServerError(&'a str, String),
/// Includes reference to the original request /// Includes reference to the original request
NotFound(&'a str), NotFound(&'a str),
/// Includes bytes array /// Includes bytes array

View file

@ -12,15 +12,11 @@ pub fn start(server: TcpListener, session: &Arc<Session>) {
let session = session.clone(); let session = session.clone();
move || match Connection::init(&session, stream) { move || match Connection::init(&session, stream) {
Ok(connection) => connection.handle(), Ok(connection) => connection.handle(),
Err(e) => session Err(e) => eprintln!("failed to init connection: `{e}`"),
.debug
.error(&format!("failed to init connection: `{e}`")),
} }
}); });
} }
Err(e) => session Err(e) => eprintln!("failed to accept incoming connection: `{e}`"),
.debug
.error(&format!("failed to accept incoming connection: `{e}`")),
} }
} }
} }

View file

@ -35,10 +35,12 @@ impl Connection {
let mut t = 0; // total bytes let mut t = 0; // total bytes
match self.request() { match self.request() {
Ok(q) => { Ok(q) => {
self.session.debug.info(&format!( if self.session.is_debug {
"[{}] < [{}] request `{q}`...", println!(
self.address.server, self.address.client "[{}] < [{}] request `{q}`...",
)); self.address.server, self.address.client
)
}
if let Some(ref i) = self.session.request { if let Some(ref i) = self.session.request {
i.add(&self.address.client, &q) i.add(&self.address.client, &q)
} }
@ -51,10 +53,13 @@ impl Connection {
.clf(&self.address.client, Some(&q), 0, t); .clf(&self.address.client, Some(&q), 0, t);
} }
Err(e) => { Err(e) => {
t += self.response(Response::InternalServerError(format!( t += self.response(Response::InternalServerError(
"[{}] < [{}] failed to handle incoming request: `{e}`", "",
self.address.server, self.address.client format!(
))); "[{}] < [{}] failed to handle incoming request: `{e}`",
self.address.server, self.address.client
),
));
self.session self.session
.access_log .access_log
.clf(&self.address.client, None, 1, t); .clf(&self.address.client, None, 1, t);
@ -87,50 +92,61 @@ impl Connection {
) )
} }
} }
Response::InternalServerError(e) => { Response::InternalServerError(q, e) => {
self.session.debug.error(&e); eprintln!(
"[{}] > [{}] `{q}`: internal server error: `{e}`",
self.address.server, self.address.client
);
self.session.template.internal_server_error() self.session.template.internal_server_error()
} }
Response::AccessDenied(q) => { Response::AccessDenied(q) => {
self.session.debug.error(&format!( eprintln!(
"[{}] < [{}] access to `{q}` denied.", "[{}] < [{}] access to `{q}` denied.",
self.address.server, self.address.client self.address.server, self.address.client
)); );
self.session.template.access_denied() self.session.template.access_denied()
} }
Response::NotFound(q) => { Response::NotFound(q) => {
self.session.debug.error(&format!( eprintln!(
"[{}] < [{}] requested resource `{q}` not found.", "[{}] < [{}] requested resource `{q}` not found.",
self.address.server, self.address.client self.address.server, self.address.client
)); );
self.session.template.not_found() self.session.template.not_found()
} }
}; };
match self.stream.write_all(bytes) { match self.stream.write_all(bytes) {
Ok(()) => self.session.debug.info(&format!( Ok(()) => {
"[{}] > [{}] sent {} bytes response.", if self.session.is_debug {
self.address.server, println!(
self.address.client, "[{}] > [{}] sent {} bytes response.",
bytes.len() self.address.server,
)), self.address.client,
Err(e) => self.session.debug.error(&format!( bytes.len()
)
}
}
Err(e) => eprintln!(
"[{}] ! [{}] failed to response: `{e}`", "[{}] ! [{}] failed to response: `{e}`",
self.address.server, self.address.client, self.address.server, self.address.client,
)), ),
}; };
bytes.len() bytes.len()
} }
fn shutdown(self) { fn shutdown(self) {
match self.stream.shutdown(std::net::Shutdown::Both) { match self.stream.shutdown(std::net::Shutdown::Both) {
Ok(()) => self.session.debug.info(&format!( Ok(()) => {
"[{}] - [{}] connection closed by server.", if self.session.is_debug {
self.address.server, self.address.client, println!(
)), "[{}] - [{}] connection closed by server.",
Err(e) => self.session.debug.error(&format!( self.address.server, self.address.client,
)
}
}
Err(e) => eprintln!(
"[{}] > [{}] failed to close connection: `{e}`", "[{}] > [{}] failed to close connection: `{e}`",
self.address.server, self.address.client, self.address.server, self.address.client,
)), ),
} }
} }
} }

View file

@ -1,18 +1,17 @@
mod access_log; mod access_log;
mod debug;
mod public; mod public;
mod request; mod request;
mod template; mod template;
use {access_log::AccessLog, debug::Debug, public::Public, request::Request, template::Template}; use {access_log::AccessLog, public::Public, request::Request, template::Template};
/// Shared, multi-thread features for the current server session /// Shared, multi-thread features for the current server session
pub struct Session { pub struct Session {
pub access_log: AccessLog, pub access_log: AccessLog,
pub debug: Debug,
pub public: Public, pub public: Public,
pub request: Option<Request>, pub request: Option<Request>,
pub template: Template, pub template: Template,
pub is_debug: bool,
} }
impl Session { impl Session {
@ -20,7 +19,6 @@ impl Session {
let template = Template::init(config)?; let template = Template::init(config)?;
Ok(Self { Ok(Self {
access_log: AccessLog::init(config)?, access_log: AccessLog::init(config)?,
debug: Debug::init(config)?,
public: Public::init(config)?, public: Public::init(config)?,
request: if template.welcome.contains("{hosts}") request: if template.welcome.contains("{hosts}")
|| template.welcome.contains("{hits}") || template.welcome.contains("{hits}")
@ -32,6 +30,7 @@ impl Session {
None // do not int request collector if its features not in use None // do not int request collector if its features not in use
}, },
template, template,
is_debug: config.debug,
}) })
} }
} }

View file

@ -1,30 +0,0 @@
mod level;
use level::Level;
pub struct Debug(Vec<Level>);
impl Debug {
pub fn init(config: &crate::config::Config) -> anyhow::Result<Self> {
let mut l = Vec::with_capacity(config.debug.len());
for s in config.debug.to_lowercase().chars() {
l.push(Level::parse(s)?);
}
Ok(Self(l))
}
pub fn error(&self, message: &str) {
if self.0.contains(&Level::Error) {
eprintln!("[{}] [error] {message}", now());
}
}
pub fn info(&self, message: &str) {
if self.0.contains(&Level::Info) {
println!("[{}] [info] {message}", now());
}
}
}
fn now() -> String {
chrono::Local::now().to_rfc3339()
}

View file

@ -1,17 +0,0 @@
use anyhow::{Result, bail};
#[derive(PartialEq)]
pub enum Level {
Error,
Info,
}
impl Level {
pub fn parse(value: char) -> Result<Self> {
match value {
'e' => Ok(Self::Error),
'i' => Ok(Self::Info),
_ => bail!("unsupported debug level `{value}`!"),
}
}
}

View file

@ -61,7 +61,7 @@ impl Public {
Ok(t) => match (t.is_dir(), t.is_file()) { Ok(t) => match (t.is_dir(), t.is_file()) {
(true, _) => callback(match self.list(&p) { (true, _) => callback(match self.list(&p) {
Ok(list) => Response::Directory(query, list, p == self.public_dir), Ok(list) => Response::Directory(query, list, p == self.public_dir),
Err(e) => Response::InternalServerError(e.to_string()), Err(e) => Response::InternalServerError(query, e.to_string()),
}), }),
(_, true) => match fs::File::open(p) { (_, true) => match fs::File::open(p) {
Ok(mut f) => loop { Ok(mut f) => loop {
@ -70,21 +70,24 @@ impl Public {
Ok(0) => break, Ok(0) => break,
Ok(n) => callback(Response::File(&b[..n])), Ok(n) => callback(Response::File(&b[..n])),
Err(e) => { Err(e) => {
return callback(Response::InternalServerError(format!( return callback(Response::InternalServerError(
"failed to read response chunk for `{query}`: `{e}`" query,
))); format!("failed to read response chunk: `{e}`"),
));
} }
} }
}, },
Err(e) => callback(Response::InternalServerError(format!( Err(e) => callback(Response::InternalServerError(
"failed to read response for query`{query}`: `{e}`" query,
))), format!("failed to read response: `{e}`"),
)),
}, },
_ => panic!(), // unexpected _ => panic!(), // unexpected
}, },
Err(e) => callback(Response::InternalServerError(format!( Err(e) => callback(Response::InternalServerError(
"failed to read storage for `{query}`: `{e}`" query,
))), format!("failed to read storage: `{e}`"),
)),
} }
} }