110 lines
3.6 KiB
Rust
110 lines
3.6 KiB
Rust
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");
|
||
} |