feat: setup default tracing subscriber (#564)

This commit is contained in:
Valerio Ageno
2025-02-17 20:49:51 +01:00
committed by GitHub
parent 2905cfae3d
commit 8e04abbe42
5 changed files with 144 additions and 33 deletions
+2
View File
@@ -19,6 +19,8 @@ path = "src/lib.rs"
[dependencies]
clap = { version = "4.5.4", features = ["derive", "cargo"] }
watchexec = "5.0.0"
tracing = "0.1.41"
tracing-subscriber = {version = "0.3.19", features = ["env-filter"]}
miette = "7.2.0"
watchexec-signals = "4.0.0"
tokio = { version = "1", features = ["full"] }
+10 -6
View File
@@ -12,6 +12,7 @@ use std::path::PathBuf;
use std::process::Child;
use std::process::Command;
use std::process::Stdio;
use tracing::error;
use tuono_internal::config::Config;
use crate::route::Route;
@@ -176,15 +177,18 @@ impl App {
pub fn build_react_prod(&self) {
if !Path::new(BUILD_JS_SCRIPT).exists() {
eprintln!("Failed to find the build script. Please run `npm install`");
error!("Failed to find the build script. Please run `npm install`");
std::process::exit(1);
}
let output = Command::new(BUILD_JS_SCRIPT)
.output()
.expect("Failed to build the react source");
let output = Command::new(BUILD_JS_SCRIPT).output().unwrap_or_else(|_| {
error!("Failed to build the react source");
std::process::exit(1);
});
if !output.status.success() {
eprintln!("Failed to build the react source");
eprintln!("Error: {}", String::from_utf8_lossy(&output.stderr));
error!("Failed to build the react source");
error!("Error: {}", String::from_utf8_lossy(&output.stderr));
std::process::exit(1);
}
}
+36 -8
View File
@@ -3,24 +3,30 @@ use spinners::{Spinner, Spinners};
use std::path::PathBuf;
use std::thread::sleep;
use std::time::Duration;
use tracing::{error, trace};
use crate::app::App;
use crate::mode::Mode;
fn exit_gracefully_with_error(msg: &str) -> ! {
error!(msg);
std::process::exit(1);
}
pub fn build(mut app: App, ssg: bool, no_js_emit: bool) {
if no_js_emit {
println!("Rust build successfully finished");
return;
std::process::exit(0);
}
if ssg && app.has_dynamic_routes() {
// TODO: allow dynamic routes static generation
println!("Cannot statically build dynamic routes");
return;
std::process::exit(1);
}
app.build_tuono_config()
.expect("Failed to build tuono.config.ts");
.unwrap_or_else(|_| exit_gracefully_with_error("Failed to build tuono.config.ts"));
let mut app_build_spinner = Spinner::new(Spinners::Dots, "Building app...".into());
@@ -35,44 +41,66 @@ pub fn build(mut app: App, ssg: bool, no_js_emit: bool) {
let mut app_build_static_spinner =
Spinner::new(Spinners::Dots, "Static site generation".into());
let mut exit_gracefully_with_error = |msg: &str| -> ! {
app_build_static_spinner.stop_with_message("\u{274C} Build failed\n".into());
exit_gracefully_with_error(msg)
};
let static_dir = PathBuf::from("out/static");
if static_dir.is_dir() {
std::fs::remove_dir_all(&static_dir).expect("Failed to clear the out/static folder");
std::fs::remove_dir_all(&static_dir).unwrap_or_else(|_| {
exit_gracefully_with_error("Failed to clear the out/static folder")
});
}
std::fs::create_dir(&static_dir).expect("Failed to create static output dir");
std::fs::create_dir(&static_dir)
.unwrap_or_else(|_| exit_gracefully_with_error("Failed to create static output dir"));
copy(
"./out/client",
static_dir,
&CopyOptions::new().overwrite(true).content_only(true),
)
.expect("Failed to clone assets into static output folder");
.unwrap_or_else(|_| {
exit_gracefully_with_error("Failed to clone assets into static output folder")
});
// Start the server
#[allow(clippy::zombie_processes)]
let mut rust_server = app.run_rust_server();
let mut exit_and_shut_server = |msg: &str| -> ! {
_ = rust_server.kill();
exit_gracefully_with_error(msg)
};
let reqwest_client = reqwest::blocking::Client::builder()
.user_agent("")
.build()
.expect("Failed to build reqwest client");
.unwrap_or_else(|_| exit_and_shut_server("Failed to build reqwest client"));
// Wait for server
let mut is_server_ready = false;
let config = app.config.as_ref().unwrap();
while !is_server_ready {
trace!("Checking server availability");
let server_url = format!("http://{}:{}", config.server.host, config.server.port);
if reqwest_client.get(&server_url).send().is_ok() {
is_server_ready = true;
} else {
trace!("Server not ready yet. Sleeping for 1 second");
}
sleep(Duration::from_secs(1));
}
trace!("Server is ready, starting static site generation");
for (_, route) in app.route_map {
route.save_ssg_file(&reqwest_client);
if let Err(msg) = route.save_ssg_file(&reqwest_client) {
exit_and_shut_server(&msg);
}
}
// Close server
+20
View File
@@ -1,6 +1,8 @@
use std::path::PathBuf;
use clap::{Parser, Subcommand};
use tracing::{span, Level};
use tracing_subscriber::EnvFilter;
use crate::app::App;
use crate::build;
@@ -61,10 +63,20 @@ fn init_tuono_folder(mode: Mode) -> std::io::Result<App> {
}
pub fn app() -> std::io::Result<()> {
tracing_subscriber::fmt()
// Not need for the time since the code is synchronous
.without_time()
.with_env_filter(EnvFilter::from_default_env())
.init();
let args = Args::parse();
match args.action {
Actions::Dev => {
let span = span!(Level::TRACE, "DEV");
let _guard = span.enter();
let mut app = init_tuono_folder(Mode::Dev)?;
app.build_tuono_config()
@@ -75,6 +87,10 @@ pub fn app() -> std::io::Result<()> {
watch::watch().unwrap();
}
Actions::Build { ssg, no_js_emit } => {
let span = span!(Level::TRACE, "BUILD");
let _guard = span.enter();
let app = init_tuono_folder(Mode::Prod)?;
build::build(app, ssg, no_js_emit);
@@ -84,6 +100,10 @@ pub fn app() -> std::io::Result<()> {
template,
head,
} => {
let span = span!(Level::TRACE, "NEW");
let _guard = span.enter();
scaffold_project::create_new_project(folder_name, template, head);
}
}
+76 -19
View File
@@ -7,6 +7,7 @@ use std::fs::File;
use std::io;
use std::path::PathBuf;
use std::str::FromStr;
use tracing::trace;
fn has_dynamic_path(route: &str) -> bool {
let regex = Regex::new(r"\[(.*?)\]").expect("Failed to create the regex");
@@ -141,35 +142,72 @@ impl Route {
self.axum_info = Some(AxumInfo::new(self))
}
pub fn save_ssg_file(&self, reqwest: &Client) {
pub fn save_ssg_file(&self, reqwest: &Client) -> Result<(), String> {
if self.is_api() {
return Ok(());
}
let path = &self.path.replace("index", "");
let mut response = reqwest
.get(format!("http://localhost:3000{path}"))
.send()
.unwrap();
let url = format!("http://localhost:3000{path}");
trace!("Requesting the page: {}", url);
let mut response = match reqwest.get(format!("http://localhost:3000{path}")).send() {
Ok(response) => response,
Err(_) => return Err(format!("Failed to get the response: {}", url)),
};
let file_path = self.output_file_path();
let parent_dir = file_path.parent().unwrap();
let parent_dir = match file_path.parent() {
Some(parent_dir) => parent_dir,
None => {
return Err(format!(
"Failed to get the parent directory {:?}",
file_path
))
}
};
if !parent_dir.is_dir() {
create_all(parent_dir, false).expect("Failed to create parent directories");
if !parent_dir.is_dir() || create_all(parent_dir, false).is_err() {
return Err(format!(
"Failed to create the parent directory {:?}",
parent_dir
));
}
let mut file = File::create(file_path).expect("Failed to create the HTML file");
trace!("Saving the HTML file: {:?}", file_path);
io::copy(&mut response, &mut file).expect("Failed to write the HTML on the file");
let mut file = match File::create(&file_path) {
Ok(file) => file,
Err(_) => return Err(format!("Failed to create the file: {:?}", file_path)),
};
if io::copy(&mut response, &mut file).is_err() {
return Err(format!("Failed to write the file: {:?}", file_path));
}
// Saving also the server response
if self.axum_info.is_some() {
let data_file_path = PathBuf::from(&format!("out/static/__tuono/data{path}"));
trace!("The route is an axum route, saving the JSON file");
let data_parent_dir = data_file_path.parent().unwrap();
let data_file_path = PathBuf::from(&format!("out/static/__tuono/data{path}.json"));
if !data_parent_dir.is_dir() {
create_all(data_parent_dir, false)
.expect("Failed to create data parent directories");
let data_parent_dir = match data_file_path.parent() {
Some(parent_dir) => parent_dir,
None => {
return Err(format!(
"Failed to get the parent directory {:?}",
data_file_path
))
}
};
if !data_parent_dir.is_dir() && create_all(data_parent_dir, false).is_err() {
return Err(format!(
"Failed to create the parent directory {:?}",
data_parent_dir
));
}
let base = Url::parse("http://localhost:3000/__tuono/data").unwrap();
@@ -182,13 +220,32 @@ impl Route {
.join(pathname)
.expect("Failed to build the reqwest URL");
let mut response = reqwest.get(url).send().unwrap();
trace!("Requesting the JSON file: {}", url);
let mut data_file =
File::create(data_file_path).expect("Failed to create the JSON file");
let mut response = match reqwest.get(url.clone()).send() {
Ok(response) => {
trace!("Successfully got the response for: {url}");
response
}
Err(_) => return Err(format!("Failed to get the response: {url}")),
};
io::copy(&mut response, &mut data_file).expect("Failed to write the JSON on the file");
let mut data_file = match File::create(&data_file_path) {
Ok(file) => file,
Err(_) => {
return Err(format!(
"Failed to create the JSON file: {:?}",
data_file_path
))
}
};
if io::copy(&mut response, &mut data_file).is_err() {
return Err("Failed to write the JSON file".to_string());
}
}
Ok(())
}
fn output_file_path(&self) -> PathBuf {