use std::sync::Arc; use axum::Router; use axum::routing::get; use sqlx::mysql::MySqlPoolOptions; use tokio::signal; use tower_http::trace::{DefaultMakeSpan, DefaultOnResponse, TraceLayer}; use tracing::Level; use tracing_appender::{non_blocking, rolling}; use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt}; use tracing_subscriber::fmt::time::LocalTime; use axum_sqlx::{config::AppConfig, handler, model::state::AppState}; fn init_trace_logger_by_no_blocking() -> WorkerGuard { // 设置日志级别 根据RUST_LOG let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); let timer = LocalTime::rfc_3339(); // 输出到文件中(按天分割) let file_appender = rolling::daily("logs", "app.log"); // 非阻塞 _guard释放会导致文件记录失效 let (non_blocking_appender, guard) = non_blocking(file_appender); // 构建layer 文件输出 let file_layer = fmt::layer() .with_timer(timer.clone()) .with_ansi(false) .with_writer(non_blocking_appender); // 构建layer 控制台打印 let console_layer = fmt::layer().with_timer(timer); tracing_subscriber::registry() .with(env_filter) .with(console_layer) .with(file_layer.json()) .init(); return guard; } #[tokio::main] async fn main() { // 解析 .env 文件 dotenvy::dotenv().expect("解析.env文件失败"); let _guard = init_trace_logger_by_no_blocking(); let cfg = AppConfig::new() .map_err(|e| tracing::error!("初始化配置失败:{}", e.to_string())) .unwrap(); let pool = MySqlPoolOptions::new() .max_connections(cfg.mysql.max_cons) .connect(&cfg.mysql.dsn) .await .map_err(|e| tracing::error!("数据库连接失败:{}", e.to_string())) .unwrap(); // 默认TraceLayer的打印级别为DEBUG,设置response的打印级别为INFO,这样可以避免过多的打印日志 // 利用make_span_with来显示span中的其他信息 let trace_layer = TraceLayer::new_for_http() .make_span_with(DefaultMakeSpan::new().level(Level::INFO)) .on_response(DefaultOnResponse::new().level(Level::INFO)); let app = Router::new() .route("/", get(handler::index)) .route("/detail/:id", get(handler::detail)) .route("/add", get(handler::add_ui).post(handler::add)) .route("/edit/:id", get(handler::edit_ui).post(handler::edit)) .route("/del/:id", get(handler::del)) .route("/real_del/:id", get(handler::real_del)) .route("/tran", get(handler::tran_ui).post(handler::tran)) .layer(trace_layer) .with_state(Arc::new(AppState { pool: Arc::new(pool) })); let listener = tokio::net::TcpListener::bind(&cfg.web.addr).await.unwrap(); tracing::info!("服务器运行于: {}", listener.local_addr().unwrap()); axum::serve(listener, app).with_graceful_shutdown(shutdown_signal()).await.unwrap() } async fn shutdown_signal() { let ctrl_c = async { signal::ctrl_c() .await .expect("failed to install Ctrl+C handler"); }; #[cfg(unix)] let terminate = async { signal::unix::signal(signal::unix::SignalKind::terminate()) .expect("failed to install signal handler") .recv() .await; }; #[cfg(not(unix))] let terminate = std::future::pending::<()>(); tokio::select! { _ = ctrl_c => {}, _ = terminate => {}, } tracing::info!("signal received, starting graceful shutdown"); }